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