home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
ddjmag
/
ddj8610.arc
/
HOLUBLST.OCT
< prev
next >
Wrap
Text File
|
1986-10-31
|
28KB
|
777 lines
Listing 1 -- more.c
------------------------------------------------------------------------------
1 #include <stdio.h>
2 #include <ctype.h>
3 #include <fcntl.h>
4 #include <process.h>
5
6 /* MORE.C Page input to stdout.
7 *
8 * (C) 1986, Allen I. Holub. All rights reserved.
9 *
10 * Usage: more [-<offset>] file...
11 *
12 * Exit status: 0 always
13 */
14
15 /*-----------------------------------------------------------------*/
16
17 extern int b_getc (); /* Direct console input function */
18 /* source in /src/tools/b_getc.c */
19 extern int look (); /* Char. lookahead function. */
20 /* source in /src/tools/look.asm */
21 extern long ftell ( FILE *); /* Standard library functions: */
22 extern long atol ( char* );
23 extern FILE *fopen ( char*, char* );
24 extern int fclose ( FILE* );
25 extern int spawnl ( int, char*, char*, );
26 extern char *getenv ( char* );
27 extern char *fgets ( char*, int, FILE* );
28 extern long filelength ( int );
29
30 /*-----------------------------------------------------------------*/
31
32 #define CAN 0x18 /* ^X */
33 #define ESC 0x1b /* ^[ */
34
35 #define max(a,b) ((a) > (b) ? (a) : (b))
36 #define min(a,b) ((a) < (b) ? (a) : (b))
37
38 #define BSIZE 256 /* Maximum length of a line in the file */
39 #define PAGESIZE 23 /* # of lines to output before stopping */
40
41 #define E(x) fprintf(stderr,"%s\n", x)
42 #define HAS_DOT(p) strchr(p,'.')
43
44 FILE *Ifile = stdin; /* Current input file */
45 char *Ifile_name = "/dev/con"; /* Name of file associated w/ Ifile */
46 int Repeat_count = 1; /* Repeat count for most recent cmd */
47 long Line = 0; /* # of output Lines printed so far */
48 long Flen = 0; /* Length of input file in chars */
49 long Start_here = 0; /* Seek to here when prog starts */
50
51 /*-----------------------------------------------------------------
52 * Stack used to keep track of start of lines. Maximum number
53 * of lines is determined by STACKSIZE.
54 */
55
56 typedef long STACKTYPE;
57 #define STACKSIZE (1024*6) /* Must be divisible by 2 */
58
59 STACKTYPE Stack[ STACKSIZE ];
60 STACKTYPE *Sp = Stack + STACKSIZE;
61
62 #define STACKFULL (Sp <= Stack)
63 #define STACKEMPTY (Sp >= Stack + STACKSIZE )
64 #define CLEAR_STACK() Sp = Stack + STACKSIZE ;
65 #define TOS (STACKEMPTY ? 0 : *Sp)
66 #define BACK_SCRN *( min( Sp+(PAGESIZE-1), Stack+(STACKSIZE-1)) )
67
68 #define erase_line() line( ' ', 0 ) /* Draw a line of spaces */
69
70 /*------------------------------------------------------------------*/
71
72 help()
73 {
74 register int i;
75
76 /* Print a help message with a box around it, special IBM graphics
77 * characters are used for the box
78 */
79
80 putc( 0xd6, stderr );
81 for( i = 56 ; --i >= 0; putc( 0xc4, stderr) )
82 ;
83 E("\267");
84 E("\272 b ............... go (B)ack a page \272");
85 E("\272 e ............... go to end of file \272");
86 E("\272 n ............... go to (N)ext file \272");
87 E("\272 o ............... print (O)ffset from start of file \272");
88 E("\272 q ............... (Q)uit (return to DOS) \272");
89 E("\272 s ............... (S)kip one line (w/o printing) \272");
90 E("\272 r ............... (R)ewind file (go back to beginning) \272");
91 E("\272 ! ............... execute a program (type blank line \272");
92 E("\272 at prompt to execute last) \272");
93 E("\272 / ............... search for regular expression \272");
94 E("\272 (type blank line at prompt for last) \272");
95 E("\272 ESC ............. Scroll until any key is hit \272");
96 E("\272 CR .............. next line \272");
97 E("\272 SP .............. next screen \272");
98 E("\272 anything else ... print this list \272");
99 E("\272 \272");
100 E("\272 All commands may be preceeded by a count. \272");
101
102 putc( 0xd3, stderr );
103 for( i = 56 ; --i >= 0; putc( 0xc4 ,stderr) )
104 ;
105
106 E("\275");
107 }
108
109 /*------------------------------------------------------------------*/
110
111 usage()
112 {
113 E("more: Copyright (C) 1986, Allen I. Holub. All rights reserved.");
114 E("\nUseage: more [+<num>] [file...] \n");
115 E("Print all files in list on the screen, pausing every 23 lines");
116 E("If + is specified, more will start printing at character <num>");
117 E("One of the following commands is executed after each page:");
118
119 help();
120 exit(1);
121 }
122
123 /*----------------------------------------------------------------------*/
124
125 push( file_posn )
126 long file_posn; /* Push file_posn onto the stack */
127 {
128 if( STACKFULL ) /* If the stack is full, compress it */
129 comp_stk();
130
131 *( --Sp ) = file_posn;
132 }
133
134 /*------------------------------------------------------------------*/
135
136 long pop()
137 {
138 /* Pop one entry off the stack and return the file
139 * position.
140 */
141
142 return STACKEMPTY ? 0 : *Sp++ ;
143 }
144
145 /*------------------------------------------------------------------*/
146
147 comp_stk()
148 {
149 /* Compress the stack by removing every other entry.
150 * This routine is called when the stack is full (we've
151 * read more lines than the stack can hold).
152 */
153
154 register STACKTYPE *dest, *src;
155
156 fprintf(stderr,"\007Stack Full: Compressing\n");
157
158 src = dest = Stack + STACKSIZE;
159
160 while( (src -= 2) >= Stack )
161 *--dest = *src;
162
163 Sp = dest;
164 }
165
166 /*------------------------------------------------------------------*/
167
168 getcon()
169 {
170 /* Get one character from the console using a direct
171 * bios call. Map \r into \n if one is encountered.
172 */
173
174 register int c;
175
176 c = b_getc() & 0x7f; /* Get a character from console */
177 putchar(c); /* Echo character */
178
179 return ( c == '\r' ) ? '\n' : c ;
180 }
181
182 /*----------------------------------------------------------------------*/
183
184 clear_io()
185 {
186 /* Clears the entire I/O queue, both at the bios and the
187 * bdos level.
188 */
189
190 while( look() )
191 b_getc();
192
193 #ifdef NEVER
194 while( kbhit() )
195 getchar();
196 #endif
197
198 }
199
200 /*----------------------------------------------------------------------*/
201
202 khit() /* Return true if a key has been hit on */
203 { /* the physical keyboard (as compared */
204 if( look() ) /* with a character available on stdin) */
205 {
206 clear_io();
207 return 1;
208 }
209 return 0;
210 }
211
212 /*----------------------------------------------------------------------*/
213
214 char *getbuf( p )
215 register char *p;
216 {
217 /* Get a line of input using direct console I/O and put it
218 * into buf. Return a pointer to the first whitespace on the
219 * line or to end of line if none. This routine is for
220 * getting commands from the user, not for getting normal
221 * input. ^H is supported as a destructive backspace but no
222 * other editing is available.
223 */
224
225 register int c;
226 int gottail = 0;
227 char *start = p;
228 char *tail = "" ;
229
230 clear_io();
231
232 while( (c=getcon()) != '\n' )
233 {
234 if( c == '\b' )
235 {
236 if( p <= start )
237 fputs( " \007", stderr );
238 else
239 {
240 --p;
241 fputs( " \b", stderr );
242 }
243 }
244 else
245 {
246 if( isspace(c) && !gottail )
247 gottail = (int)( tail = p );
248 *p++ = c;
249 }
250 }
251
252 *p = '\0';
253 return( p <= start ? NULL : tail );
254 }
255
256 /*------------------------------------------------------------------*/
257
258 percent(s)
259 char *s;
260 {
261 /* Print the percentage of the file that we've seen so far */
262
263 printf("%4.1f%%%s", ((double)TOS / (double)Flen) * 100.00, s );
264 }
265
266 /*------------------------------------------------------------------*/
267
268 int getcmd()
269 {
270 /* Get a command from the keyboard, using direct
271 * bios I/O. Commands take the form [num]<c>. Returns
272 * the command. Repeat_count is initialized to hold [num]
273 * or 1 if no num is entered.
274 */
275
276 int c;
277
278 clear_io();
279 percent("");
280 printf(", line %ld (? for commands): ", Line );
281
282 Repeat_count = 0;
283 while( '0' <= (c = getcon()) && c <= '9' )
284 Repeat_count = (Repeat_count * 10) + (c - '0');
285
286 if( Repeat_count == 0 )
287 Repeat_count = 1;
288
289 erase_line();
290
291 if( c == 0x03 ) /* ^C == abort */
292 exit( 1 );
293
294 return( c );
295 }
296
297 /*------------------------------------------------------------------*/
298
299 char *inputline( suppress )
300 {
301 /* Get a line from the file being processed and put it into
302 * buf. Push the start of line character onto the stack.
303 * return 0 on end of file, a pointer to the line (ie. to buf)
304 * otherwise.
305 */
306
307 register int rval;
308 register long start_of_line ;
309 static char buf[BSIZE];
310
311 start_of_line = ftell( Ifile );
312
313 if( rval = (int) fgets(buf, BSIZE, Ifile) )
314 {
315 Line++;
316 push( start_of_line );
317 if( !suppress )
318 fputs( buf, stdout );
319 }
320
321 return rval ? buf : NULL ;
322 }
323
324 /*------------------------------------------------------------------*/
325
326 printpage()
327 {
328 /* Print an entire page from the input file */
329
330 register int i;
331
332 for( i = PAGESIZE-1; --i >= 0 && inputline(0); )
333 ;
334 }
335
336 /*-------------------------------------------------------------------*/
337
338 search()
339 {
340 /* Prompt for a pattern and then search for it in the
341 * file. Stop searching if the pattern is found or if
342 * any key is hit. The previous pattern is remembered
343 * in a local array so, if CR is entered instead of a
344 * pattern, the previous pattern is used.
345 */
346
347 static char pat[128], opat[128] ;
348 char *iline;
349 extern int *makepat();
350 int *template;
351
352 printf("/");
353
354 if( !getbuf( pat ) )
355 strcpy( pat, opat );
356
357 if( !(template = makepat( pat, 0 )) )
358 printf("Illegal regular expression: %s\n", pat );
359 else
360 {
361 erase_line();
362 printf("/%s\n", pat );
363
364 while( (iline = inputline(1)) && !khit() )
365 {
366 percent("\r");
367 if( matchs( iline, template, 0) )
368 break;
369 }
370
371 unmakepat( template );
372 fseek( Ifile, pop(), 0 ); /* back up one line */
373 --Line;
374 line ( 0xcd, 1);
375 printpage();
376 }
377
378 strcpy( opat, pat );
379 }
380
381 /*----------------------------------------------------------------------*/
382
383 execute()
384 {
385 /* Spawn off a child process. When the process terminates
386 * print a message and redraw the current page. Note that
387 * spawn() is used (rather than system()) so you can't
388 * execute a batch file or a built-in command. This
389 * subroutine will set the CMDLINE environment variable
390 * to a null string for the sake of those routines that
391 * are executing under the shell which will use it.
392 */
393
394 static char buf[128];
395 char *tail = " ";
396
397 static char obuf[128], *otail = obuf;
398
399 register char *p;
400 register int c;
401 int got_tail = 0;
402
403 printf("!");
404
405 if( !(tail = getbuf(buf)) ) /* If no command entered, */
406 { /* use the same one we */
407 tail = otail; /* used last time */
408 memcpy( buf, obuf, 128 );
409 printf( "\n!%s %s\n", buf, tail );
410 }
411 else
412 {
413 if( *tail )
414 *tail++ = '\0';
415 }
416
417 if( HAS_DOT(buf) )
418 {
419 /* Spawnlp will actually try to execute any file that you
420 * give it. If you say to execute an ASCII file, it will
421 * load that file into memory, try to execute it, and die
422 * a horrible death. We attempt to avoid this by checking
423 * for a dot in the file name. You may want to put a
424 * more rigorous test here.
425 */
426
427 fprintf(stderr,"\007<%s> is not a command\n", buf);
428 }
429 else
430 {
431 putenv("CMDLINE=");
432 if( spawnlp(P_WAIT, buf, buf, tail, NULL) == -1)
433 fprintf(stderr,"Can't execute <%s %s>\n", buf, tail );
434
435 }
436
437 printf("Hit any key to continue ....");
438 getcon();
439 erase_line();
440 putchar('\n');
441
442 otail = tail;
443 memcpy( obuf, buf, 128 );
444 }
445
446 /*----------------------------------------------------------------------*/
447
448 line( c , newline )
449 {
450 /* Print a line of characters to mark top of page. 0xcd
451 * is the IBM graphics character for a horizontal double
452 * line. The cursor is put at the beginning of next line
453 * if "newline" is true, else it's put at beginning of
454 * current line.
455 */
456
457 register int i;
458
459 putchar('\r');
460
461 for( i = 79; --i >= 0 ; putchar( c ) )
462 ;
463
464 putchar( newline ? '\n' : '\r' );
465
466 }
467
468 /*------------------------------------------------------------------*/
469
470 backapage( count )
471 {
472 /* Go back count pages and print the resulting page.
473 */
474
475 register int i;
476
477 i = ((count+1) * PAGESIZE) -1;
478
479 while( --i >= 0 )
480 {
481 Line--;
482 pop();
483 }
484
485 line ( 0xcd, 1);
486 fseek( Ifile, pop(), 0 );
487 Line = max( Line - 1, 0 );
488 printpage();
489 }
490
491 /*------------------------------------------------------------------*/
492
493 docmd( cmd, ateof )
494 {
495 /* Do a single command, return 1 if next file is requested.
496 * Actually call exit on a "quit" command or ^C.
497 */
498
499 register int rval = 0;
500 register int i;
501 long posn;
502
503 do {
504 switch( cmd )
505 {
506 case CAN: break; /* NOP */
507 case 'q': exit(0); /* abort */
508
509 case '\n': /* FORWARD MOTION */
510 if( ateof ) /* one line */
511 rval = 1;
512 else
513 inputline(0);
514 break;
515
516 case ' ': /* one page */
517 if( ateof )
518 rval = 1;
519
520 printpage();
521 break;
522
523 case 'e': /* To end of file */
524 erase_line();
525 while( inputline(1) && !khit() )
526 percent("\r");
527 break;
528
529 case 's': /* one line w/o printing */
530 if( ateof )
531 rval = 1;
532 else
533 {
534 erase_line();
535 inputline(1);
536 percent("\r");
537 }
538 break;
539
540 case ESC: /* scroll till key is hit */
541 if( ateof )
542 rval = 1;
543 else
544 while( inputline(0) && !khit() )
545 clear_io();
546
547 clear_io();
548 Repeat_count = 0; /* Ignore repeat count */
549 break; /* if it's set */
550
551 case 'n': /* to next file */
552 rval = 1;
553 break;
554
555 case '/': /* search for pattern */
556 search();
557 break;
558
559 case 'r': /* to start of file */
560 line( 0xcd, 1 );
561 CLEAR_STACK();
562 Line = 0;
563 fseek( Ifile, 0L, 0 );
564 printpage();
565 break;
566
567 case 'b': /* to previous page */
568 backapage( Repeat_count );
569 Repeat_count = 0;
570 break;
571
572 case 'o': /* print file position */
573
574 printf("Top line = %ld, ", BACK_SCRN );
575 printf("Bottom line = %ld\n", TOS );
576 break;
577
578 case '!':
579 /* Close the file and spawn another shell.
580 * when we come back, reopen the file
581 * and position to the same place we
582 * were before. This is necessary because of
583 * a bug in Microsoft C ver. 3.0's spawn functions
584 * (they trash the IOB). It will cause problems
585 * if standard input is used as the input source
586 * (as in a pipe) because we won't be able to
587 * successfully reopen stdin.
588 */
589
590 Repeat_count = 0; /* Ignore repeat count */
591 fclose( Ifile );
592 execute();
593 posn = pop();
594
595 if( Ifile = fopen(Ifile_name, "r") )
596 {
597 fseek( Ifile, posn, 0 );
598 backapage( 0 );
599 }
600 else
601 {
602 fprintf(stderr,"more: can't open %s\n",
603 Ifile_name);
604 rval = 1;
605 }
606 break;
607
608 default : /* Print the help msg. */
609 help();
610 cmd = getcmd(); /* get a new command */
611 Repeat_count++;
612 break;
613 }
614
615 } while( --Repeat_count > 0 );
616
617 return( rval );
618 }
619
620 /*----------------------------------------------------------------------*/
621
622 dofile( fname )
623 char *fname;
624 {
625 /* Process lines from an input file having the indicated
626 * name.
627 */
628
629 if( (Ifile_name = fname) && !(Ifile = fopen(fname, "r")) )
630 fprintf(stderr, "more: can't open %s\n", fname );
631 else
632 {
633 Flen = filelength( fileno(Ifile) );
634 fseek( Ifile, Start_here, 0 );
635
636 CLEAR_STACK();
637 docmd(' ', 0 ); /* dump the first page */
638
639 for(;;)
640 {
641 for(;;)
642 {
643 if( docmd( getcmd(), 0) )
644 return;
645
646 if( feof(Ifile) )
647 break;
648 }
649
650 E("\n\020\020\020 LAST LINE IN FILE \021\021\021");
651 if( docmd( getcmd(), 1) )
652 break;
653 }
654
655 fclose( Ifile );
656 }
657 }
658
659 /*------------------------------------------------------------------*/
660
661 main(argc, argv)
662 char **argv;
663 {
664 ctlc();
665 reargv(&argc, &argv);
666
667 if( argc > 1 )
668 {
669 if( argv[1][0] == '-' )
670 usage();
671
672 else if (argv [1][0] == '+' )
673 {
674 Start_here = atol( &argv[1][1] );
675 printf("Starting at character %ld\n", Start_here );
676 push ( Start_here );
677 ++argv;
678 --argc;
679 }
680 }
681
682 if( argc <= 1 )
683 dofile( NULL );
684 else
685 for(; --argc > 0 ; dofile(*++argv) )
686 ;
687
688 exit(0);
689 }
Listing 2 -- b_getc.c
------------------------------------------------------------------------------
1 #include <stdio.h>
2 #include <dos.h>
3
4 /* B_GETC.C Get a character with a direct video bios call.
5 * this routine can be used to complement stderr as
6 * it can be used to get characters from the keyboard, even when input
7 * redirected. The typed character is returned in the low byte of the
8 * returned integer, the high byte holds the auxillary byte used to
9 * mark ALT keys and such. See the Technical Ref for more info.
10 *
11 * Copyright (C) 1985 Allen I. Holub. All rights reserved.
12 *
13 *----------------------------------------------------------------------
14 */
15
16 extern int int86(int, union REGS *, union REGS *);
17
18 /*----------------------------------------------------------------------*/
19
20 #define KB_INT 0x16 /* Keyboard BIOS interrupt */
21 #define GETC 0x00 /* Getc is service 0 */
22
23
24 int b_getc()
25 {
26 union REGS Regs;
27
28 Regs.h.ah = GETC ;
29 int86( KB_INT, &Regs, &Regs );
30 return( (int)Regs.x.ax );
31 }
Listing 3 -- look.asm
------------------------------------------------------------------------------
1 ; Static Name Aliases
2 ;
3 TITLE foo
4
5 _TEXT SEGMENT BYTE PUBLIC 'CODE'
6 _TEXT ENDS
7 CONST SEGMENT WORD PUBLIC 'CONST'
8 CONST ENDS
9 _BSS SEGMENT WORD PUBLIC 'BSS'
10 _BSS ENDS
11 _DATA SEGMENT WORD PUBLIC 'DATA'
12 _DATA ENDS
13 ;
14 DGROUP GROUP CONST, _BSS, _DATA
15 ASSUME CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP
16 ;
17 _DATA SEGMENT
18 EXTRN __chkstk:NEAR
19 _DATA ENDS
20 ;
21 _TEXT SEGMENT
22 ;
23 ; int look();
24 ;
25 ; Tests the bios to see if a key has been hit. If no key has been
26 ; hit then 0 is returned, else an int is returned in which the
27 ; high byte is the scan code and the low byte is the character
28 ; code, if the low byte is 0 then a non-ascii key has been hit
29 ;
30 PUBLIC _look
31 _look PROC NEAR
32 push bp
33 mov bp,sp
34 mov ax,2
35 call __chkstk
36
37 mov ah,1 ; service 1, Report on character ready
38 int 016H ; BIOS keyboard interrupt.
39 jnz exit ; jump if a key is available
40 ; (return the character)
41 mov ax,0 ; else (return 0);
42 exit:
43 mov sp,bp
44 pop bp
45 ret
46 _look ENDP
47
48 _TEXT ENDS
49 END