|
|
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) {
336: default:
337: if(c>=' ' && c<=0177)
338: break;
339: printf("\n******** %s: Not a text file ********\n\n", fs);
340: fclose (f);
341: return (NULL);
342: case '\b':
343: case '\t':
344: case '\n':
345: case '\f':
346: case '\r':
347: break;
348: }
349: if (c == '\f')
350: *clearfirst = 1;
351: else {
352: *clearfirst = 0;
353: Ungetc (c, f);
354: }
355: if ((file_size = stbuf.st_size) == 0)
356: file_size = 0x7fffffffffffffffL;
357: return (f);
358: }
359:
360: /*
361: ** A real function, for the tputs routine in termlib
362: */
363:
364: putch (ch)
365: char ch;
366: {
367: putchar (ch);
368: }
369:
370: /*
371: ** Print out the contents of the file f, one screenful at a time.
372: */
373:
374: #define STOP -10
375:
376: screen (f, num_lines)
377: register FILE *f;
378: register int num_lines;
379: {
380: register int c;
381: register int nchars;
382: int length; /* length of current line */
383: static int prev_len = 1; /* length of previous line */
384:
385: for (;;) {
386: while (num_lines > 0 && !Pause) {
387: if ((nchars = getline (f, &length)) == EOF)
388: {
389: if (clreol)
390: clreos();
391: return;
392: }
393: if (ssp_opt && length == 0 && prev_len == 0)
394: continue;
395: prev_len = length;
396: if (bad_so || (Senter && *Senter == ' ') && promptlen > 0)
397: erase (0);
398: /* must clear before drawing line since tabs on some terminals
399: * do not erase what they tab over.
400: */
401: if (clreol)
402: cleareol ();
403: prbuf (Line, length);
404: if (nchars < promptlen)
405: erase (nchars); /* erase () sets promptlen to 0 */
406: else promptlen = 0;
407: /* is this needed?
408: * if (clreol)
409: * cleareol(); /* must clear again in case we wrapped *
410: */
411: if (nchars < Mcol || !fold_opt)
412: putchar('\n');
413: if (nchars == STOP)
414: break;
415: num_lines--;
416: }
417: fflush(stdout);
418: if ((c = Getc(f)) == EOF)
419: {
420: if (clreol)
421: clreos ();
422: return;
423: }
424:
425: if (Pause && clreol)
426: clreos ();
427: Ungetc (c, f);
428: setjmp (restore);
429: Pause = 0; startup = 0;
430: if ((num_lines = command (NULL, f)) == 0)
431: return;
432: if (hard && promptlen > 0)
433: erase (0);
434: if (noscroll && num_lines >= dlines)
435: {
436: if (clreol)
437: home();
438: else
439: doclear ();
440: }
441: screen_start.line = Currline;
442: screen_start.chrctr = Ftell (f);
443: }
444: }
445:
446: /*
447: ** Come here if a quit signal is received
448: */
449:
450: onquit()
451: {
452: signal(SIGQUIT, SIG_IGN);
453: if (!inwait) {
454: putchar ('\n');
455: if (!startup) {
456: signal(SIGQUIT, onquit);
457: longjmp (restore, 1);
458: }
459: else
460: Pause++;
461: }
462: else if (!dum_opt && notell) {
463: write (2, "[Use q or Q to quit]", 20);
464: promptlen += 20;
465: notell = 0;
466: }
467: signal(SIGQUIT, onquit);
468: }
469:
470: /*
471: ** Clean up terminal state and exit. Also come here if interrupt signal received
472: */
473:
474: end_it ()
475: {
476:
477: reset_tty ();
478: if (clreol) {
479: putchar ('\r');
480: clreos ();
481: fflush (stdout);
482: }
483: else if (!clreol && (promptlen > 0)) {
484: kill_line ();
485: fflush (stdout);
486: }
487: else
488: write (2, "\n", 1);
489: _exit(0);
490: }
491:
492: copy_file(f)
493: register FILE *f;
494: {
495: register int c;
496:
497: while ((c = getc(f)) != EOF)
498: putchar(c);
499: }
500:
501: /* Simplified printf function */
502:
503: printf (fmt, args)
504: register char *fmt;
505: int args;
506: {
507: register int *argp;
508: register char ch;
509: register int ccount;
510:
511: ccount = 0;
512: argp = &args;
513: while (*fmt) {
514: while ((ch = *fmt++) != '%') {
515: if (ch == '\0')
516: return (ccount);
517: ccount++;
518: putchar (ch);
519: }
520: switch (*fmt++) {
521: case 'd':
522: ccount += printd (*argp);
523: break;
524: case 's':
525: ccount += pr ((char *)*argp);
526: break;
527: case '%':
528: ccount++;
529: argp--;
530: putchar ('%');
531: break;
532: case '0':
533: return (ccount);
534: default:
535: break;
536: }
537: ++argp;
538: }
539: return (ccount);
540:
541: }
542:
543: /*
544: ** Print an integer as a string of decimal digits,
545: ** returning the length of the print representation.
546: */
547:
548: printd (n)
549: int n;
550: {
551: int a, nchars;
552:
553: if (a = n/10)
554: nchars = 1 + printd(a);
555: else
556: nchars = 1;
557: putchar (n % 10 + '0');
558: return (nchars);
559: }
560:
561: /* Put the print representation of an integer into a string */
562: static char *sptr;
563:
564: scanstr (n, str)
565: int n;
566: char *str;
567: {
568: sptr = str;
569: Sprintf (n);
570: *sptr = '\0';
571: }
572:
573: Sprintf (n)
574: {
575: int a;
576:
577: if (a = n/10)
578: Sprintf (a);
579: *sptr++ = n % 10 + '0';
580: }
581:
582: static char bell = ctrl(G);
583:
584: strlen (s)
585: char *s;
586: {
587: register char *p;
588:
589: p = s;
590: while (*p++)
591: ;
592: return (p - s - 1);
593: }
594:
595: /* See whether the last component of the path name "path" is equal to the
596: ** string "string"
597: */
598:
599: tailequ (path, string)
600: char *path;
601: register char *string;
602: {
603: register char *tail;
604:
605: tail = path + strlen(path);
606: while (tail >= path)
607: if (*(--tail) == '/')
608: break;
609: ++tail;
610: while (*tail++ == *string++)
611: if (*tail == '\0')
612: return(1);
613: return(0);
614: }
615:
616: prompt (filename)
617: char *filename;
618: {
619: if (clreol)
620: cleareol ();
621: else if (promptlen > 0)
622: kill_line ();
623: if (!hard) {
624: promptlen = 8;
625: if (Senter && Sexit)
626: tputs (Senter, 1, putch);
627: if (clreol)
628: cleareol ();
629: pr("--More--");
630: if (filename != NULL) {
631: promptlen += printf ("(Next file: %s)", filename);
632: }
633: else if (!no_intty) {
634: promptlen += printf ("(%d%%)", (int)((file_pos * 100) / file_size));
635: }
636: if (dum_opt) {
637: promptlen += pr("[Hit space to continue, Rubout to abort]");
638: }
639: if (Senter && Sexit)
640: tputs (Sexit, 1, putch);
641: if (clreol)
642: clreos ();
643: fflush(stdout);
644: }
645: else
646: write (2, &bell, 1);
647: inwait++;
648: }
649:
650: /*
651: ** Get a logical line
652: */
653:
654: getline(f, length)
655: register FILE *f;
656: int *length;
657: {
658: register int c;
659: register char *p;
660: register int column;
661: static int colflg;
662:
663: p = Line;
664: column = 0;
665: c = Getc (f);
666: if (colflg && c == '\n') {
667: Currline++;
668: c = Getc (f);
669: }
670: while (p < &Line[LINSIZ - 1]) {
671: if (c == EOF) {
672: if (p > Line) {
673: *p = '\0';
674: *length = p - Line;
675: return (column);
676: }
677: *length = p - Line;
678: return (EOF);
679: }
680: if (c == '\n') {
681: Currline++;
682: break;
683: }
684: *p++ = c;
685: if (c == '\t')
686: if (hardtabs && column < promptlen && !hard) {
687: if (eraseln && !dumb) {
688: column = 1 + (column | 7);
689: tputs (eraseln, 1, putch);
690: promptlen = 0;
691: }
692: else {
693: for (--p; column & 7 && p < &Line[LINSIZ - 1]; column++) {
694: *p++ = ' ';
695: }
696: if (column >= promptlen) promptlen = 0;
697: }
698: }
699: else
700: column = 1 + (column | 7);
701: else if (c == '\b' && column > 0)
702: column--;
703: else if (c == '\r')
704: column = 0;
705: else if (c == '\f' && stop_opt) {
706: p[-1] = '^';
707: *p++ = 'L';
708: column += 2;
709: Pause++;
710: }
711: else if (c == EOF) {
712: *length = p - Line;
713: return (column);
714: }
715: else if (c >= ' ' && c != RUBOUT)
716: column++;
717: if (column >= Mcol && fold_opt) break;
718: c = Getc (f);
719: }
720: if (column >= Mcol && Mcol > 0) {
721: if (!Wrap) {
722: *p++ = '\n';
723: }
724: }
725: colflg = column == Mcol && fold_opt;
726: *length = p - Line;
727: *p = 0;
728: return (column);
729: }
730:
731: /*
732: ** Erase the rest of the prompt, assuming we are starting at column col.
733: */
734:
735: erase (col)
736: register int col;
737: {
738:
739: if (promptlen == 0)
740: return;
741: if (hard) {
742: putchar ('\n');
743: }
744: else {
745: if (col == 0)
746: putchar ('\r');
747: if (!dumb && eraseln)
748: tputs (eraseln, 1, putch);
749: else
750: for (col = promptlen - col; col > 0; col--)
751: putchar (' ');
752: }
753: promptlen = 0;
754: }
755:
756: /*
757: ** Erase the current line entirely
758: */
759:
760: kill_line ()
761: {
762: erase (0);
763: if (!eraseln || dumb) putchar ('\r');
764: }
765:
766: /*
767: * force clear to end of line
768: */
769: cleareol()
770: {
771: tputs(eraseln, 1, putch);
772: }
773:
774: clreos()
775: {
776: tputs(EodClr, 1, putch);
777: }
778:
779: /*
780: ** Print string and return number of characters
781: */
782:
783: pr(s1)
784: char *s1;
785: {
786: register char *s;
787: register char c;
788:
789: for (s = s1; c = *s++; )
790: putchar(c);
791: return (s - s1 - 1);
792: }
793:
794:
795: /* Print a buffer of n characters */
796:
797: prbuf (s, n)
798: register char *s;
799: register int n;
800: {
801: char c; /* next ouput character */
802: register int state; /* next output char's UL state */
803: static int pstate = 0; /* current terminal UL state (off) */
804:
805: while (--n >= 0)
806: if (!ul_opt)
807: putchar (*s++);
808: else {
809: if (n >= 2 && s[0] == '_' && s[1] == '\b') {
810: n -= 2;
811: s += 2;
812: c = *s++;
813: state = 1;
814: } else if (n >= 2 && s[1] == '\b' && s[2] == '_') {
815: n -= 2;
816: c = *s++;
817: s += 2;
818: state = 1;
819: } else {
820: c = *s++;
821: state = 0;
822: }
823: if (state != pstate)
824: tputs(state ? ULenter : ULexit, 1, putch);
825: pstate = state;
826: putchar(c);
827: if (state && *chUL) {
828: pr(chBS);
829: tputs(chUL, 1, putch);
830: }
831: }
832: }
833:
834: /*
835: ** Clear the screen
836: */
837:
838: doclear()
839: {
840: if (Clear && !hard) {
841: tputs(Clear, 1, putch);
842:
843: /* Put out carriage return so that system doesn't
844: ** get confused by escape sequences when expanding tabs
845: */
846: putchar ('\r');
847: promptlen = 0;
848: }
849: }
850:
851: /*
852: * Go to home position
853: */
854: home()
855: {
856: tputs(Home,1,putch);
857: }
858:
859: static int lastcmd, lastarg, lastp;
860: static int lastcolon;
861: char shell_line[132];
862:
863: /*
864: ** Read a command and do it. A command consists of an optional integer
865: ** argument followed by the command character. Return the number of lines
866: ** to display in the next screenful. If there is nothing more to display
867: ** in the current file, zero is returned.
868: */
869:
870: command (filename, f)
871: char *filename;
872: register FILE *f;
873: {
874: register int nlines;
875: register int retval;
876: register char c;
877: char colonch;
878: FILE *helpf;
879: int done;
880: char comchar, cmdbuf[80], *p;
881:
882: #define ret(val) retval=val;done++;break
883:
884: done = 0;
885: if (!errors)
886: prompt (filename);
887: else
888: errors = 0;
889: if (MBIT == RAW && slow_tty) {
890: otty.sg_flags |= MBIT;
891: stty(2, &otty);
892: }
893: for (;;) {
894: nlines = number (&comchar);
895: lastp = colonch = 0;
896: if (comchar == '.') { /* Repeat last command */
897: lastp++;
898: comchar = lastcmd;
899: nlines = lastarg;
900: if (lastcmd == ':')
901: colonch = lastcolon;
902: }
903: lastcmd = comchar;
904: lastarg = nlines;
905: if (comchar == otty.sg_erase) {
906: kill_line ();
907: prompt (filename);
908: continue;
909: }
910: switch (comchar) {
911: case ':':
912: retval = colon (filename, colonch, nlines);
913: if (retval >= 0)
914: done++;
915: break;
916: case ' ':
917: case 'z':
918: if (nlines == 0) nlines = dlines;
919: else if (comchar == 'z') dlines = nlines;
920: ret (nlines);
921: case 'd':
922: case ctrl(D):
923: if (nlines != 0) nscroll = nlines;
924: ret (nscroll);
925: case RUBOUT:
926: case 'q':
927: case 'Q':
928: end_it ();
929: case 's':
930: case 'f':
931: if (nlines == 0) nlines++;
932: if (comchar == 'f')
933: nlines *= dlines;
934: putchar ('\r');
935: erase (0);
936: printf ("\n");
937: if (clreol)
938: cleareol ();
939: printf ("...skipping %d line", nlines);
940: if (nlines > 1)
941: pr ("s\n");
942: else
943: pr ("\n");
944:
945: if (clreol)
946: cleareol ();
947: pr ("\n");
948:
949: while (nlines > 0) {
950: while ((c = Getc (f)) != '\n')
951: if (c == EOF) {
952: retval = 0;
953: done++;
954: goto endsw;
955: }
956: Currline++;
957: nlines--;
958: }
959: ret (dlines);
960: case '\n':
961: if (nlines != 0)
962: dlines = nlines;
963: else
964: nlines = 1;
965: ret (nlines);
966: case '\f':
967: if (!no_intty) {
968: doclear ();
969: Fseek (f, screen_start.chrctr);
970: Currline = screen_start.line;
971: ret (dlines);
972: }
973: else {
974: write (2, &bell, 1);
975: break;
976: }
977: case '\'':
978: if (!no_intty) {
979: kill_line ();
980: pr ("\n***Back***\n\n");
981: Fseek (f, context.chrctr);
982: Currline = context.line;
983: ret (dlines);
984: }
985: else {
986: write (2, &bell, 1);
987: break;
988: }
989: case '=':
990: kill_line ();
991: promptlen = printd (Currline);
992: fflush (stdout);
993: break;
994: case 'n':
995: lastp++;
996: case '/':
997: if (nlines == 0) nlines++;
998: kill_line ();
999: pr ("/");
1000: promptlen = 1;
1001: fflush (stdout);
1002: if (lastp) {
1003: write (2,"\r", 1);
1004: search (NULL, f, nlines); /* Use previous r.e. */
1005: }
1006: else {
1007: ttyin (cmdbuf, 78, '/');
1008: write (2, "\r", 1);
1009: search (cmdbuf, f, nlines);
1010: }
1011: ret (dlines-1);
1012: case '!':
1013: do_shell (filename);
1014: break;
1015: case 'h':
1016: if ((helpf = fopen (HELPFILE, "r")) == NULL)
1017: error ("Can't open help file");
1018: if (noscroll) doclear ();
1019: copy_file (helpf);
1.1.1.2 ! root 1020: fclose (helpf);
1.1 root 1021: prompt (filename);
1022: break;
1023: case 'v': /* This case should go right before default */
1024: if (!no_intty) {
1025: kill_line ();
1026: cmdbuf[0] = '+';
1027: scanstr (Currline, &cmdbuf[1]);
1028: pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
1029: execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
1030: break;
1031: }
1032: default:
1033: write (2, &bell, 1);
1034: break;
1035: }
1036: if (done) break;
1037: }
1038: putchar ('\r');
1039: endsw:
1040: inwait = 0;
1041: notell++;
1042: if (MBIT == RAW && slow_tty) {
1043: otty.sg_flags &= ~MBIT;
1044: stty(2, &otty);
1045: }
1046: return (retval);
1047: }
1048:
1049: char ch;
1050:
1051: /*
1052: * Execute a colon-prefixed command.
1053: * Returns <0 if not a command that should cause
1054: * more of the file to be printed.
1055: */
1056:
1057: colon (filename, cmd, nlines)
1058: char *filename;
1059: int cmd;
1060: int nlines;
1061: {
1062: if (cmd == 0)
1063: ch = readch ();
1064: else
1065: ch = cmd;
1066: lastcolon = ch;
1067: switch (ch) {
1068: case 'f':
1069: kill_line ();
1070: if (!no_intty)
1071: promptlen = printf ("\"%s\" line %d", fnames[fnum], Currline);
1072: else
1073: promptlen = printf ("[Not a file] line %d", Currline);
1074: fflush (stdout);
1075: return (-1);
1076: case 'n':
1077: if (nlines == 0) {
1078: if (fnum >= nfiles - 1)
1079: end_it ();
1080: nlines++;
1081: }
1082: putchar ('\r');
1083: erase (0);
1084: skipf (nlines);
1085: return (0);
1086: case 'p':
1087: if (no_intty) {
1088: write (2, &bell, 1);
1089: return (-1);
1090: }
1091: putchar ('\r');
1092: erase (0);
1093: if (nlines == 0)
1094: nlines++;
1095: skipf (-nlines);
1096: return (0);
1097: case '!':
1098: do_shell (filename);
1099: return (-1);
1100: case 'q':
1101: case 'Q':
1102: end_it ();
1103: default:
1104: write (2, &bell, 1);
1105: return (-1);
1106: }
1107: }
1108:
1109: /*
1110: ** Read a decimal number from the terminal. Set cmd to the non-digit which
1111: ** terminates the number.
1112: */
1113:
1114: number(cmd)
1115: char *cmd;
1116: {
1117: register int i;
1118:
1119: i = 0; ch = otty.sg_kill;
1120: for (;;) {
1121: ch = readch ();
1122: if (ch >= '0' && ch <= '9')
1123: i = i*10 + ch - '0';
1124: else if (ch == otty.sg_kill)
1125: i = 0;
1126: else {
1127: *cmd = ch;
1128: break;
1129: }
1130: }
1131: return (i);
1132: }
1133:
1134: do_shell (filename)
1135: char *filename;
1136: {
1137: char cmdbuf[80];
1138:
1139: kill_line ();
1140: pr ("!");
1141: fflush (stdout);
1142: promptlen = 1;
1143: if (lastp)
1144: pr (shell_line);
1145: else {
1146: ttyin (cmdbuf, 78, '!');
1147: if (expand (shell_line, cmdbuf)) {
1148: kill_line ();
1149: promptlen = printf ("!%s", shell_line);
1150: }
1151: }
1152: fflush (stdout);
1153: write (2, "\n", 1);
1154: promptlen = 0;
1155: shellp = 1;
1156: execute (filename, shell, shell, "-c", shell_line, 0);
1157: }
1158:
1159: /*
1160: ** Search for nth ocurrence of regular expression contained in buf in the file
1161: */
1162:
1163: search (buf, file, n)
1164: char buf[];
1165: FILE *file;
1166: register int n;
1167: {
1168: long startline = Ftell (file);
1169: register long line1 = startline;
1170: register long line2 = startline;
1171: register long line3 = startline;
1172: register int lncount;
1173: int saveln, rv, re_exec();
1174: char *s, *re_comp();
1175:
1176: context.line = saveln = Currline;
1177: context.chrctr = startline;
1178: lncount = 0;
1179: if ((s = re_comp (buf)) != 0)
1180: error (s);
1181: while (!feof (file)) {
1182: line3 = line2;
1183: line2 = line1;
1184: line1 = Ftell (file);
1185: rdline (file);
1186: lncount++;
1187: if ((rv = re_exec (Line)) == 1)
1188: if (--n == 0) {
1189: if (lncount > 3 || (lncount > 1 && no_intty))
1190: {
1191: pr ("\n");
1192: if (clreol)
1193: cleareol ();
1194: pr("...skipping\n");
1195: }
1196: if (!no_intty) {
1197: Currline -= (lncount >= 3 ? 3 : lncount);
1198: Fseek (file, line3);
1199: if (noscroll)
1200: if (clreol) {
1201: home ();
1202: cleareol ();
1203: }
1204: else
1205: doclear ();
1206: }
1207: else {
1208: kill_line ();
1209: if (noscroll)
1210: if (clreol) {
1211: home ();
1212: cleareol ();
1213: }
1214: else
1215: doclear ();
1216: pr (Line);
1217: putchar ('\n');
1218: }
1219: break;
1220: }
1221: else if (rv == -1)
1222: error ("Regular expression botch");
1223: }
1224: if (feof (file)) {
1225: if (!no_intty) {
1226: file->_flag &= ~_IOEOF; /* why doesn't fseek do this ??!!??! */
1227: Currline = saveln;
1228: Fseek (file, startline);
1229: }
1230: else {
1231: pr ("\nPattern not found\n");
1232: end_it ();
1233: }
1234: error ("Pattern not found");
1235: }
1236: }
1237:
1238: execute (filename, cmd, args)
1239: char *filename;
1240: char *cmd, *args;
1241: {
1242: int id;
1243:
1244: fflush (stdout);
1245: reset_tty ();
1246: while ((id = fork ()) < 0)
1247: sleep (5);
1248: if (id == 0) {
1249: execv (cmd, &args);
1250: write (2, "exec failed\n", 12);
1251: exit (1);
1252: }
1253: signal (SIGINT, SIG_IGN);
1254: signal (SIGQUIT, SIG_IGN);
1255: if (catch_susp)
1256: signal(SIGTSTP, SIG_DFL);
1257: wait (0);
1258: signal (SIGINT, end_it);
1259: signal (SIGQUIT, onquit);
1260: if (catch_susp)
1261: signal(SIGTSTP, onsusp);
1262: set_tty ();
1263: pr ("------------------------\n");
1264: prompt (filename);
1265: }
1266: /*
1267: ** Skip n lines in the file f
1268: */
1269:
1270: skiplns (n, f)
1271: register int n;
1272: register FILE *f;
1273: {
1274: register char c;
1275:
1276: while (n > 0) {
1277: while ((c = Getc (f)) != '\n')
1278: if (c == EOF)
1279: return;
1280: n--;
1281: Currline++;
1282: }
1283: }
1284:
1285: /*
1286: ** Skip nskip files in the file list (from the command line). Nskip may be
1287: ** negative.
1288: */
1289:
1290: skipf (nskip)
1291: register int nskip;
1292: {
1293: if (nskip == 0) return;
1294: if (nskip > 0) {
1295: if (fnum + nskip > nfiles - 1)
1296: nskip = nfiles - fnum - 1;
1297: }
1298: else if (within)
1299: ++fnum;
1300: fnum += nskip;
1301: if (fnum < 0)
1302: fnum = 0;
1303: pr ("\n...Skipping ");
1304: pr ("\n");
1305: if (clreol)
1306: cleareol ();
1307: pr ("...Skipping ");
1308: pr (nskip > 0 ? "to file " : "back to file ");
1309: pr (fnames[fnum]);
1310: pr ("\n");
1311: if (clreol)
1312: cleareol ();
1313: pr ("\n");
1314: --fnum;
1315: }
1316:
1317: /*----------------------------- Terminal I/O -------------------------------*/
1318:
1319: initterm ()
1320: {
1321: char buf[TBUFSIZ];
1322: char clearbuf[100];
1323: char *clearptr, *padstr;
1324: int ldisc;
1325: char *term;
1326:
1327: setbuf(stdout, obuf);
1328: if (!(no_tty = gtty(1, &otty))) {
1329: if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) {
1330: dumb++; ul_opt = 0;
1331: }
1332: else {
1333: if (((Lpp = tgetnum("li")) < 0) || tgetflag("hc")) {
1334: hard++; /* Hard copy terminal */
1335: Lpp = 24;
1336: }
1337: if (tailequ (fnames[0], "page") || !hard && tgetflag("ns"))
1338: noscroll++;
1339: if ((Mcol = tgetnum("co")) < 0)
1340: Mcol = 80;
1341: Wrap = tgetflag("am");
1342: bad_so = tgetflag ("xs");
1343: clearptr = clearbuf;
1344: eraseln = tgetstr("ce",&clearptr);
1345: Clear = tgetstr("cl", &clearptr);
1346: Senter = tgetstr("so", &clearptr);
1347: Sexit = tgetstr("se", &clearptr);
1348:
1349: /*
1350: * Set up for underlining: some terminals don't need it;
1351: * others have start/stop sequences, still others have an
1352: * underline char sequence which is assumed to move the
1353: * cursor forward one character. If underline sequence
1354: * isn't available, settle for standout sequence.
1355: */
1356:
1357: if (tgetflag("ul") || tgetflag("os"))
1358: ul_opt = 0;
1359: if ((chUL = tgetstr("uc", &clearptr)) == NULL )
1360: chUL = "";
1361: if ((ULenter = tgetstr("us", &clearptr)) == NULL &&
1362: (!*chUL) && (ULenter = tgetstr("so", &clearptr)) == NULL)
1363: ULenter = "";
1364: if ((ULexit = tgetstr("ue", &clearptr)) == NULL &&
1365: (!*chUL) && (ULexit = tgetstr("se", &clearptr)) == NULL)
1366: ULexit = "";
1367:
1368: if (padstr = tgetstr("pc", &clearptr))
1369: PC = *padstr;
1370: Home = tgetstr("ho",&clearptr);
1371: if (Home == 0 || *Home == '\0')
1372: {
1373: if ((cursorm = tgetstr("cm", &clearptr)) != NULL) {
1374: strcpy(cursorhome, tgoto(cursorm, 0, 0));
1375: Home = cursorhome;
1376: }
1377: }
1378: EodClr = tgetstr("cd", &clearptr);
1379: }
1380: if ((shell = getenv("SHELL")) == NULL)
1381: shell = "/bin/sh";
1382: }
1383: no_intty = gtty(0, &otty);
1384: gtty(2, &otty);
1385: savetty = otty;
1386: ospeed = otty.sg_ospeed;
1387: slow_tty = ospeed < B1200;
1388: hardtabs = !(otty.sg_flags & XTABS);
1389: if (!no_tty) {
1390: otty.sg_flags &= ~ECHO;
1391: if (MBIT == CBREAK || !slow_tty)
1392: otty.sg_flags |= MBIT;
1393: }
1394: }
1395:
1396: readch ()
1397: {
1398: char ch;
1399: extern int errno;
1400:
1401: if (read (2, &ch, 1) <= 0)
1402: if (errno != EINTR)
1403: exit(0);
1404: else
1405: ch = otty.sg_kill;
1406: return (ch);
1407: }
1408:
1409: static char BS = '\b';
1410: static char CARAT = '^';
1411:
1412: ttyin (buf, nmax, pchar)
1413: char buf[];
1414: register int nmax;
1415: char pchar;
1416: {
1417: register char *sptr;
1418: register char ch;
1419: register int slash = 0;
1420: int maxlen;
1421: char cbuf;
1422:
1423: sptr = buf;
1424: maxlen = 0;
1425: while (sptr - buf < nmax) {
1426: if (promptlen > maxlen) maxlen = promptlen;
1427: ch = readch ();
1428: if (ch == '\\') {
1429: slash++;
1430: }
1431: else if ((ch == otty.sg_erase) && !slash) {
1432: if (sptr > buf) {
1433: --promptlen;
1434: write (2, &BS, 1);
1435: --sptr;
1436: if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
1437: --promptlen;
1438: write (2, &BS, 1);
1439: }
1440: continue;
1441: }
1442: else {
1443: if (!eraseln) promptlen = maxlen;
1444: longjmp (restore, 1);
1445: }
1446: }
1447: else if ((ch == otty.sg_kill) && !slash) {
1448: if (hard) {
1449: show (ch);
1450: putchar ('\n');
1451: putchar (pchar);
1452: }
1453: else {
1454: putchar ('\r');
1455: putchar (pchar);
1456: if (eraseln)
1457: erase (1);
1458: promptlen = 1;
1459: }
1460: sptr = buf;
1461: fflush (stdout);
1462: continue;
1463: }
1464: if (slash && (ch == otty.sg_kill || ch == otty.sg_erase)) {
1465: write (2, &BS, 1);
1466: --sptr;
1467: }
1468: if (ch != '\\')
1469: slash = 0;
1470: *sptr++ = ch;
1471: if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1472: ch += ch == RUBOUT ? -0100 : 0100;
1473: write (2, &CARAT, 1);
1474: promptlen++;
1475: }
1476: cbuf = ch;
1477: if (ch != '\n' && ch != ESC) {
1478: write (2, &cbuf, 1);
1479: promptlen++;
1480: }
1481: else
1482: break;
1483: }
1484: *--sptr = '\0';
1485: if (!eraseln) promptlen = maxlen;
1486: if (sptr - buf >= nmax - 1)
1487: error ("Line too long");
1488: }
1489:
1490: expand (outbuf, inbuf)
1491: char *outbuf;
1492: char *inbuf;
1493: {
1494: register char *instr;
1495: register char *outstr;
1496: register char ch;
1497: char temp[200];
1498: int changed = 0;
1499:
1500: instr = inbuf;
1501: outstr = temp;
1502: while ((ch = *instr++) != '\0')
1503: switch (ch) {
1504: case '%':
1505: if (!no_intty) {
1506: strcpy (outstr, fnames[fnum]);
1507: outstr += strlen (fnames[fnum]);
1508: changed++;
1509: }
1510: else
1511: *outstr++ = ch;
1512: break;
1513: case '!':
1514: if (!shellp)
1515: error ("No previous command to substitute for");
1516: strcpy (outstr, shell_line);
1517: outstr += strlen (shell_line);
1518: changed++;
1519: break;
1520: case '\\':
1521: if (*instr == '%' || *instr == '!') {
1522: *outstr++ = *instr++;
1523: break;
1524: }
1525: default:
1526: *outstr++ = ch;
1527: }
1528: *outstr++ = '\0';
1529: strcpy (outbuf, temp);
1530: return (changed);
1531: }
1532:
1533: show (ch)
1534: register char ch;
1535: {
1536: char cbuf;
1537:
1538: if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1539: ch += ch == RUBOUT ? -0100 : 0100;
1540: write (2, &CARAT, 1);
1541: promptlen++;
1542: }
1543: cbuf = ch;
1544: write (2, &cbuf, 1);
1545: promptlen++;
1546: }
1547:
1548: error (mess)
1549: char *mess;
1550: {
1551: if (clreol)
1552: cleareol ();
1553: else
1554: kill_line ();
1555: promptlen += strlen (mess);
1556: if (Senter && Sexit) {
1557: tputs (Senter, 1, putch);
1558: pr(mess);
1559: tputs (Sexit, 1, putch);
1560: }
1561: else
1562: pr (mess);
1563: fflush(stdout);
1564: errors++;
1565: longjmp (restore, 1);
1566: }
1567:
1568:
1569: set_tty ()
1570: {
1571: otty.sg_flags |= MBIT;
1572: otty.sg_flags &= ~ECHO;
1573: stty(2, &otty);
1574: }
1575:
1576: reset_tty ()
1577: {
1578: otty.sg_flags |= ECHO;
1579: otty.sg_flags &= ~MBIT;
1580: stty(2, &savetty);
1581: }
1582:
1583: rdline (f)
1584: register FILE *f;
1585: {
1586: register char c;
1587: register char *p;
1588:
1589: p = Line;
1590: while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
1591: *p++ = c;
1592: if (c == '\n')
1593: Currline++;
1594: *p = '\0';
1595: }
1596:
1597: /* Come here when we get a suspend signal from the terminal */
1598:
1599: onsusp ()
1600: {
1601: /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
1602: signal(SIGTTOU, SIG_IGN);
1603: reset_tty ();
1604: fflush (stdout);
1605: signal(SIGTTOU, SIG_DFL);
1606: /* Send the TSTP signal to suspend our process group */
1607: signal(SIGTSTP, SIG_DFL);
1608: sigsetmask(0);
1609: kill (0, SIGTSTP);
1610: /* Pause for station break */
1611:
1612: /* We're back */
1613: signal (SIGTSTP, onsusp);
1614: set_tty ();
1615: if (inwait)
1616: longjmp (restore);
1617: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.