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