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