]> git.ipfire.org Git - thirdparty/util-linux.git/blob - text-utils/more.c
Imported from util-linux-2.9i tarball.
[thirdparty/util-linux.git] / text-utils / more.c
1 /*
2 * Copyright (C) 1980 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that the above copyright notice and this paragraph are
7 * duplicated in all such forms and that any documentation,
8 * advertising materials, and other materials related to such
9 * distribution and use acknowledge that the software was developed
10 * by the University of California, Berkeley. The name of the
11 * University may not be used to endorse or promote products derived
12 * from this software without specific prior written permission.
13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16 */
17
18 /*
19 ** more.c - General purpose tty output filter and file perusal program
20 **
21 ** by Eric Shienbrood, UC Berkeley
22 **
23 ** modified by Geoff Peck, UCB to add underlining, single spacing
24 ** modified by John Foderaro, UCB to add -c and MORE environment variable
25 ** modified by Erik Troan <ewt@redhat.com> to be more posix and so compile
26 ** on linux/axp.
27 ** modified by Kars de Jong <jongk@cs.utwente.nl> to use terminfo instead
28 ** of termcap.
29 */
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdlib.h> /* for alloca() */
35 #include <sys/param.h>
36 #include <ctype.h>
37 #include <signal.h>
38 #include <errno.h>
39 #include <termios.h>
40 #include <setjmp.h>
41 #include <sys/ioctl.h>
42 #include <sys/stat.h>
43 #include <sys/file.h>
44 #include <sys/wait.h>
45 #include <a.out.h>
46 #if NCH
47 #include <ncurses.h>
48 #else
49 #include <curses.h>
50 #endif
51 #include <term.h>
52 #include <locale.h>
53
54 #define HELPFILE "/usr/lib/more.help"
55 #define VI "/usr/bin/vi"
56
57 #define Fopen(s,m) (Currline = 0,file_pos=0,fopen(s,m))
58 #define Ftell(f) file_pos
59 #define Fseek(f,off) (file_pos=off,fseek(f,off,0))
60 #define Getc(f) (++file_pos, getc(f))
61 #define Ungetc(c,f) (--file_pos, ungetc(c,f))
62
63 #define stty(fd,argp) tcsetattr(fd,TCSANOW,argp)
64
65 /* some function declarations */
66 void initterm(void);
67 void kill_line(void);
68 void doclear(void);
69 void cleareol(void);
70 void clreos(void);
71 void home(void);
72 void error (char *mess);
73 void do_shell (char *filename);
74 int colon (char *filename, int cmd, int nlines);
75 int expand (char *outbuf, char *inbuf);
76 void argscan(char *s);
77 void rdline (register FILE *f);
78 void copy_file(register FILE *f);
79 void search(char buf[], FILE *file, register int n);
80 void skipf (register int nskip);
81 void skiplns(register int n, register FILE *f);
82 void screen (register FILE *f, register int num_lines);
83 int command (char *filename, register FILE *f);
84 void erasep (register int col);
85 void show (register char ch);
86 int pr(char *s1);
87 int printd (int n);
88 void set_tty(void);
89 void reset_tty(void);
90 void ttyin (char buf[], register int nmax, char pchar);
91 int number(char *cmd);
92 int readch (void);
93 int get_line(register FILE *f, int *length);
94 void prbuf (register char *s, register int n);
95 int xprintf (char *fmt, ...);
96 void execute (char *filename, char *cmd, ...);
97 void errwrite (char *txt);
98 void errwrite1 (char *sym);
99
100 #define TBUFSIZ 1024
101 #define LINSIZ 256
102 #define ctrl(letter) (letter & 077)
103 #define RUBOUT '\177'
104 #define ESC '\033'
105 #define QUIT '\034'
106
107 struct termios otty, savetty0;
108 long file_pos, file_size;
109 int fnum, no_intty, no_tty, slow_tty;
110 int dum_opt, dlines;
111 void onquit(), onsusp(), chgwinsz(), end_it();
112 int nscroll = 11; /* Number of lines scrolled by 'd' */
113 int fold_opt = 1; /* Fold long lines */
114 int stop_opt = 1; /* Stop after form feeds */
115 int ssp_opt = 0; /* Suppress white space */
116 int ul_opt = 1; /* Underline as best we can */
117 int promptlen;
118 int Currline; /* Line we are currently at */
119 int startup = 1;
120 int firstf = 1;
121 int notell = 1;
122 int docrterase = 0;
123 int docrtkill = 0;
124 int bad_so; /* True if overwriting does not turn off standout */
125 int inwait, Pause, errors;
126 int within; /* true if we are within a file,
127 false if we are between files */
128 int hard, dumb, noscroll, hardtabs, clreol, eatnl;
129 int catch_susp; /* We should catch the SIGTSTP signal */
130 char **fnames; /* The list of file names */
131 int nfiles; /* Number of files left to process */
132 char *shell; /* The name of the shell to use */
133 int shellp; /* A previous shell command exists */
134 char ch;
135 jmp_buf restore;
136 char Line[LINSIZ]; /* Line buffer */
137 int Lpp = 24; /* lines per page */
138 char *Clear; /* clear screen */
139 char *eraseln; /* erase line */
140 char *Senter, *Sexit;/* enter and exit standout mode */
141 char *ULenter, *ULexit; /* enter and exit underline mode */
142 char *chUL; /* underline character */
143 char *chBS; /* backspace character */
144 char *Home; /* go to home */
145 char *cursorm; /* cursor movement */
146 char cursorhome[40]; /* contains cursor movement to home */
147 char *EodClr; /* clear rest of screen */
148 char *tgetstr();
149 int Mcol = 80; /* number of columns */
150 int Wrap = 1; /* set if automargins */
151 int soglitch; /* terminal has standout mode glitch */
152 int ulglitch; /* terminal has underline mode glitch */
153 int pstate = 0; /* current UL state */
154 char *getenv();
155 static int magic();
156 struct {
157 long chrctr, line;
158 } context, screen_start;
159 /* extern */ char PC; /* pad character */
160
161 void
162 idummy(int *kk) {}
163
164 void
165 Fdummy(FILE **ff) {}
166
167 int main(int argc, char **argv) {
168 FILE *f;
169 char *s;
170 char *p;
171 int ch;
172 int left;
173 int prnames = 0;
174 int initopt = 0;
175 int srchopt = 0;
176 int clearit = 0;
177 int initline = 0;
178 char initbuf[80];
179 FILE *checkf();
180
181 /* avoid gcc complaints about register variables that
182 may be clobbered by a longjmp, by forcing our variables here
183 to be non-register */
184 Fdummy(&f); idummy(&left); idummy(&prnames);
185 idummy(&initopt); idummy(&srchopt); idummy(&initline);
186
187 nfiles = argc;
188 fnames = argv;
189 setlocale(LC_ALL, "");
190 initterm ();
191 nscroll = Lpp/2 - 1;
192 if (nscroll <= 0)
193 nscroll = 1;
194 if((s = getenv("MORE")) != NULL) argscan(s);
195 while (--nfiles > 0) {
196 if ((ch = (*++fnames)[0]) == '-') {
197 argscan(*fnames+1);
198 }
199 else if (ch == '+') {
200 s = *fnames;
201 if (*++s == '/') {
202 srchopt++;
203 for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
204 *p++ = *s++;
205 *p = '\0';
206 }
207 else {
208 initopt++;
209 for (initline = 0; *s != '\0'; s++)
210 if (isdigit (*s))
211 initline = initline*10 + *s -'0';
212 --initline;
213 }
214 }
215 else break;
216 }
217 /* allow clreol only if Home and eraseln and EodClr strings are
218 * defined, and in that case, make sure we are in noscroll mode
219 */
220 if(clreol)
221 {
222 if((Home == NULL) || (*Home == '\0') ||
223 (eraseln == NULL) || (*eraseln == '\0') ||
224 (EodClr == NULL) || (*EodClr == '\0') )
225 clreol = 0;
226 else noscroll = 1;
227 }
228 if (dlines == 0)
229 dlines = Lpp - (noscroll ? 1 : 2);
230 left = dlines;
231 if (nfiles > 1)
232 prnames++;
233 if (!no_intty && nfiles == 0) {
234 char *rindex();
235
236 p = rindex(argv[0], '/');
237 fputs("usage: ",stderr);
238 fputs(p ? p + 1 : argv[0],stderr);
239 fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr);
240 exit(1);
241 }
242 else
243 f = stdin;
244 if (!no_tty) {
245 signal(SIGQUIT, onquit);
246 signal(SIGINT, end_it);
247 #ifdef SIGWINCH
248 signal(SIGWINCH, chgwinsz);
249 #endif
250 if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
251 signal(SIGTSTP, onsusp);
252 catch_susp++;
253 }
254 stty (fileno(stderr), &otty);
255 }
256 if (no_intty) {
257 if (no_tty)
258 copy_file (stdin);
259 else {
260 if ((ch = Getc (f)) == '\f')
261 doclear();
262 else {
263 Ungetc (ch, f);
264 if (noscroll && (ch != EOF)) {
265 if (clreol)
266 home ();
267 else
268 doclear ();
269 }
270 }
271 if (srchopt)
272 {
273 search (initbuf, stdin, 1);
274 if (noscroll)
275 left--;
276 }
277 else if (initopt)
278 skiplns (initline, stdin);
279 screen (stdin, left);
280 }
281 no_intty = 0;
282 prnames++;
283 firstf = 0;
284 }
285
286 while (fnum < nfiles) {
287 if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
288 context.line = context.chrctr = 0;
289 Currline = 0;
290 if (firstf) setjmp (restore);
291 if (firstf) {
292 firstf = 0;
293 if (srchopt) {
294 search (initbuf, f, 1);
295 if (noscroll)
296 left--;
297 }
298 else if (initopt)
299 skiplns (initline, f);
300 }
301 else if (fnum < nfiles && !no_tty) {
302 setjmp (restore);
303 left = command (fnames[fnum], f);
304 }
305 if (left != 0) {
306 if ((noscroll || clearit) && (file_size != LONG_MAX)) {
307 if (clreol)
308 home ();
309 else
310 doclear ();
311 }
312 if (prnames) {
313 if (bad_so)
314 erasep (0);
315 if (clreol)
316 cleareol ();
317 pr("::::::::::::::");
318 if (promptlen > 14)
319 erasep (14);
320 xprintf ("\n");
321 if(clreol) cleareol();
322 xprintf("%s\n", fnames[fnum]);
323 if(clreol) cleareol();
324 xprintf("::::::::::::::\n");
325 if (left > Lpp - 4)
326 left = Lpp - 4;
327 }
328 if (no_tty)
329 copy_file (f);
330 else {
331 within++;
332 screen(f, left);
333 within = 0;
334 }
335 }
336 setjmp (restore);
337 fflush(stdout);
338 fclose(f);
339 screen_start.line = screen_start.chrctr = 0L;
340 context.line = context.chrctr = 0L;
341 }
342 fnum++;
343 firstf = 0;
344 }
345 reset_tty ();
346 exit(0);
347 }
348
349 void argscan(char *s) {
350 int seen_num = 0;
351
352 while (*s != '\0') {
353 switch (*s) {
354 case '0': case '1': case '2':
355 case '3': case '4': case '5':
356 case '6': case '7': case '8':
357 case '9':
358 if (!seen_num) {
359 dlines = 0;
360 seen_num = 1;
361 }
362 dlines = dlines*10 + *s - '0';
363 break;
364 case 'd':
365 dum_opt = 1;
366 break;
367 case 'l':
368 stop_opt = 0;
369 break;
370 case 'f':
371 fold_opt = 0;
372 break;
373 case 'p':
374 noscroll++;
375 break;
376 case 'c':
377 clreol++;
378 break;
379 case 's':
380 ssp_opt = 1;
381 break;
382 case 'u':
383 ul_opt = 0;
384 break;
385 }
386 s++;
387 }
388 }
389
390
391 /*
392 ** Check whether the file named by fs is an ASCII file which the user may
393 ** access. If it is, return the opened file. Otherwise return NULL.
394 */
395
396 FILE *
397 checkf (fs, clearfirst)
398 register char *fs;
399 int *clearfirst;
400 {
401 struct stat stbuf;
402 register FILE *f;
403 char c;
404
405 if (stat (fs, &stbuf) == -1) {
406 (void)fflush(stdout);
407 if (clreol)
408 cleareol ();
409 perror(fs);
410 return((FILE *)NULL);
411 }
412 if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
413 xprintf("\n*** %s: directory ***\n\n", fs);
414 return((FILE *)NULL);
415 }
416 if ((f = Fopen(fs, "r")) == NULL) {
417 (void)fflush(stdout);
418 perror(fs);
419 return((FILE *)NULL);
420 }
421 if (magic(f, fs))
422 return((FILE *)NULL);
423 c = Getc(f);
424 *clearfirst = c == '\f';
425 Ungetc (c, f);
426 if ((file_size = stbuf.st_size) == 0)
427 file_size = LONG_MAX;
428 return(f);
429 }
430
431 /*
432 * magic --
433 * check for file magic numbers. This code would best be shared with
434 * the file(1) program or, perhaps, more should not try and be so smart?
435 */
436 static int
437 magic(f, fs)
438 FILE *f;
439 char *fs;
440 {
441 char twobytes[2];
442
443 if (fread(twobytes, 2, 1, f) == 1) {
444 switch(twobytes[0] + (twobytes[1]<<8)) {
445 case OMAGIC: /* 0407 */
446 case NMAGIC: /* 0410 */
447 case ZMAGIC: /* 0413 */
448 case 0405:
449 case 0411:
450 case 0177545:
451 case 0x457f: /* simple ELF detection */
452 xprintf("\n******** %s: Not a text file ********\n\n", fs);
453 (void)fclose(f);
454 return(1);
455 }
456 }
457 (void)fseek(f, 0L, SEEK_SET); /* rewind() not necessary */
458 return(0);
459 }
460
461 /*
462 ** Print out the contents of the file f, one screenful at a time.
463 */
464
465 #define STOP -10
466
467 void screen (register FILE *f, register int num_lines)
468 {
469 register int c;
470 register int nchars;
471 int length; /* length of current line */
472 static int prev_len = 1; /* length of previous line */
473
474 for (;;) {
475 while (num_lines > 0 && !Pause) {
476 if ((nchars = get_line (f, &length)) == EOF)
477 {
478 if (clreol)
479 clreos();
480 return;
481 }
482 if (ssp_opt && length == 0 && prev_len == 0)
483 continue;
484 prev_len = length;
485 if (bad_so || ((Senter && *Senter == ' ') && (promptlen > 0)))
486 erasep (0);
487 /* must clear before drawing line since tabs on some terminals
488 * do not erase what they tab over.
489 */
490 if (clreol)
491 cleareol ();
492 prbuf (Line, length);
493 if (nchars < promptlen)
494 erasep (nchars); /* erasep () sets promptlen to 0 */
495 else promptlen = 0;
496 /* is this needed?
497 * if (clreol)
498 * cleareol(); * must clear again in case we wrapped *
499 */
500 if (nchars < Mcol || !fold_opt)
501 prbuf("\n", 1); /* will turn off UL if necessary */
502 if (nchars == STOP)
503 break;
504 num_lines--;
505 }
506 if (pstate) {
507 putp (ULexit);
508 pstate = 0;
509 }
510 fflush(stdout);
511 if ((c = Getc(f)) == EOF)
512 {
513 if (clreol)
514 clreos ();
515 return;
516 }
517
518 if (Pause && clreol)
519 clreos ();
520 Ungetc (c, f);
521 setjmp (restore);
522 Pause = 0; startup = 0;
523 if ((num_lines = command (NULL, f)) == 0)
524 return;
525 if (hard && promptlen > 0)
526 erasep (0);
527 if (noscroll && num_lines >= dlines)
528 {
529 if (clreol)
530 home();
531 else
532 doclear ();
533 }
534 screen_start.line = Currline;
535 screen_start.chrctr = Ftell (f);
536 }
537 }
538
539 /*
540 ** Come here if a quit signal is received
541 */
542
543 void onquit()
544 {
545 signal(SIGQUIT, SIG_IGN);
546 if (!inwait) {
547 putchar ('\n');
548 if (!startup) {
549 signal(SIGQUIT, onquit);
550 longjmp (restore, 1);
551 }
552 else
553 Pause++;
554 }
555 else if (!dum_opt && notell) {
556 errwrite("[Use q or Q to quit]");
557 promptlen += 20;
558 notell = 0;
559 }
560 signal(SIGQUIT, onquit);
561 }
562
563 /*
564 ** Come here if a signal for a window size change is received
565 */
566
567 #ifdef SIGWINCH
568 void chgwinsz()
569 {
570 struct winsize win;
571
572 (void) signal(SIGWINCH, SIG_IGN);
573 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
574 if (win.ws_row != 0) {
575 Lpp = win.ws_row;
576 nscroll = Lpp/2 - 1;
577 if (nscroll <= 0)
578 nscroll = 1;
579 dlines = Lpp - (noscroll ? 1 : 2);
580 }
581 if (win.ws_col != 0)
582 Mcol = win.ws_col;
583 }
584 (void) signal(SIGWINCH, chgwinsz);
585 }
586 #endif
587
588 /*
589 ** Clean up terminal state and exit. Also come here if interrupt signal received
590 */
591
592 void end_it ()
593 {
594
595 reset_tty ();
596 if (clreol) {
597 putchar ('\r');
598 clreos ();
599 fflush (stdout);
600 }
601 else if (!clreol && (promptlen > 0)) {
602 kill_line ();
603 fflush (stdout);
604 }
605 else
606 errwrite("\n");
607 _exit(0);
608 }
609
610 void copy_file(register FILE *f) {
611 register int c;
612
613 while ((c = getc(f)) != EOF)
614 putchar(c);
615 }
616
617 /* Simplified printf function */
618
619 int xprintf (char *fmt, ...)
620 {
621 va_list ap;
622 register char ch;
623 register int ccount;
624
625 ccount = 0;
626 va_start(ap, fmt);
627 while (*fmt) {
628 while ((ch = *fmt++) != '%') {
629 if (ch == '\0')
630 return (ccount);
631 ccount++;
632 putchar (ch);
633 }
634 switch (*fmt++) {
635 case 'd':
636 ccount += printd (va_arg(ap, int));
637 break;
638 case 's':
639 ccount += pr (va_arg(ap, char *));
640 break;
641 case '%':
642 ccount++;
643 putchar ('%');
644 break;
645 case '0':
646 return (ccount);
647 default:
648 break;
649 }
650 }
651 va_end(ap);
652 return (ccount);
653
654 }
655
656 /*
657 ** Print an integer as a string of decimal digits,
658 ** returning the length of the print representation.
659 */
660
661 int printd (int n)
662 {
663 int a, nchars;
664
665 if ((a = n/10) != 0)
666 nchars = 1 + printd(a);
667 else
668 nchars = 1;
669 putchar (n % 10 + '0');
670 return (nchars);
671 }
672
673 /* Put the print representation of an integer into a string */
674 static char *sptr;
675
676 void Sprintf (int n) {
677 int a;
678
679 if ((a = n/10) != 0)
680 Sprintf (a);
681 *sptr++ = n % 10 + '0';
682 }
683
684 void scanstr (int n, char *str)
685 {
686 sptr = str;
687 Sprintf (n);
688 *sptr = '\0';
689 }
690
691 #define ringbell() errwrite("\007");
692
693 #ifdef undef
694 strlen (s)
695 char *s;
696 {
697 register char *p;
698
699 p = s;
700 while (*p++)
701 ;
702 return (p - s - 1);
703 }
704 #endif
705
706 /* See whether the last component of the path name "path" is equal to the
707 ** string "string"
708 */
709
710 int tailequ (path, string)
711 char *path;
712 register char *string;
713 {
714 register char *tail;
715
716 tail = path + strlen(path);
717 while (tail >= path)
718 if (*(--tail) == '/')
719 break;
720 ++tail;
721 while (*tail++ == *string++)
722 if (*tail == '\0')
723 return(1);
724 return(0);
725 }
726
727 void prompt (filename)
728 char *filename;
729 {
730 if (clreol)
731 cleareol ();
732 else if (promptlen > 0)
733 kill_line ();
734 if (!hard) {
735 promptlen = 8;
736 if (Senter && Sexit) {
737 putp (Senter);
738 promptlen += (2 * soglitch);
739 }
740 if (clreol)
741 cleareol ();
742 pr("--More--");
743 if (filename != NULL) {
744 promptlen += xprintf ("(Next file: %s)", filename);
745 }
746 else if (!no_intty) {
747 promptlen += xprintf ("(%d%%)", (int)((file_pos * 100) / file_size));
748 }
749 if (dum_opt) {
750 promptlen += pr("[Press space to continue, 'q' to quit.]");
751 }
752 if (Senter && Sexit)
753 putp (Sexit);
754 if (clreol)
755 clreos ();
756 fflush(stdout);
757 }
758 else
759 ringbell();
760 inwait++;
761 }
762
763 /*
764 ** Get a logical line
765 */
766
767 int get_line(register FILE *f, int *length)
768 {
769 register int c;
770 register char *p;
771 register int column;
772 static int colflg;
773
774 p = Line;
775 column = 0;
776 c = Getc (f);
777 if (colflg && c == '\n') {
778 Currline++;
779 c = Getc (f);
780 }
781 while (p < &Line[LINSIZ - 1]) {
782 if (c == EOF) {
783 if (p > Line) {
784 *p = '\0';
785 *length = p - Line;
786 return (column);
787 }
788 *length = p - Line;
789 return (EOF);
790 }
791 if (c == '\n') {
792 Currline++;
793 break;
794 }
795 *p++ = c;
796 if (c == '\t') {
797 if (!hardtabs || (column < promptlen && !hard)) {
798 if (hardtabs && eraseln && !dumb) {
799 column = 1 + (column | 7);
800 putp (eraseln);
801 promptlen = 0;
802 }
803 else {
804 for (--p; p < &Line[LINSIZ - 1];) {
805 *p++ = ' ';
806 if ((++column & 7) == 0)
807 break;
808 }
809 if (column >= promptlen) promptlen = 0;
810 }
811 }
812 else
813 column = 1 + (column | 7);
814 } else if (c == '\b' && column > 0) {
815 column--;
816 } else if (c == '\r') {
817 int next = Getc(f);
818 if (next == '\n') {
819 p--;
820 Currline++;
821 break;
822 }
823 Ungetc(next,f);
824 column = 0;
825 } else if (c == '\f' && stop_opt) {
826 p[-1] = '^';
827 *p++ = 'L';
828 column += 2;
829 Pause++;
830 } else if (c == EOF) {
831 *length = p - Line;
832 return (column);
833 }
834 else if (isprint(c))
835 column++;
836 if (column >= Mcol && fold_opt) break;
837 c = Getc (f);
838 }
839 if (column >= Mcol && Mcol > 0) {
840 if (!Wrap) {
841 *p++ = '\n';
842 }
843 }
844 colflg = column == Mcol && fold_opt;
845 if (colflg && eatnl && Wrap) {
846 *p++ = '\n'; /* simulate normal wrap */
847 }
848 *length = p - Line;
849 *p = 0;
850 return (column);
851 }
852
853 /*
854 ** Erase the rest of the prompt, assuming we are starting at column col.
855 */
856
857 void erasep (register int col)
858 {
859
860 if (promptlen == 0)
861 return;
862 if (hard) {
863 putchar ('\n');
864 }
865 else {
866 if (col == 0)
867 putchar ('\r');
868 if (!dumb && eraseln)
869 putp (eraseln);
870 else
871 for (col = promptlen - col; col > 0; col--)
872 putchar (' ');
873 }
874 promptlen = 0;
875 }
876
877 /*
878 ** Erase the current line entirely
879 */
880
881 void kill_line ()
882 {
883 erasep (0);
884 if (!eraseln || dumb)
885 putchar ('\r');
886 }
887
888 /*
889 * force clear to end of line
890 */
891 void cleareol()
892 {
893 putp(eraseln);
894 }
895
896 void clreos()
897 {
898 putp(EodClr);
899 }
900
901 /*
902 ** Print string and return number of characters
903 */
904
905 int pr(char *s1)
906 {
907 register char *s;
908 register char c;
909
910 for (s = s1; (c = *s++) != 0; )
911 putchar(c);
912 return (int) (s - s1 - 1);
913 }
914
915
916 /* Print a buffer of n characters */
917
918 void prbuf (register char *s, register int n)
919 {
920 register char c; /* next output character */
921 register int state; /* next output char's UL state */
922 #define wouldul(s,n) ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
923
924 while (--n >= 0)
925 if (!ul_opt)
926 putchar (*s++);
927 else {
928 if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
929 s++;
930 continue;
931 }
932 if ((state = wouldul(s, n)) != 0) {
933 c = (*s == '_')? s[2] : *s ;
934 n -= 2;
935 s += 3;
936 } else
937 c = *s++;
938 if (state != pstate) {
939 if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
940 state = 1;
941 else
942 putp(state ? ULenter : ULexit);
943 }
944 if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
945 putchar(c);
946 if (state && *chUL) {
947 pr(chBS);
948 putp(chUL);
949 }
950 pstate = state;
951 }
952 }
953
954 /*
955 ** Clear the screen
956 */
957 void
958 doclear()
959 {
960 if (Clear && !hard) {
961 putp(Clear);
962
963 /* Put out carriage return so that system doesn't
964 ** get confused by escape sequences when expanding tabs
965 */
966 putchar ('\r');
967 promptlen = 0;
968 }
969 }
970
971 /*
972 * Go to home position
973 */
974 void
975 home()
976 {
977 putp(Home);
978 }
979
980 static int lastcmd, lastarg, lastp;
981 static int lastcolon;
982 char shell_line[132];
983
984 /*
985 ** Read a command and do it. A command consists of an optional integer
986 ** argument followed by the command character. Return the number of lines
987 ** to display in the next screenful. If there is nothing more to display
988 ** in the current file, zero is returned.
989 */
990
991 int command (char *filename, register FILE *f)
992 {
993 register int nlines;
994 register int retval = 0;
995 register int c;
996 char colonch;
997 FILE *helpf;
998 int done;
999 char comchar, cmdbuf[80];
1000
1001 #define ret(val) retval=val;done++;break
1002
1003 done = 0;
1004 if (!errors)
1005 prompt (filename);
1006 else
1007 errors = 0;
1008 for (;;) {
1009 nlines = number (&comchar);
1010 lastp = colonch = 0;
1011 if (comchar == '.') { /* Repeat last command */
1012 lastp++;
1013 comchar = lastcmd;
1014 nlines = lastarg;
1015 if (lastcmd == ':')
1016 colonch = lastcolon;
1017 }
1018 lastcmd = comchar;
1019 lastarg = nlines;
1020 if (comchar == otty.c_cc[VERASE]) {
1021 kill_line ();
1022 prompt (filename);
1023 continue;
1024 }
1025 switch (comchar) {
1026 case ':':
1027 retval = colon (filename, colonch, nlines);
1028 if (retval >= 0)
1029 done++;
1030 break;
1031 case 'b':
1032 case ctrl('B'):
1033 {
1034 register int initline;
1035
1036 if (no_intty) {
1037 ringbell();
1038 return (-1);
1039 }
1040
1041 if (nlines == 0) nlines++;
1042
1043 putchar ('\r');
1044 erasep (0);
1045 xprintf ("\n");
1046 if (clreol)
1047 cleareol ();
1048 xprintf ("...back %d page", nlines);
1049 if (nlines > 1)
1050 pr ("s\n");
1051 else
1052 pr ("\n");
1053
1054 if (clreol)
1055 cleareol ();
1056 pr ("\n");
1057
1058 initline = Currline - dlines * (nlines + 1);
1059 if (! noscroll)
1060 --initline;
1061 if (initline < 0) initline = 0;
1062 Fseek(f, 0L);
1063 Currline = 0; /* skiplns() will make Currline correct */
1064 skiplns(initline, f);
1065 if (! noscroll) {
1066 ret(dlines + 1);
1067 }
1068 else {
1069 ret(dlines);
1070 }
1071 }
1072 case ' ':
1073 case 'z':
1074 if (nlines == 0) nlines = dlines;
1075 else if (comchar == 'z') dlines = nlines;
1076 ret (nlines);
1077 case 'd':
1078 case ctrl('D'):
1079 if (nlines != 0) nscroll = nlines;
1080 ret (nscroll);
1081 case 'q':
1082 case 'Q':
1083 end_it ();
1084 case 's':
1085 case 'f':
1086 if (nlines == 0) nlines++;
1087 if (comchar == 'f')
1088 nlines *= dlines;
1089 putchar ('\r');
1090 erasep (0);
1091 xprintf ("\n");
1092 if (clreol)
1093 cleareol ();
1094 xprintf ("...skipping %d line", nlines);
1095 if (nlines > 1)
1096 pr ("s\n");
1097 else
1098 pr ("\n");
1099
1100 if (clreol)
1101 cleareol ();
1102 pr ("\n");
1103
1104 while (nlines > 0) {
1105 while ((c = Getc (f)) != '\n')
1106 if (c == EOF) {
1107 retval = 0;
1108 done++;
1109 goto endsw;
1110 }
1111 Currline++;
1112 nlines--;
1113 }
1114 ret (dlines);
1115 case '\n':
1116 if (nlines != 0)
1117 dlines = nlines;
1118 else
1119 nlines = 1;
1120 ret (nlines);
1121 case '\f':
1122 if (!no_intty) {
1123 doclear ();
1124 Fseek (f, screen_start.chrctr);
1125 Currline = screen_start.line;
1126 ret (dlines);
1127 }
1128 else {
1129 ringbell();
1130 break;
1131 }
1132 case '\'':
1133 if (!no_intty) {
1134 kill_line ();
1135 pr ("\n***Back***\n\n");
1136 Fseek (f, context.chrctr);
1137 Currline = context.line;
1138 ret (dlines);
1139 }
1140 else {
1141 ringbell();
1142 break;
1143 }
1144 case '=':
1145 kill_line ();
1146 promptlen = printd (Currline);
1147 fflush (stdout);
1148 break;
1149 case 'n':
1150 lastp++;
1151 case '/':
1152 if (nlines == 0) nlines++;
1153 kill_line ();
1154 pr ("/");
1155 promptlen = 1;
1156 fflush (stdout);
1157 if (lastp) {
1158 errwrite ("\r");
1159 search (NULL, f, nlines); /* Use previous r.e. */
1160 }
1161 else {
1162 ttyin (cmdbuf, sizeof(cmdbuf)-2, '/');
1163 errwrite("\r");
1164 search (cmdbuf, f, nlines);
1165 }
1166 ret (dlines-1);
1167 case '!':
1168 do_shell (filename);
1169 break;
1170 case '?':
1171 case 'h':
1172 if ((helpf = fopen (HELPFILE, "r")) == NULL)
1173 error ("Can't open help file");
1174 if (noscroll) doclear ();
1175 copy_file (helpf);
1176 fclose (helpf);
1177 prompt (filename);
1178 break;
1179 case 'v': /* This case should go right before default */
1180 if (!no_intty) {
1181 kill_line ();
1182 cmdbuf[0] = '+';
1183 scanstr (Currline - dlines < 0 ? 0
1184 : Currline - (dlines + 1) / 2, &cmdbuf[1]);
1185 pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
1186 execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
1187 break;
1188 }
1189 default:
1190 if (dum_opt) {
1191 kill_line ();
1192 if (Senter && Sexit) {
1193 putp (Senter);
1194 promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch);
1195 putp (Sexit);
1196 }
1197 else
1198 promptlen = pr ("[Press 'h' for instructions.]");
1199 fflush (stdout);
1200 }
1201 else
1202 ringbell();
1203 break;
1204 }
1205 if (done) break;
1206 }
1207 putchar ('\r');
1208 endsw:
1209 inwait = 0;
1210 notell++;
1211 return (retval);
1212 }
1213
1214 char ch;
1215
1216 /*
1217 * Execute a colon-prefixed command.
1218 * Returns <0 if not a command that should cause
1219 * more of the file to be printed.
1220 */
1221
1222 int colon (char *filename, int cmd, int nlines)
1223 {
1224 if (cmd == 0)
1225 ch = readch ();
1226 else
1227 ch = cmd;
1228 lastcolon = ch;
1229 switch (ch) {
1230 case 'f':
1231 kill_line ();
1232 if (!no_intty)
1233 promptlen = xprintf ("\"%s\" line %d", fnames[fnum], Currline);
1234 else
1235 promptlen = xprintf ("[Not a file] line %d", Currline);
1236 fflush (stdout);
1237 return (-1);
1238 case 'n':
1239 if (nlines == 0) {
1240 if (fnum >= nfiles - 1)
1241 end_it ();
1242 nlines++;
1243 }
1244 putchar ('\r');
1245 erasep (0);
1246 skipf (nlines);
1247 return (0);
1248 case 'p':
1249 if (no_intty) {
1250 ringbell();
1251 return (-1);
1252 }
1253 putchar ('\r');
1254 erasep (0);
1255 if (nlines == 0)
1256 nlines++;
1257 skipf (-nlines);
1258 return (0);
1259 case '!':
1260 do_shell (filename);
1261 return (-1);
1262 case 'q':
1263 case 'Q':
1264 end_it ();
1265 default:
1266 ringbell();
1267 return (-1);
1268 }
1269 }
1270
1271 /*
1272 ** Read a decimal number from the terminal. Set cmd to the non-digit which
1273 ** terminates the number.
1274 */
1275
1276 int number(char *cmd)
1277 {
1278 register int i;
1279
1280 i = 0; ch = otty.c_cc[VKILL];
1281 for (;;) {
1282 ch = readch ();
1283 if (isdigit(ch))
1284 i = i*10 + ch - '0';
1285 else if (ch == otty.c_cc[VKILL])
1286 i = 0;
1287 else {
1288 *cmd = ch;
1289 break;
1290 }
1291 }
1292 return (i);
1293 }
1294
1295 void do_shell (char *filename)
1296 {
1297 char cmdbuf[80];
1298
1299 kill_line ();
1300 pr ("!");
1301 fflush (stdout);
1302 promptlen = 1;
1303 if (lastp)
1304 pr (shell_line);
1305 else {
1306 ttyin (cmdbuf, sizeof(cmdbuf)-2, '!');
1307 if (expand (shell_line, cmdbuf)) {
1308 kill_line ();
1309 promptlen = xprintf ("!%s", shell_line);
1310 }
1311 }
1312 fflush (stdout);
1313 errwrite("\n");
1314 promptlen = 0;
1315 shellp = 1;
1316 execute (filename, shell, shell, "-c", shell_line, 0);
1317 }
1318
1319 /*
1320 ** Search for nth ocurrence of regular expression contained in buf in the file
1321 */
1322
1323 void search(char buf[], FILE *file, register int n)
1324 {
1325 long startline = Ftell (file);
1326 register long line1 = startline;
1327 register long line2 = startline;
1328 register long line3 = startline;
1329 register int lncount;
1330 int saveln, rv, re_exec();
1331 char *s, *re_comp();
1332
1333 context.line = saveln = Currline;
1334 context.chrctr = startline;
1335 lncount = 0;
1336 if ((s = re_comp (buf)) != 0)
1337 error (s);
1338 while (!feof (file)) {
1339 line3 = line2;
1340 line2 = line1;
1341 line1 = Ftell (file);
1342 rdline (file);
1343 lncount++;
1344 if ((rv = re_exec (Line)) == 1) {
1345 if (--n == 0) {
1346 if (lncount > 3 || (lncount > 1 && no_intty))
1347 {
1348 pr ("\n");
1349 if (clreol)
1350 cleareol ();
1351 pr("...skipping\n");
1352 }
1353 if (!no_intty) {
1354 Currline -= (lncount >= 3 ? 3 : lncount);
1355 Fseek (file, line3);
1356 if (noscroll) {
1357 if (clreol) {
1358 home ();
1359 cleareol ();
1360 }
1361 else
1362 doclear ();
1363 }
1364 }
1365 else {
1366 kill_line ();
1367 if (noscroll) {
1368 if (clreol) {
1369 home ();
1370 cleareol ();
1371 }
1372 else
1373 doclear ();
1374 }
1375 pr (Line);
1376 putchar ('\n');
1377 }
1378 break;
1379 }
1380 } else if (rv == -1)
1381 error ("Regular expression botch");
1382 }
1383 if (feof (file)) {
1384 if (!no_intty) {
1385 #ifndef __linux__
1386 /* No longer in libc 4.5.8. . . */
1387 file->_flags &= ~STDIO_S_EOF_SEEN; /* why doesn't fseek do this ??!!??! */
1388 #endif
1389 Currline = saveln;
1390 Fseek (file, startline);
1391 }
1392 else {
1393 pr ("\nPattern not found\n");
1394 end_it ();
1395 }
1396 error ("Pattern not found");
1397 }
1398 }
1399
1400 /*VARARGS2*/
1401 void execute (char * filename, char * cmd, ...)
1402 {
1403 int id;
1404 int n;
1405 va_list argp;
1406 char * arg;
1407 char ** args;
1408 int argcount;
1409
1410 fflush (stdout);
1411 reset_tty ();
1412 for (n = 10; (id = fork ()) < 0 && n > 0; n--)
1413 sleep (5);
1414 if (id == 0) {
1415 if (!isatty(0)) {
1416 close(0);
1417 open("/dev/tty", 0);
1418 }
1419
1420 va_start(argp, cmd);
1421
1422 arg = va_arg(argp, char *);
1423 argcount = 0;
1424 while (arg) {
1425 argcount++;
1426 arg = va_arg(argp, char *);
1427 }
1428 va_end(argp); /* balance {}'s for some UNIX's */
1429
1430 args = alloca(sizeof(char *) * (argcount + 1));
1431 args[argcount] = NULL;
1432
1433 va_start(argp, cmd);
1434 arg = va_arg(argp, char *);
1435 argcount = 0;
1436 while (arg) {
1437 args[argcount] = arg;
1438 argcount++;
1439 arg = va_arg(argp, char *);
1440 }
1441 va_end(argp); /* balance {}'s for some UNIX's */
1442
1443 execv (cmd, args);
1444 errwrite("exec failed\n");
1445 exit (1);
1446 }
1447 if (id > 0) {
1448 signal (SIGINT, SIG_IGN);
1449 signal (SIGQUIT, SIG_IGN);
1450 if (catch_susp)
1451 signal(SIGTSTP, SIG_DFL);
1452 while (wait(0) > 0);
1453 signal (SIGINT, end_it);
1454 signal (SIGQUIT, onquit);
1455 if (catch_susp)
1456 signal(SIGTSTP, onsusp);
1457 } else
1458 errwrite("can't fork\n");
1459 set_tty ();
1460 pr ("------------------------\n");
1461 prompt (filename);
1462 }
1463 /*
1464 ** Skip n lines in the file f
1465 */
1466
1467 void skiplns (register int n, register FILE *f)
1468 {
1469 register int c;
1470
1471 while (n > 0) {
1472 while ((c = Getc (f)) != '\n')
1473 if (c == EOF)
1474 return;
1475 n--;
1476 Currline++;
1477 }
1478 }
1479
1480 /*
1481 ** Skip nskip files in the file list (from the command line). Nskip may be
1482 ** negative.
1483 */
1484
1485 void skipf (register int nskip)
1486 {
1487 if (nskip == 0) return;
1488 if (nskip > 0) {
1489 if (fnum + nskip > nfiles - 1)
1490 nskip = nfiles - fnum - 1;
1491 }
1492 else if (within)
1493 ++fnum;
1494 fnum += nskip;
1495 if (fnum < 0)
1496 fnum = 0;
1497 pr ("\n...Skipping ");
1498 pr ("\n");
1499 if (clreol)
1500 cleareol ();
1501 pr ("...Skipping ");
1502 pr (nskip > 0 ? "to file " : "back to file ");
1503 pr (fnames[fnum]);
1504 pr ("\n");
1505 if (clreol)
1506 cleareol ();
1507 pr ("\n");
1508 --fnum;
1509 }
1510
1511 /*----------------------------- Terminal I/O -------------------------------*/
1512
1513 void initterm()
1514 {
1515 int ret;
1516 char *padstr;
1517 char *term;
1518 struct winsize win;
1519
1520 #ifdef do_SIGTTOU
1521 retry:
1522 #endif
1523 no_tty = tcgetattr(fileno(stdout), &otty);
1524 if (!no_tty) {
1525 docrterase = (otty.c_cc[VERASE] != 255);
1526 docrtkill = (otty.c_cc[VKILL] != 255);
1527 #ifdef do_SIGTTOU
1528 {
1529 int tgrp;
1530 /*
1531 * Wait until we're in the foreground before we save the
1532 * the terminal modes.
1533 */
1534 if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) {
1535 perror("TIOCGPGRP");
1536 exit(1);
1537 }
1538 if (tgrp != getpgrp(0)) {
1539 kill(0, SIGTTOU);
1540 goto retry;
1541 }
1542 }
1543 #endif
1544 if ((term = getenv("TERM")) == 0) {
1545 dumb++; ul_opt = 0;
1546 }
1547 setupterm(term, 1, &ret);
1548 if (ret <= 0) {
1549 dumb++; ul_opt = 0;
1550 }
1551 else {
1552 #ifdef TIOCGWINSZ
1553 if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
1554 #endif
1555 Lpp = tigetnum("lines");
1556 Mcol = tigetnum("cols");
1557 #ifdef TIOCGWINSZ
1558 } else {
1559 if ((Lpp = win.ws_row) == 0)
1560 Lpp = tigetnum("lines");
1561 if ((Mcol = win.ws_col) == 0)
1562 Mcol = tigetnum("cols");
1563 }
1564 #endif
1565 if ((Lpp <= 0) || tigetflag("hc")) {
1566 hard++; /* Hard copy terminal */
1567 Lpp = 24;
1568 }
1569
1570 if (tigetflag("xenl"))
1571 eatnl++; /* Eat newline at last column + 1; dec, concept */
1572 if (Mcol <= 0)
1573 Mcol = 80;
1574
1575 if (tailequ (fnames[0], "page"))
1576 noscroll++;
1577 Wrap = tigetflag("am");
1578 bad_so = tigetflag ("xhp");
1579 eraseln = tigetstr("el");
1580 Clear = tigetstr("clear");
1581 Senter = tigetstr("smso");
1582 Sexit = tigetstr("rmso");
1583 if ((soglitch = tigetnum("xmc")) < 0)
1584 soglitch = 0;
1585
1586 /*
1587 * Set up for underlining: some terminals don't need it;
1588 * others have start/stop sequences, still others have an
1589 * underline char sequence which is assumed to move the
1590 * cursor forward one character. If underline sequence
1591 * isn't available, settle for standout sequence.
1592 */
1593
1594 if (tigetflag("ul") || tigetflag("os"))
1595 ul_opt = 0;
1596 if ((chUL = tigetstr("uc")) == NULL )
1597 chUL = "";
1598 if (((ULenter = tigetstr("smul")) == NULL ||
1599 (ULexit = tigetstr("rmul")) == NULL) && !*chUL) {
1600 if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
1601 ULenter = "";
1602 ULexit = "";
1603 } else
1604 ulglitch = soglitch;
1605 } else {
1606 ulglitch = 0;
1607 }
1608
1609 if ((padstr = tigetstr("pad")) != NULL)
1610 PC = *padstr;
1611 Home = tigetstr("home");
1612 if (Home == 0 || *Home == '\0')
1613 {
1614 if ((cursorm = tigetstr("cup")) != NULL) {
1615 strcpy(cursorhome, (const char *)tparm(cursorm, 0, 0));
1616 Home = cursorhome;
1617 }
1618 }
1619 EodClr = tigetstr("ed");
1620 if ((chBS = tigetstr("cub1")) == NULL)
1621 chBS = "\b";
1622
1623 }
1624 if ((shell = getenv("SHELL")) == NULL)
1625 shell = "/bin/sh";
1626 }
1627 no_intty = tcgetattr(fileno(stdin), &otty);
1628 tcgetattr(fileno(stderr), &otty);
1629 savetty0 = otty;
1630 slow_tty = (otty.c_cflag & CBAUD) < B1200;
1631 hardtabs = (otty.c_oflag & TABDLY) != XTABS;
1632 if (!no_tty) {
1633 otty.c_lflag &= ~(ICANON|ECHO);
1634 otty.c_cc[VMIN] = 1;
1635 otty.c_cc[VTIME] = 0;
1636 }
1637 }
1638
1639 int readch ()
1640 {
1641 char ch;
1642 extern int errno;
1643
1644 errno = 0;
1645 if (read (2, &ch, 1) <= 0) {
1646 if (errno != EINTR)
1647 end_it();
1648 else
1649 ch = otty.c_cc[VKILL];
1650 }
1651 return (ch);
1652 }
1653
1654 static char *BS = "\b";
1655 static char *BSB = "\b \b";
1656 static char *CARAT = "^";
1657 #define ERASEONECHAR \
1658 if (docrterase) \
1659 errwrite(BSB); \
1660 else \
1661 errwrite(BS);
1662
1663 void ttyin (char buf[], register int nmax, char pchar)
1664 {
1665 register char *sptr;
1666 register char ch;
1667 register int slash = 0;
1668 int maxlen;
1669 char cbuf;
1670
1671 sptr = buf;
1672 maxlen = 0;
1673 while (sptr - buf < nmax) {
1674 if (promptlen > maxlen) maxlen = promptlen;
1675 ch = readch ();
1676 if (ch == '\\') {
1677 slash++;
1678 }
1679 else if ((ch == otty.c_cc[VERASE]) && !slash) {
1680 if (sptr > buf) {
1681 --promptlen;
1682 ERASEONECHAR
1683 --sptr;
1684 if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
1685 --promptlen;
1686 ERASEONECHAR
1687 }
1688 continue;
1689 }
1690 else {
1691 if (!eraseln) promptlen = maxlen;
1692 longjmp (restore, 1);
1693 }
1694 }
1695 else if ((ch == otty.c_cc[VKILL]) && !slash) {
1696 if (hard) {
1697 show (ch);
1698 putchar ('\n');
1699 putchar (pchar);
1700 }
1701 else {
1702 putchar ('\r');
1703 putchar (pchar);
1704 if (eraseln)
1705 erasep (1);
1706 else if (docrtkill)
1707 while (promptlen-- > 1)
1708 errwrite(BSB);
1709 promptlen = 1;
1710 }
1711 sptr = buf;
1712 fflush (stdout);
1713 continue;
1714 }
1715 if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) {
1716 ERASEONECHAR
1717 --sptr;
1718 }
1719 if (ch != '\\')
1720 slash = 0;
1721 *sptr++ = ch;
1722 if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1723 ch += ch == RUBOUT ? -0100 : 0100;
1724 errwrite(CARAT);
1725 promptlen++;
1726 }
1727 cbuf = ch;
1728 if (ch != '\n' && ch != ESC) {
1729 errwrite1(&cbuf);
1730 promptlen++;
1731 }
1732 else
1733 break;
1734 }
1735 *--sptr = '\0';
1736 if (!eraseln) promptlen = maxlen;
1737 if (sptr - buf >= nmax - 1)
1738 error ("Line too long");
1739 }
1740
1741 int expand (char *outbuf, char *inbuf)
1742 {
1743 register char *instr;
1744 register char *outstr;
1745 register char ch;
1746 char temp[200];
1747 int changed = 0;
1748
1749 instr = inbuf;
1750 outstr = temp;
1751 while ((ch = *instr++) != '\0')
1752 switch (ch) {
1753 case '%':
1754 if (!no_intty) {
1755 strcpy (outstr, fnames[fnum]);
1756 outstr += strlen (fnames[fnum]);
1757 changed++;
1758 }
1759 else
1760 *outstr++ = ch;
1761 break;
1762 case '!':
1763 if (!shellp)
1764 error ("No previous command to substitute for");
1765 strcpy (outstr, shell_line);
1766 outstr += strlen (shell_line);
1767 changed++;
1768 break;
1769 case '\\':
1770 if (*instr == '%' || *instr == '!') {
1771 *outstr++ = *instr++;
1772 break;
1773 }
1774 default:
1775 *outstr++ = ch;
1776 }
1777 *outstr++ = '\0';
1778 strcpy (outbuf, temp);
1779 return (changed);
1780 }
1781
1782 void show (register char ch)
1783 {
1784 char cbuf;
1785
1786 if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
1787 ch += ch == RUBOUT ? -0100 : 0100;
1788 errwrite(CARAT);
1789 promptlen++;
1790 }
1791 cbuf = ch;
1792 errwrite1(&cbuf);
1793 promptlen++;
1794 }
1795
1796 void errwrite (char *txt)
1797 {
1798 write (fileno(stderr), txt, strlen(txt));
1799 }
1800
1801 void errwrite1 (char *sym)
1802 {
1803 write (fileno(stderr), sym, 1);
1804 }
1805
1806 void error (char *mess)
1807 {
1808 if (clreol)
1809 cleareol ();
1810 else
1811 kill_line ();
1812 promptlen += strlen (mess);
1813 if (Senter && Sexit) {
1814 putp (Senter);
1815 pr(mess);
1816 putp (Sexit);
1817 }
1818 else
1819 pr (mess);
1820 fflush(stdout);
1821 errors++;
1822 longjmp (restore, 1);
1823 }
1824
1825
1826 void set_tty ()
1827 {
1828 otty.c_lflag &= ~(ICANON|ECHO);
1829 otty.c_cc[VMIN] = 1; /* read at least 1 char */
1830 otty.c_cc[VTIME] = 0; /* no timeout */
1831 stty(fileno(stderr), &otty);
1832 }
1833
1834 int ourputch(int ch) {
1835 return putc(ch, stdout);
1836 }
1837
1838 void reset_tty ()
1839 {
1840 if (no_tty)
1841 return;
1842 if (pstate) {
1843 tputs(ULexit, 1, ourputch);
1844 fflush(stdout);
1845 pstate = 0;
1846 }
1847 otty.c_lflag |= ICANON|ECHO;
1848 otty.c_cc[VMIN] = savetty0.c_cc[VMIN];
1849 otty.c_cc[VTIME] = savetty0.c_cc[VTIME];
1850 stty(fileno(stderr), &savetty0);
1851 }
1852
1853 void rdline (register FILE *f)
1854 {
1855 register int c;
1856 register char *p;
1857
1858 p = Line;
1859 while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
1860 *p++ = c;
1861 if (c == '\n')
1862 Currline++;
1863 *p = '\0';
1864 }
1865
1866 /* Come here when we get a suspend signal from the terminal */
1867
1868 void onsusp ()
1869 {
1870 sigset_t signals, oldmask;
1871
1872 /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
1873 signal(SIGTTOU, SIG_IGN);
1874 reset_tty ();
1875 fflush (stdout);
1876 signal(SIGTTOU, SIG_DFL);
1877 /* Send the TSTP signal to suspend our process group */
1878 signal(SIGTSTP, SIG_DFL);
1879
1880 /* unblock SIGTSTP or we won't be able to suspend ourself */
1881 sigemptyset(&signals);
1882 sigaddset(&signals, SIGTSTP);
1883 sigprocmask(SIG_UNBLOCK, &signals, &oldmask);
1884
1885 kill (0, SIGTSTP);
1886 /* Pause for station break */
1887
1888 sigprocmask(SIG_SETMASK, &oldmask, NULL);
1889
1890 /* We're back */
1891 signal (SIGTSTP, onsusp);
1892 set_tty ();
1893 if (inwait)
1894 longjmp (restore, 1);
1895 }