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