xref: /openbsd/distrib/special/more/more.c (revision ce166376)
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(&reg, buf, REG_NOSUB)) != 0) {
1188 			initialized = 0;
1189 			regerror(rv, &reg, ebuf, sizeof(ebuf));
1190 			regfree(&reg);
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(&reg, 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, &reg, 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