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