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