|
|
1.1 root 1: #ifndef lint
2: static char *sccsid = "@(#)more.c 4.16 (Berkeley) 83/08/26";
3: #endif
4:
5: /*
6: ** more.c - General purpose tty output filter and file perusal program
7: **
8: ** by Eric Shienbrood, UC Berkeley
9: **
10: ** modified by Geoff Peck, UCB to add underlining, single spacing
11: ** modified by John Foderaro, UCB to add -c and MORE environment variable
12: */
13:
14: #include <stdio.h>
15: #include <ctype.h>
16: #include <signal.h>
17: #include <errno.h>
18: #include <sgtty.h>
19: #include <setjmp.h>
20: #include <sys/types.h>
21: #include <sys/stat.h>
22:
23: #define HELPFILE "/usr/lib/more.help"
24: #define VI "/usr/ucb/vi"
25:
26: #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m))
27: #define Ftell(f) file_pos
28: #define Fseek(f,off) (file_pos=off,fseek(f,off,0))
29: #define Getc(f) (++file_pos, getc(f))
30: #define Ungetc(c,f) (--file_pos, ungetc(c,f))
31:
32: #define MBIT CBREAK
33: #define stty(fd,argp) ioctl(fd,TIOCSETN,argp)
34:
35: #define TBUFSIZ 1024
36: #define LINSIZ 256
37: #define ctrl(letter) ('letter' & 077)
38: #define RUBOUT '\177'
39: #define ESC '\033'
40: #define QUIT '\034'
41:
42: struct sgttyb otty, savetty;
43: long file_pos, file_size;
44: int fnum, no_intty, no_tty, slow_tty;
45: int dum_opt, dlines, onquit(), end_it();
46: int onsusp();
47: int nscroll = 11; /* Number of lines scrolled by 'd' */
48: int fold_opt = 1; /* Fold long lines */
49: int stop_opt = 1; /* Stop after form feeds */
50: int ssp_opt = 0; /* Suppress white space */
51: int ul_opt = 1; /* Underline as best we can */
52: int promptlen;
53: int Currline; /* Line we are currently at */
54: int startup = 1;
55: int firstf = 1;
56: int notell = 1;
57: int bad_so; /* True if overwriting does not turn off standout */
58: int inwait, Pause, errors;
59: int within; /* true if we are within a file,
60: false if we are between files */
61: int hard, dumb, noscroll, hardtabs, clreol;
62: int catch_susp; /* We should catch the SIGTSTP signal */
63: char **fnames; /* The list of file names */
64: int nfiles; /* Number of files left to process */
65: char *shell; /* The name of the shell to use */
66: int shellp; /* A previous shell command exists */
67: char ch;
68: jmp_buf restore;
69: char obuf[BUFSIZ]; /* stdout buffer */
70: char Line[LINSIZ]; /* Line buffer */
71: int Lpp = 24; /* lines per page */
72: char *Clear; /* clear screen */
73: char *eraseln; /* erase line */
74: char *Senter, *Sexit;/* enter and exit standout mode */
75: char *ULenter, *ULexit; /* enter and exit underline mode */
76: char *chUL; /* underline character */
77: char *chBS; /* backspace character */
78: char *Home; /* go to home */
79: char *cursorm; /* cursor movement */
80: char cursorhome[40]; /* contains cursor movement to home */
81: char *EodClr; /* clear rest of screen */
82: char *tgetstr();
83: int Mcol = 80; /* number of columns */
84: int Wrap = 1; /* set if automargins */
85: long fseek();
86: char *getenv();
87: struct {
88: long chrctr, line;
89: } context, screen_start;
90: extern char PC; /* pad character */
91: extern short ospeed;
92:
93:
94: main(argc, argv)
95: int argc;
96: char *argv[];
97: {
98: register FILE *f;
99: register char *s;
100: register char *p;
101: register char ch;
102: register int left;
103: int prnames = 0;
104: int initopt = 0;
105: int srchopt = 0;
106: int clearit = 0;
107: int initline;
108: char initbuf[80];
109: FILE *checkf();
110:
111: nfiles = argc;
112: fnames = argv;
113: initterm ();
114: if(s = getenv("MORE")) argscan(s);
115: while (--nfiles > 0) {
116: if ((ch = (*++fnames)[0]) == '-') {
117: argscan(*fnames+1);
118: }
119: else if (ch == '+') {
120: s = *fnames;
121: if (*++s == '/') {
122: srchopt++;
123: for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
124: *p++ = *s++;
125: *p = '\0';
126: }
127: else {
128: initopt++;
129: for (initline = 0; *s != '\0'; s++)
130: if (isdigit (*s))
131: initline = initline*10 + *s -'0';
132: --initline;
133: }
134: }
135: else break;
136: }
137: /* allow clreol only if Home and eraseln and EodClr strings are
138: * defined, and in that case, make sure we are in noscroll mode
139: */
140: if(clreol)
141: {
142: if ((*Home == '\0') || (*eraseln == '\0') || (*EodClr == '\0'))
143: clreol = 0;
144: else noscroll = 1;
145: }
146:
147: if (dlines == 0)
148: dlines = Lpp - (noscroll ? 1 : 2);
149: left = dlines;
150: if (nfiles > 1)
151: prnames++;
152: if (!no_intty && nfiles == 0) {
153: fputs("Usage: ",stderr);
154: fputs(argv[0],stderr);
155: fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr);
156: exit(1);
157: }
158: else
159: f = stdin;
160: if (!no_tty) {
161: signal(SIGQUIT, onquit);
162: signal(SIGINT, end_it);
163: if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
164: signal(SIGTSTP, onsusp);
165: catch_susp++;
166: }
167: stty (2, &otty);
168: }
169: if (no_intty) {
170: if (no_tty)
171: copy_file (stdin);
172: else {
173: if ((ch = Getc (f)) == '\f')
174: doclear();
175: else {
176: Ungetc (ch, f);
177: if (noscroll && (ch != EOF)) {
178: if (clreol)
179: home ();
180: else
181: doclear ();
182: }
183: }
184: if (srchopt)
185: {
186: search (initbuf, stdin, 1);
187: if (noscroll)
188: left--;
189: }
190: else if (initopt)
191: skiplns (initline, stdin);
192: screen (stdin, left);
193: }
194: no_intty = 0;
195: prnames++;
196: firstf = 0;
197: }
198:
199: while (fnum < nfiles) {
200: if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
201: context.line = context.chrctr = 0;
202: Currline = 0;
203: if (firstf) setjmp (restore);
204: if (firstf) {
205: firstf = 0;
206: if (srchopt)
207: {
208: search (initbuf, f, 1);
209: if (noscroll)
210: left--;
211: }
212: else if (initopt)
213: skiplns (initline, f);
214: }
215: else if (fnum < nfiles && !no_tty) {
216: setjmp (restore);
217: left = command (fnames[fnum], f);
218: }
219: if (left != 0) {
220: if ((noscroll || clearit) && (file_size != 0x7fffffffffffffffL))
221: if (clreol)
222: home ();
223: else
224: doclear ();
225: if (prnames) {
226: if (bad_so)
227: erase (0);
228: if (clreol)
229: cleareol ();
230: pr("::::::::::::::");
231: if (promptlen > 14)
232: erase (14);
233: printf ("\n");
234: if(clreol) cleareol();
235: printf("%s\n", fnames[fnum]);
236: if(clreol) cleareol();
237: printf("::::::::::::::\n", fnames[fnum]);
238: if (left > Lpp - 4)
239: left = Lpp - 4;
240: }
241: if (no_tty)
242: copy_file (f);
243: else {
244: within++;
245: screen(f, left);
246: within = 0;
247: }
248: }
249: setjmp (restore);
250: fflush(stdout);
251: fclose(f);
252: screen_start.line = screen_start.chrctr = 0L;
253: context.line = context.chrctr = 0L;
254: }
255: fnum++;
256: firstf = 0;
257: }
258: reset_tty ();
259: exit(0);
260: }
261:
262: argscan(s)
263: char *s;
264: {
265: for (dlines = 0; *s != '\0'; s++)
266: {
267: switch (*s)
268: {
269: case '0': case '1': case '2':
270: case '3': case '4': case '5':
271: case '6': case '7': case '8':
272: case '9':
273: dlines = dlines*10 + *s - '0';
274: break;
275: case 'd':
276: dum_opt = 1;
277: break;
278: case 'l':
279: stop_opt = 0;
280: break;
281: case 'f':
282: fold_opt = 0;
283: break;
284: case 'p':
285: noscroll++;
286: break;
287: case 'c':
288: clreol++;
289: break;
290: case 's':
291: ssp_opt = 1;
292: break;
293: case 'u':
294: ul_opt = 0;
295: break;
296: }
297: }
298: }
299:
300:
301: /*
302: ** Check whether the file named by fs is an ASCII file which the user may
303: ** access. If it is, return the opened file. Otherwise return NULL.
304: */
305:
306: FILE *
307: checkf (fs, clearfirst)
308: register char *fs;
309: int *clearfirst;
310: {
311: struct stat stbuf;
312: register FILE *f;
313: char c;
314:
315: if (stat (fs, &stbuf) == -1) {
316: fflush(stdout);
317: if (clreol)
318: cleareol ();
319: perror(fs);
320: return (NULL);
321: }
322: if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
323: printf("\n*** %s: directory ***\n\n", fs);
324: return (NULL);
325: }
326: if ((f=Fopen(fs, "r")) == NULL) {
327: fflush(stdout);
328: perror(fs);
329: return (NULL);
330: }
331: c = Getc(f);
332:
333: /* Try to see whether it is an ASCII file */
334:
335: switch ((c | *f->_ptr << 8) & 0177777) {
336: case 0405:
337: case 0407:
338: case 0410:
339: case 0411:
340: case 0413:
341: case 0177545:
342: printf("\n******** %s: Not a text file ********\n\n", fs);
343: fclose (f);
344: return (NULL);
345: default:
346: break;
347: }
348: if (c == '\f')
349: *clearfirst = 1;
350: else {
351: *clearfirst = 0;
352: Ungetc (c, f);
353: }
354: if ((file_size = stbuf.st_size) == 0)
355: file_size = 0x7fffffffffffffffL;
356: return (f);
357: }
358:
359: /*
360: ** A real function, for the tputs routine in termlib
361: */
362:
363: putch (ch)
364: char ch;
365: {
366: putchar (ch);
367: }
368:
369: /*
370: ** Print out the contents of the file f, one screenful at a time.
371: */
372:
373: #define STOP -10
374:
375: screen (f, num_lines)
376: register FILE *f;
377: register int num_lines;
378: {
379: register int c;
380: register int nchars;
381: int length; /* length of current line */
382: static int prev_len = 1; /* length of previous line */
383:
384: for (;;) {
385: while (num_lines > 0 && !Pause) {
386: if ((nchars = getline (f, &length)) == EOF)
387: {
388: if (clreol)
389: clreos();
390: return;
391: }
392: if (ssp_opt && length == 0 && prev_len == 0)
393: continue;
394: prev_len = length;
395: if (bad_so || (Senter && *Senter == ' ') && promptlen > 0)
396: erase (0);
397: /* must clear before drawing line since tabs on some terminals
398: * do not erase what they tab over.
399: */
400: if (clreol)
401: cleareol ();
402: prbuf (Line, length);
403: if (nchars < promptlen)
404: erase (nchars); /* erase () sets promptlen to 0 */
405: else promptlen = 0;
406: /* is this needed?
407: * if (clreol)
408: * cleareol(); /* must clear again in case we wrapped *
409: */
410: if (nchars < Mcol || !fold_opt)
411: putchar('\n');
412: if (nchars == STOP)
413: break;
414: num_lines--;
415: }
416: fflush(stdout);
417: if ((c = Getc(f)) == EOF)
418: {
419: if (clreol)
420: clreos ();
421: return;
422: }
423:
424: if (Pause && clreol)
425: clreos ();
426: Ungetc (c, f);
427: setjmp (restore);
428: Pause = 0; startup = 0;
429: if ((num_lines = command (NULL, f)) == 0)
430: return;
431: if (hard && promptlen > 0)
432: erase (0);
433: if (noscroll && num_lines >= dlines)
434: {
435: if (clreol)
436: home();
437: else
438: doclear ();
439: }
440: screen_start.line = Currline;
441: screen_start.chrctr = Ftell (f);
442: }
443: }
444:
445: /*
446: ** Come here if a quit signal is received
447: */
448:
449: onquit()
450: {
451: signal(SIGQUIT, SIG_IGN);
452: if (!inwait) {
453: putchar ('\n');
454: if (!startup) {
455: signal(SIGQUIT, onquit);
456: longjmp (restore, 1);
457: }
458: else
459: Pause++;
460: }
461: else if (!dum_opt && notell) {
462: write (2, "[Use q or Q to quit]", 20);
463: promptlen += 20;
464: notell = 0;
465: }
466: signal(SIGQUIT, onquit);
467: }
468:
469: /*
470: ** Clean up terminal state and exit. Also come here if interrupt signal received
471: */
472:
473: end_it ()
474: {
475:
476: reset_tty ();
477: if (clreol) {
478: putchar ('\r');
479: clreos ();
480: fflush (stdout);
481: }
482: else if (!clreol && (promptlen > 0)) {
483: kill_line ();
484: fflush (stdout);
485: }
486: else
487: write (2, "\n", 1);
488: _exit(0);
489: }
490:
491: copy_file(f)
492: register FILE *f;
493: {
494: register int c;
495:
496: while ((c = getc(f)) != EOF)
497: putchar(c);
498: }
499:
500: /* Simplified printf function */
501:
502: printf (fmt, args)
503: register char *fmt;
504: int args;
505: {
506: register int *argp;
507: register char ch;
508: register int ccount;
509:
510: ccount = 0;
511: argp = &args;
512: while (*fmt) {
513: while ((ch = *fmt++) != '%') {
514: if (ch == '\0')
515: return (ccount);
516: ccount++;
517: putchar (ch);
518: }
519: switch (*fmt++) {
520: case 'd':
521: ccount += printd (*argp);
522: break;
523: case 's':
524: ccount += pr ((char *)*argp);
525: break;
526: case '%':
527: ccount++;
528: argp--;
529: putchar ('%');
530: break;
531: case '0':
532: return (ccount);
533: default:
534: break;
535: }
536: ++argp;
537: }
538: return (ccount);
539:
540: }
541:
542: /*
543: ** Print an integer as a string of decimal digits,
544: ** returning the length of the print representation.
545: */
546:
547: printd (n)
548: int n;
549: {
550: int a, nchars;
551:
552: if (a = n/10)
553: nchars = 1 + printd(a);
554: else
555: nchars = 1;
556: putchar (n % 10 + '0');
557: return (nchars);
558: }
559:
560: /* Put the print representation of an integer into a string */
561: static char *sptr;
562:
563: scanstr (n, str)
564: int n;
565: char *str;
566: {
567: sptr = str;
568: Sprintf (n);
569: *sptr = '\0';
570: }
571:
572: Sprintf (n)
573: {
574: int a;
575:
576: if (a = n/10)
577: Sprintf (a);
578: *sptr++ = n % 10 + '0';
579: }
580:
581: static char bell = ctrl(G);
582:
583: strlen (s)
584: char *s;
585: {
586: register char *p;
587:
588: p = s;
589: while (*p++)
590: ;
591: return (p - s - 1);
592: }
593:
594: /* See whether the last component of the path name "path" is equal to the
595: ** string "string"
596: */
597:
598: tailequ (path, string)
599: char *path;
600: register char *string;
601: {
602: register char *tail;
603:
604: tail = path + strlen(path);
605: while (tail >= path)
606: if (*(--tail) == '/')
607: break;
608: ++tail;
609: while (*tail++ == *string++)
610: if (*tail == '\0')
611: return(1);
612: return(0);
613: }
614:
615: prompt (filename)
616: char *filename;
617: {
618: if (clreol)
619: cleareol ();
620: else if (promptlen > 0)
621: kill_line ();
622: if (!hard) {
623: promptlen = 8;
624: if (Senter && Sexit)
625: tputs (Senter, 1, putch);
626: if (clreol)
627: cleareol ();
628: pr("--More--");
629: if (filename != NULL) {
630: promptlen += printf ("(Next file: %s)", filename);
631: }
632: else if (!no_intty) {
633: promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size));
634: }
635: if (dum_opt) {
636: promptlen += pr("[Hit space to continue, Rubout to abort]");
637: }
638: if (Senter && Sexit)
639: tputs (Sexit, 1, putch);
640: if (clreol)
641: clreos ();
642: fflush(stdout);
643: }
644: else
645: write (2, &bell, 1);
646: inwait++;
647: }
648:
649: /*
650: ** Get a logical line
651: */
652:
653: getline(f, length)
654: register FILE *f;
655: int *length;
656: {
657: register int c;
658: register char *p;
659: register int column;
660: static int colflg;
661:
662: p = Line;
663: column = 0;
664: c = Getc (f);
665: if (colflg && c == '\n') {
666: Currline++;
667: c = Getc (f);
668: }
669: while (p < &Line[LINSIZ - 1]) {
670: if (c == EOF) {
671: if (p > Line) {
672: *p = '\0';
673: *length = p - Line;
674: return (column);
675: }
676: *length = p - Line;
677: return (EOF);
678: }
679: if (c == '\n') {
680: Currline++;
681: break;
682: }
683: *p++ = c;
684: if (c == '\t')
685: if (hardtabs && column < promptlen && !hard) {
686: if (eraseln && !dumb) {
687: column = 1 + (column | 7);
688: tputs (eraseln, 1, putch);
689: promptlen = 0;
690: }
691: else {
692: for (--p; column & 7 && p < &Line[LINSIZ - 1]; column++) {
693: *p++ = ' ';
694: }
695: if (column >= promptlen) promptlen = 0;
696: }
697: }
698: else
699: column = 1 + (column | 7);
700: else if (c == '\b' && column > 0)
701: column--;
702: else if (c == '\r')
703: column = 0;
704: else if (c == '\f' && stop_opt) {
705: p[-1] = '^';
706: *p++ = 'L';
707: column += 2;
708: Pause++;
709: }
710: else if (c == EOF) {
711: *length = p - Line;
712: return (column);
713: }
714: else if (c >= ' ' && c != RUBOUT)
715: column++;
716: if (column >= Mcol && fold_opt) break;
717: c = Getc (f);
718: }
719: if (column >= Mcol && Mcol > 0) {
720: if (!Wrap) {
721: *p++ = '\n';
722: }
723: }
724: colflg = column == Mcol && fold_opt;
725: *length = p - Line;
726: *p = 0;
727: return (column);
728: }
729:
730: /*
731: ** Erase the rest of the prompt, assuming we are starting at column col.
732: */
733:
734: erase (col)
735: register int col;
736: {
737:
738: if (promptlen == 0)
739: return;
740: if (hard) {
741: putchar ('\n');
742: }
743: else {
744: if (col == 0)
745: putchar ('\r');
746: if (!dumb && eraseln)
747: tputs (eraseln, 1, putch);
748: else
749: for (col = promptlen - col; col > 0; col--)
750: putchar (' ');
751: }
752: promptlen = 0;
753: }
754:
755: /*
756: ** Erase the current line entirely
757: */
758:
759: kill_line ()
760: {
761: erase (0);
762: if (!eraseln || dumb) putchar ('\r');
763: }
764:
765: /*
766: * force clear to end of line
767: */
768: cleareol()
769: {
770: tputs(eraseln, 1, putch);
771: }
772:
773: clreos()
774: {
775: tputs(EodClr, 1, putch);
776: }
777:
778: /*
779: ** Print string and return number of characters
780: */
781:
782: pr(s1)
783: char *s1;
784: {
785: register char *s;
786: register char c;
787:
788: for (s = s1; c = *s++; )
789: putchar(c);
790: return (s - s1 - 1);
791: }
792:
793:
794: /* Print a buffer of n characters */
795:
796: prbuf (s, n)
797: register char *s;
798: register int n;
799: {
800: char c; /* next ouput character */
801: register int state; /* next output char's UL state */
802: static int pstate = 0; /* current terminal UL state (off) */
803:
804: while (--n >= 0)
805: if (!ul_opt)
806: putchar (*s++);
807: else {
808: if (n >= 2 && s[0] == '_' && s[1] == '\b') {
809: n -= 2;
810: s += 2;
811: c = *s++;
812: state = 1;
813: } else if (n >= 2 && s[1] == '\b' && s[2] == '_') {
814: n -= 2;
815: c = *s++;
816: s += 2;
817: state = 1;
818: } else {
819: c = *s++;
820: state = 0;
821: }
822: if (state != pstate)
823: tputs(state ? ULenter : ULexit, 1, putch);
824: pstate = state;
825: putchar(c);
826: if (state && *chUL) {
827: pr(chBS);
828: tputs(chUL, 1, putch);
829: }
830: }
831: }
832:
833: /*
834: ** Clear the screen
835: */
836:
837: doclear()
838: {
839: if (Clear && !hard) {
840: tputs(Clear, 1, putch);
841:
842: /* Put out carriage return so that system doesn't
843: ** get confused by escape sequences when expanding tabs
844: */
845: putchar ('\r');
846: promptlen = 0;
847: }
848: }
849:
850: /*
851: * Go to home position
852: */
853: home()
854: {
855: tputs(Home,1,putch);
856: }
857:
858: static int lastcmd, lastarg, lastp;
859: static int lastcolon;
860: char shell_line[132];
861:
862: /*
863: ** Read a command and do it. A command consists of an optional integer
864: ** argument followed by the command character. Return the number of lines
865: ** to display in the next screenful. If there is nothing more to display
866: ** in the current file, zero is returned.
867: */
868:
869: command (filename, f)
870: char *filename;
871: register FILE *f;
872: {
873: register int nlines;
874: register int retval;
875: register char c;
876: char colonch;
877: FILE *helpf;
878: int done;
879: char comchar, cmdbuf[80], *p;
880:
881: #define ret(val) retval=val;done++;break
882:
883: done = 0;
884: if (!errors)
885: prompt (filename);
886: else
887: errors = 0;
888: if (MBIT == RAW && slow_tty) {
889: otty.sg_flags |= MBIT;
890: stty(2, &otty);
891: }
892: for (;;) {
893: nlines = number (&comchar);
894: lastp = colonch = 0;
895: if (comchar == '.') { /* Repeat last command */
896: lastp++;
897: comchar = lastcmd;
898: nlines = lastarg;
899: if (lastcmd == ':')
900: colonch = lastcolon;
901: }
902: lastcmd = comchar;
903: lastarg = nlines;
904: if (comchar == otty.sg_erase) {
905: kill_line ();
906: prompt (filename);
907: continue;
908: }
909: switch (comchar) {
910: case ':':
911: retval = colon (filename, colonch, nlines);
912: if (retval >= 0)
913: done++;
914: break;
915: case ' ':
916: case 'z':
917: if (nlines == 0) nlines = dlines;
918: else if (comchar == 'z') dlines = nlines;
919: ret (nlines);
920: case 'd':
921: case ctrl(D):
922: if (nlines != 0) nscroll = nlines;
923: ret (nscroll);
924: case RUBOUT:
925: case 'q':
926: case 'Q':
927: end_it ();
928: case 's':
929: case 'f':
930: if (nlines == 0) nlines++;
931: if (comchar == 'f')
932: nlines *= dlines;
933: putchar ('\r');
934: erase (0);
935: printf ("\n");
936: if (clreol)
937: cleareol ();
938: printf ("...skipping %d line", nlines);
939: if (nlines > 1)
940: pr ("s\n");
941: else
942: pr ("\n");
943:
944: if (clreol)
945: cleareol ();
946: pr ("\n");
947:
948: while (nlines > 0) {
949: while ((c = Getc (f)) != '\n')
950: if (c == EOF) {
951: retval = 0;
952: done++;
953: goto endsw;
954: }
955: Currline++;
956: nlines--;
957: }
958: ret (dlines);
959: case '\n':
960: if (nlines != 0)
961: dlines = nlines;
962: else
963: nlines = 1;
964: ret (nlines);
965: case '\f':
966: if (!no_intty) {
967: doclear ();
968: Fseek (f, screen_start.chrctr);
969: Currline = screen_start.line;
970: ret (dlines);
971: }
972: else {
973: write (2, &bell, 1);
974: break;
975: }
976: case '\'':
977: if (!no_intty) {
978: kill_line ();
979: pr ("\n***Back***\n\n");
980: Fseek (f, context.chrctr);
981: Currline = context.line;
982: ret (dlines);
983: }
984: else {
985: write (2, &bell, 1);
986: break;
987: }
988: case '=':
989: kill_line ();
990: promptlen = printd (Currline);
991: fflush (stdout);
992: break;
993: case 'n':
994: lastp++;
995: case '/':
996: if (nlines == 0) nlines++;
997: kill_line ();
998: pr ("/");
999: promptlen = 1;
1000: fflush (stdout);
1001: if (lastp) {
1002: write (2,"\r", 1);
1003: search (NULL, f, nlines); /* Use previous r.e. */
1004: }
1005: else {
1006: ttyin (cmdbuf, 78, '/');
1007: write (2, "\r", 1);
1008: search (cmdbuf, f, nlines);
1009: }
1010: ret (dlines-1);
1011: case '!':
1012: do_shell (filename);
1013: break;
1014: case 'h':
1015: if ((helpf = fopen (HELPFILE, "r")) == NULL)
1016: error ("Can't open help file");
1017: if (noscroll) doclear ();
1018: copy_file (helpf);
1019: close (helpf);
1020: prompt (filename);
1021: break;
1022: case 'v': /* This case should go right before default */
1023: if (!no_intty) {
1024: kill_line ();
1025: cmdbuf[0] = '+';
1026: scanstr (Currline, &cmdbuf[1]);
1027: pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
1028: execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
1029: break;
1030: }
1031: default:
1032: write (2, &bell, 1);
1033: break;
1034: }
1035: if (done) break;
1036: }
1037: putchar ('\r');
1038: endsw:
1039: inwait = 0;
1040: notell++;
1041: if (MBIT == RAW && slow_tty) {
1042: otty.sg_flags &= ~MBIT;
1043: stty(2, &otty);
1044: }
1045: return (retval);
1046: }
1047:
1048: char ch;
1049:
1050: /*
1051: * Execute a colon-prefixed command.
1052: * Returns <0 if not a command that should cause
1053: * more of the file to be printed.
1054: */
1055:
1056: colon (filename, cmd, nlines)
1057: char *filename;
1058: int cmd;
1059: int nlines;
1060: {
1061: if (cmd == 0)
1062: ch = readch ();
1063: else
1064: ch = cmd;
1065: lastcolon = ch;
1066: switch (ch) {
1067: case 'f':
1068: kill_line ();
1069: if (!no_intty)
1070: promptlen = printf ("\"%s\" line %d", fnames[fnum], Currline);
1071: else
1072: promptlen = printf ("[Not a file] line %d", Currline);
1073: fflush (stdout);
1074: return (-1);
1075: case 'n':
1076: if (nlines == 0) {
1077: if (fnum >= nfiles - 1)
1078: end_it ();
1079: nlines++;
1080: }
1081: putchar ('\r');
1082: erase (0);
1083: skipf (nlines);
1084: return (0);
1085: case 'p':
1086: if (no_intty) {
1087: write (2, &bell, 1);
1088: return (-1);
1089: }
1090: putchar ('\r');
1091: erase (0);
1092: if (nlines == 0)
1093: nlines++;
1094: skipf (-nlines);
1095: return (0);
1096: case '!':
1097: do_shell (filename);
1098: return (-1);
1099: case 'q':
1100: case 'Q':
1101: end_it ();
1102: default:
1103: write (2, &bell, 1);
1104: return (-1);
1105: }
1106: }
1107:
1108: /*
1109: ** Read a decimal number from the terminal. Set cmd to the non-digit which
1110: ** terminates the number.
1111: */
1112:
1113: number(cmd)
1114: char *cmd;
1115: {
1116: register int i;
1117:
1118: i = 0; ch = otty.sg_kill;
1119: for (;;) {
1120: ch = readch ();
1121: if (ch >= '0' && ch <= '9')
1122: i = i*10 + ch - '0';
1123: else if (ch == otty.sg_kill)
1124: i = 0;
1125: else {
1126: *cmd = ch;
1127: break;
1128: }
1129: }
1130: return (i);
1131: }
1132:
1133: do_shell (filename)
1134: char *filename;
1135: {
1136: char cmdbuf[80];
1137:
1138: kill_line ();
1139: pr ("!");
1140: fflush (stdout);
1141: promptlen = 1;
1142: if (lastp)
1143: pr (shell_line);
1144: else {
1145: ttyin (cmdbuf, 78, '!');
1146: if (expand (shell_line, cmdbuf)) {
1147: kill_line ();
1148: promptlen = printf ("!%s", shell_line);
1149: }
1150: }
1151: fflush (stdout);
1152: write (2, "\n", 1);
1153: promptlen = 0;
1154: shellp = 1;
1155: execute (filename, shell, shell, "-c", shell_line, 0);
1156: }
1157:
1158: /*
1159: ** Search for nth ocurrence of regular expression contained in buf in the file
1160: */
1161:
1162: search (buf, file, n)
1163: char buf[];
1164: FILE *file;
1165: register int n;
1166: {
1167: long startline = Ftell (file);
1168: register long line1 = startline;
1169: register long line2 = startline;
1170: register long line3 = startline;
1171: register int lncount;
1172: int saveln, rv, re_exec();
1173: char *s, *re_comp();
1174:
1175: context.line = saveln = Currline;
1176: context.chrctr = startline;
1177: lncount = 0;
1178: if ((s = re_comp (buf)) != 0)
1179: error (s);
1180: while (!feof (file)) {
1181: line3 = line2;
1182: line2 = line1;
1183: line1 = Ftell (file);
1184: rdline (file);
1185: lncount++;
1186: if ((rv = re_exec (Line)) == 1)
1187: if (--n == 0) {
1188: if (lncount > 3 || (lncount > 1 && no_intty))
1189: {
1190: pr ("\n");
1191: if (clreol)
1192: cleareol ();
1193: pr("...skipping\n");
1194: }
1195: if (!no_intty) {
1196: Currline -= (lncount >= 3 ? 3 : lncount);
1197: Fseek (file, line3);
1198: if (noscroll)
1199: if (clreol) {
1200: home ();
1201: cleareol ();
1202: }
1203: else
1204: doclear ();
1205: }
1206: else {
1207: kill_line ();
1208: if (noscroll)
1209: if (clreol) {
1210: home ();
1211: cleareol ();
1212: }
1213: else
1214: doclear ();
1215: pr (Line);
1216: putchar ('\n');
1217: }
1218: break;
1219: }
1220: else if (rv == -1)
1221: error ("Regular expression botch");
1222: }
1223: if (feof (file)) {
1224: if (!no_intty) {
1225: file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */
1226: Currline = saveln;
1227: Fseek (file, startline);
1228: }
1229: else {
1230: pr ("\nPattern not found\n");
1231: end_it ();
1232: }
1233: error ("Pattern not found");
1234: }
1235: }
1236:
1237: execute (filename, cmd, args)
1238: char *filename;
1239: char *cmd, *args;
1240: {
1241: int id;
1242:
1243: fflush (stdout);
1244: reset_tty ();
1245: while ((id = fork ()) < 0)
1246: sleep (5);
1247: if (id == 0) {
1248: execv (cmd, &args);
1249: write (2, "exec failed\n", 12);
1250: exit (1);
1251: }
1252: signal (SIGINT, SIG_IGN);
1253: signal (SIGQUIT, SIG_IGN);
1254: if (catch_susp)
1255: signal(SIGTSTP, SIG_DFL);
1256: wait (0);
1257: signal (SIGINT, end_it);
1258: signal (SIGQUIT, onquit);
1259: if (catch_susp)
1260: signal(SIGTSTP, onsusp);
1261: set_tty ();
1262: pr ("------------------------\n");
1263: prompt (filename);
1264: }
1265: /*
1266: ** Skip n lines in the file f
1267: */
1268:
1269: skiplns (n, f)
1270: register int n;
1271: register FILE *f;
1272: {
1273: register char c;
1274:
1275: while (n > 0) {
1276: while ((c = Getc (f)) != '\n')
1277: if (c == EOF)
1278: return;
1279: n--;
1280: Currline++;
1281: }
1282: }
1283:
1284: /*
1285: ** Skip nskip files in the file list (from the command line). Nskip may be
1286: ** negative.
1287: */
1288:
1289: skipf (nskip)
1290: register int nskip;
1291: {
1292: if (nskip == 0) return;
1293: if (nskip > 0) {
1294: if (fnum + nskip > nfiles - 1)
1295: nskip = nfiles - fnum - 1;
1296: }
1297: else if (within)
1298: ++fnum;
1299: fnum += nskip;
1300: if (fnum < 0)
1301: fnum = 0;
1302: pr ("\n...Skipping ");
1303: pr ("\n");
1304: if (clreol)
1305: cleareol ();
1306: pr ("...Skipping ");
1307: pr (nskip > 0 ? "to file " : "back to file ");
1308: pr (fnames[fnum]);
1309: pr ("\n");
1310: if (clreol)
1311: cleareol ();
1312: pr ("\n");
1313: --fnum;
1314: }
1315:
1316: /*----------------------------- Terminal I/O -------------------------------*/
1317:
1318: initterm ()
1319: {
1320: char buf[TBUFSIZ];
1321: char clearbuf[100];
1322: char *clearptr, *padstr;
1323: int ldisc;
1324: char *term;
1325:
1326: setbuf(stdout, obuf);
1327: if (!(no_tty = gtty(1, &otty))) {
1328: if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) {
1329: dumb++; ul_opt = 0;
1330: }
1331: else {
1332: if (((Lpp = tgetnum("li")) < 0) || tgetflag("hc")) {
1333: hard++; /* Hard copy terminal */
1334: Lpp = 24;
1335: }
1336: if (tailequ (fnames[0], "page") || !hard && tgetflag("ns"))
1337: noscroll++;
1338: if ((Mcol = tgetnum("co")) < 0)
1339: Mcol = 80;
1340: Wrap = tgetflag("am");
1341: bad_so = tgetflag ("xs");
1342: clearptr = clearbuf;
1343: eraseln = tgetstr("ce",&clearptr);
1344: Clear = tgetstr("cl", &clearptr);
1345: Senter = tgetstr("so", &clearptr);
1346: Sexit = tgetstr("se", &clearptr);
1347:
1348: /*
1349: * Set up for underlining: some terminals don't need it;
1350: * others have start/stop sequences, still others have an
1351: * underline char sequence which is assumed to move the
1352: * cursor forward one character. If underline sequence
1353: * isn't available, settle for standout sequence.
1354: */
1355:
1356: if (tgetflag("ul") || tgetflag("os"))
1357: ul_opt = 0;
1358: if ((chUL = tgetstr("uc", &clearptr)) == NULL )
1359: chUL = "";
1360: if ((ULenter = tgetstr("us", &clearptr)) == NULL &&
1361: (!*chUL) && (ULenter = tgetstr("so", &clearptr)) == NULL)
1362: ULenter = "";
1363: if ((ULexit = tgetstr("ue", &clearptr)) == NULL &&
1364: (!*chUL) && (ULexit = tgetstr("se", &clearptr)) == NULL)
1365: ULexit = "";
1366:
1367: if (padstr = tgetstr("pc", &clearptr))
1368: PC = *padstr;
1369: Home = tgetstr("ho",&clearptr);
1370: if (Home == 0 || *Home == '\0')
1371: {
1372: if ((cursorm = tgetstr("cm", &clearptr)) != NULL) {
1373: strcpy(cursorhome, tgoto(cursorm, 0, 0));
1374: Home = cursorhome;
1375: }
1376: }
1377: EodClr = tgetstr("cd", &clearptr);
1378: }
1379: if ((shell = getenv("SHELL")) == NULL)
1380: shell = "/bin/sh";
1381: }
1382: no_intty = gtty(0, &otty);
1383: gtty(2, &otty);
1384: savetty = otty;
1385: ospeed = otty.sg_ospeed;
1386: slow_tty = ospeed < B1200;
1387: hardtabs = !(otty.sg_flags & XTABS);
1388: if (!no_tty) {
1389: otty.sg_flags &= ~ECHO;
1390: if (MBIT == CBREAK || !slow_tty)
1391: otty.sg_flags |= MBIT;
1392: }
1393: }
1394:
1395: readch ()
1396: {
1397: char ch;
1398: extern int errno;
1399:
1400: if (read (2, &ch, 1) <= 0)
1401: if (errno != EINTR)
1402: exit(0);
1403: else
1404: ch = otty.sg_kill;
1405: return (ch);
1406: }
1407:
1408: static char BS = '\b';
1409: static char CARAT = '^';
1410:
1411: ttyin (buf, nmax, pchar)
1412: char buf[];
1413: register int nmax;
1414: char pchar;
1415: {
1416: register char *sptr;
1417: register char ch;
1418: register int slash = 0;
1419: int maxlen;
1420: char cbuf;
1421:
1422: sptr = buf;
1423: maxlen = 0;
1424: while (sptr - buf < nmax) {
1425: if (promptlen > maxlen) maxlen = promptlen;
1426: ch = readch ();
1427: if (ch == '\\') {
1428: slash++;
1429: }
1430: else if ((ch == otty.sg_erase) && !slash) {
1431: if (sptr > buf) {
1432: --promptlen;
1433: write (2, &BS, 1);
1434: --sptr;
1435: if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
1436: --promptlen;
1437: write (2, &BS, 1);
1438: }
1439: continue;
1440: }
1441: else {
1442: if (!eraseln) promptlen = maxlen;
1443: longjmp (restore, 1);
1444: }
1445: }
1446: else if ((ch == otty.sg_kill) && !slash) {
1447: if (hard) {
1448: show (ch);
1449: putchar ('\n');
1450: putchar (pchar);
1451: }
1452: else {
1453: putchar ('\r');
1454: putchar (pchar);
1455: if (eraseln)
1456: erase (1);
1457: promptlen = 1;
1458: }
1459: sptr = buf;
1460: fflush (stdout);
1461: continue;
1462: }
1463: if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) {
1464: write (2, &BS, 1);
1465: --sptr;
1466: }
1467: if (ch != '\\')
1468: slash = 0;
1469: *sptr++ = ch;
1470: if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1471: ch += ch == RUBOUT ? -0100 : 0100;
1472: write (2, &CARAT, 1);
1473: promptlen++;
1474: }
1475: cbuf = ch;
1476: if (ch != '\n' && ch != ESC) {
1477: write (2, &cbuf, 1);
1478: promptlen++;
1479: }
1480: else
1481: break;
1482: }
1483: *--sptr = '\0';
1484: if (!eraseln) promptlen = maxlen;
1485: if (sptr - buf >= nmax - 1)
1486: error ("Line too long");
1487: }
1488:
1489: expand (outbuf, inbuf)
1490: char *outbuf;
1491: char *inbuf;
1492: {
1493: register char *instr;
1494: register char *outstr;
1495: register char ch;
1496: char temp[200];
1497: int changed = 0;
1498:
1499: instr = inbuf;
1500: outstr = temp;
1501: while ((ch = *instr++) != '\0')
1502: switch (ch) {
1503: case '%':
1504: if (!no_intty) {
1505: strcpy (outstr, fnames[fnum]);
1506: outstr += strlen (fnames[fnum]);
1507: changed++;
1508: }
1509: else
1510: *outstr++ = ch;
1511: break;
1512: case '!':
1513: if (!shellp)
1514: error ("No previous command to substitute for");
1515: strcpy (outstr, shell_line);
1516: outstr += strlen (shell_line);
1517: changed++;
1518: break;
1519: case '\\':
1520: if (*instr == '%' || *instr == '!') {
1521: *outstr++ = *instr++;
1522: break;
1523: }
1524: default:
1525: *outstr++ = ch;
1526: }
1527: *outstr++ = '\0';
1528: strcpy (outbuf, temp);
1529: return (changed);
1530: }
1531:
1532: show (ch)
1533: register char ch;
1534: {
1535: char cbuf;
1536:
1537: if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1538: ch += ch == RUBOUT ? -0100 : 0100;
1539: write (2, &CARAT, 1);
1540: promptlen++;
1541: }
1542: cbuf = ch;
1543: write (2, &cbuf, 1);
1544: promptlen++;
1545: }
1546:
1547: error (mess)
1548: char *mess;
1549: {
1550: if (clreol)
1551: cleareol ();
1552: else
1553: kill_line ();
1554: promptlen += strlen (mess);
1555: if (Senter && Sexit) {
1556: tputs (Senter, 1, putch);
1557: pr(mess);
1558: tputs (Sexit, 1, putch);
1559: }
1560: else
1561: pr (mess);
1562: fflush(stdout);
1563: errors++;
1564: longjmp (restore, 1);
1565: }
1566:
1567:
1568: set_tty ()
1569: {
1570: otty.sg_flags |= MBIT;
1571: otty.sg_flags &= ~ECHO;
1572: stty(2, &otty);
1573: }
1574:
1575: reset_tty ()
1576: {
1577: otty.sg_flags |= ECHO;
1578: otty.sg_flags &= ~MBIT;
1579: stty(2, &savetty);
1580: }
1581:
1582: rdline (f)
1583: register FILE *f;
1584: {
1585: register char c;
1586: register char *p;
1587:
1588: p = Line;
1589: while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
1590: *p++ = c;
1591: if (c == '\n')
1592: Currline++;
1593: *p = '\0';
1594: }
1595:
1596: /* Come here when we get a suspend signal from the terminal */
1597:
1598: onsusp ()
1599: {
1600: /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
1601: signal(SIGTTOU, SIG_IGN);
1602: reset_tty ();
1603: fflush (stdout);
1604: signal(SIGTTOU, SIG_DFL);
1605: /* Send the TSTP signal to suspend our process group */
1606: signal(SIGTSTP, SIG_DFL);
1607: sigsetmask(0);
1608: kill (0, SIGTSTP);
1609: /* Pause for station break */
1610:
1611: /* We're back */
1612: signal (SIGTSTP, onsusp);
1613: set_tty ();
1614: if (inwait)
1615: longjmp (restore);
1616: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.