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