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