xref: /titanic_51/usr/src/cmd/pg/pg.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <signal.h>
34 #include <setjmp.h>
35 #include <sys/types.h>
36 #include <sys/dirent.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <wchar.h>
42 #include <curses.h>
43 #include <term.h>
44 #include <errno.h>
45 #include <stdlib.h>
46 #include <regexpr.h>
47 #include <limits.h>
48 #include <locale.h>
49 #include <wctype.h> /* iswprint() */
50 #include <string.h>
51 #include <unistd.h>
52 #include <wait.h>
53 #include <libw.h>
54 #include <regexpr.h>
55 
56 
57 /*
58  *	pg -- paginator for crt terminals
59  *
60  *	Includes the ability to display pages that have
61  *	already passed by. Also gives the user the ability
62  *	to search forward and backwards for regular expressions.
63  *	This works for piped input by copying to a temporary file,
64  *	and resolving backreferences from there.
65  *
66  *	Note:	The reason that there are so many commands to do
67  *		the same types of things is to try to accommodate
68  *		users of other paginators.
69  */
70 
71 #define	LINSIZ	1024
72 #define	QUIT	'\034'
73 #define	BOF	(EOF - 1)	/* Begining of File */
74 #define	STOP    (EOF - 2)
75 #define	PROMPTSIZE	256
76 
77 /*
78  * Function definitions
79  */
80 static	void	lineset(int);
81 static	char	*setprompt();
82 static	int	set_state(int *, wchar_t, char *);
83 static	void	help();
84 static	void	copy_file(FILE *, FILE *);
85 static	void	re_error(int);
86 static	void	save_input(FILE *);
87 static	void	save_pipe();
88 static	void	newdol(FILE *);
89 static	void	erase_line(int);
90 static	void	kill_line();
91 static	void	doclear();
92 static	void	sopr(char *, int);
93 static	void	prompt(char *);
94 static	void	error(char *);
95 static	void	terminit();
96 static	void	compact();
97 static	off_t	getline(FILE *);
98 static	int	mrdchar();
99 static	off_t	find(int, off_t);
100 static	int	search(char *, off_t);
101 static	FILE	*checkf(char *);
102 static	int	skipf(int);
103 static	int	readch();
104 static	int	ttyin();
105 static	int	number();
106 static	int	command(char *);
107 static	int	screen(char *);
108 static	int	fgetputc();
109 static 	char	*pg_strchr();
110 
111 
112 struct line {			/* how line addresses are stored */
113 	off_t	l_addr;		/* file offset */
114 	off_t	l_no;		/* line number in file */
115 };
116 
117 typedef	struct line	LINE;
118 
119 static	LINE	*zero = NULL,	/* first line */
120 		*dot,		/* current line */
121 		*dol,		/* last line */
122 		*contig;	/* where contiguous (non-aged) lines start */
123 static	long	nlall;		/* room for how many LINEs in memory */
124 
125 static	FILE	*in_file,	/* current input stream */
126 		*tmp_fin,	/* pipe temporary file in */
127 		*tmp_fou;	/* pipe temporary file out */
128 static	char	tmp_name[] = "/tmp/pgXXXXXX";
129 
130 static	short	sign;		/* sign of command input */
131 
132 static	int	fnum,		/* which file argument we're in */
133 		pipe_in,	/* set when stdin is a pipe */
134 		out_is_tty;	/* set if stdout is a tty */
135 static	gid_t	my_pgid;
136 
137 static	void	on_brk(),
138 		end_it();
139 static	short	brk_hit;	/* interrupt handling is pending flag */
140 
141 static	int	window = 0;	/* window size in lines */
142 static	short	eof_pause = 1;	/* pause w/ prompt at end of files */
143 static	short	rmode = 0;	/* deny shell escape in restricted mode */
144 static	short	soflag = 0;	/* output all messages in standout mode */
145 static	short	promptlen;	/* length of the current prompt */
146 static	short	firstf = 1;	/* set before first file has been processed */
147 static	short	inwait,		/* set while waiting for user input */
148 		errors;		/* set if error message has been printed. */
149 				/* if so, need to erase it and prompt */
150 
151 static	char	**fnames;
152 static	short	status = 0;	/* set > 0 if error detected */
153 static	short	fflag = 0;	/* set if the f option is used */
154 static	short	nflag = 0;	/* set for "no newline" input option */
155 static	short	clropt = 0;	/* set if the clear option is used */
156 static	int	initopt = 0;	/* set if the line option is used */
157 static	int	srchopt = 0;	/* set if the search option is used */
158 static	int	initline;
159 static	char	initbuf[BUFSIZ];
160 static	wchar_t	leave_search = L't';
161 				/* where on the page to leave a found string */
162 static	short	nfiles;
163 static	char	*shell;
164 static	char	*promptstr = ":";
165 static  off_t	nchars;			/* return from getline in find() */
166 static	jmp_buf	restore;
167 static	char	Line[LINSIZ+2];
168 
169 static	int	catch_susp;
170 
171 static	void	onsusp();
172 
173 struct screen_stat {
174 	off_t	first_line;
175 	off_t	last_line;
176 	short	is_eof;
177 	};
178 
179 static	struct screen_stat old_ss = { 0, 0, 0 };
180 static	struct screen_stat new_ss;
181 static	struct termio otty;	/* to save old terminal settings */
182 
183 static	short	termflg = 0;	/* set once terminal is initialized */
184 static	short	eoflag;		/* set whenever at end of current file */
185 static	short	doliseof;	/* set when last line of file is known */
186 static	off_t	eofl_no;	/* what the last line of the file is */
187 static	void	usage(void);
188 static FILE	*pg_stdin;
189 
190 main(argc, argv)
191 int argc;
192 char *argv[];
193 {
194 	char	*s;
195 	char	*p;
196 	int		prnames = 0;
197 	int		opt;
198 	int		i;
199 
200 	(void) setlocale(LC_ALL, "");
201 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
202 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
203 #endif
204 	(void) textdomain(TEXT_DOMAIN);
205 
206 	/* check for non-standard "-#" option */
207 	for (i = 1; i < argc; i++) {
208 		if (strcmp(argv[i], "--") == 0)
209 			break;
210 
211 		if ((argv[i][0] == '-') && isdigit(argv[i][1])) {
212 			if (strlen(&argv[i][1]) !=
213 			    strspn(&argv[i][1], "0123456789")) {
214 				(void) fprintf(stderr, gettext(
215 				    "pg: Badly formed number\n"));
216 				usage();
217 			}
218 
219 			window = (int)strtol(&argv[i][1], (char **)NULL, 10);
220 
221 			while (i < argc) {
222 				argv[i] = argv[i + 1];
223 				i++;
224 			}
225 			i--;
226 			argc--;
227 		}
228 	}
229 
230 	/* check for non-standard + option */
231 	for (i = 1; i < argc; i++) {
232 		if (strcmp(argv[i], "--") == 0)
233 		break;
234 
235 		if (argv[i][0] == '+') {
236 			if (argv[i][1] == '/') {
237 				srchopt++;
238 				initopt = 0;
239 				for (s = &argv[i][2], p = initbuf; *s != '\0'; )
240 					if (p < initbuf + sizeof (initbuf))
241 						*p++ = *s++;
242 					else {
243 						(void) fprintf(stderr, gettext(
244 						    "pg: pattern too long\n"));
245 						return (1);
246 					}
247 				*p = '\0';
248 			} else {
249 				initopt++;
250 				srchopt = 0;
251 				s = &argv[i][2];
252 				for (; isdigit(*s); s++)
253 					initline = initline*10 + *s -'0';
254 				if (*s != '\0')
255 					usage();
256 			}
257 
258 			while (i < argc) {
259 				argv[i] = argv[i + 1];
260 				i++;
261 			}
262 			i--;
263 			argc--;
264 		}
265 	}
266 
267 	while ((opt = getopt(argc, argv, "cefnrsp:")) != EOF) {
268 		switch (opt) {
269 		case 'c':
270 			clropt = 1;
271 			break;
272 
273 		case 'e':
274 			eof_pause = 0;
275 			break;
276 
277 		case 'f':
278 			fflag = 1;
279 			break;
280 
281 		case 'n':
282 			nflag = 1;
283 			break;
284 
285 		case 'r':
286 			rmode = 1;	/* restricted mode */
287 			break;
288 
289 		case 's':
290 			soflag = 1;	/* standout mode */
291 			break;
292 
293 		case 'p':
294 			promptstr = setprompt(optarg);
295 			break;
296 
297 		default:
298 			usage();
299 		}
300 	}
301 
302 	nfiles = argc - optind;
303 	fnames = &argv[optind];
304 
305 	(void) signal(SIGQUIT, end_it);
306 	(void) signal(SIGINT, end_it);
307 	out_is_tty = isatty(1);
308 	my_pgid = getpgrp();
309 	if (out_is_tty) {
310 		terminit();
311 		(void) signal(SIGQUIT, on_brk);
312 		(void) signal(SIGINT, on_brk);
313 		if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
314 			(void) signal(SIGTSTP, onsusp);
315 			catch_susp++;
316 		}
317 	}
318 	if (window == 0)
319 		window = lines - 1;
320 	if (window <= 1)
321 		window = 2;
322 	if (initline <= 0)
323 		initline = 1;
324 	if (nfiles > 1)
325 		prnames++;
326 
327 	if (nfiles == 0) {
328 		fnames[0] = "-";
329 		nfiles++;
330 	}
331 	while (fnum < nfiles) {
332 		if (strcmp(fnames[fnum], "") == 0)
333 			fnames[fnum] = "-";
334 		if ((in_file = checkf(fnames[fnum])) == NULL)
335 		{
336 			status = 2;
337 			fnum++;
338 		} else {
339 			status = 0;
340 			if (out_is_tty)
341 				fnum += screen(fnames[fnum]);
342 			else {
343 				if (prnames) {
344 					(void) fputs("::::::::::::::\n",
345 					    stdout);
346 					(void) fputs(fnames[fnum], stdout);
347 					(void) fputs("\n::::::::::::::\n",
348 					    stdout);
349 				}
350 				copy_file(in_file, stdout);
351 				fnum++;
352 			}
353 			(void) fflush(stdout);
354 			if (pipe_in)
355 				save_pipe();
356 			else
357 			if (in_file != tmp_fin)
358 				(void) fclose(in_file);
359 		}
360 	}
361 	end_it();
362 
363 	/*NOTREACHED*/
364 	return (0);
365 }
366 
367 static	char *
368 setprompt(s)
369 char *s;
370 {
371 	int i = 0;
372 	int pct_d = 0;
373 	static char pstr[PROMPTSIZE];
374 
375 	while (i < PROMPTSIZE - 2)
376 		switch (pstr[i++] = *s++) {
377 		case '\0':
378 			return (pstr);
379 		case '%':
380 			if (*s == 'd' && !pct_d) {
381 				pct_d++;
382 			} else if (*s != '%')
383 				pstr[i++] = '%';
384 			if ((pstr[i++] = *s++) == '\0')
385 				return (pstr);
386 			break;
387 		default:
388 			break;
389 		}
390 	(void) fprintf(stderr, gettext("pg: prompt too long\n"));
391 	exit(1);
392 	/*NOTREACHED*/
393 }
394 
395 
396 /*
397  * Print out the contents of the file f, one screenful at a time.
398  */
399 
400 static int
401 screen(file_name)
402 char *file_name;
403 {
404 	int cmd_ret = 0;
405 	off_t start;
406 	short hadchance = 0;
407 
408 	old_ss.is_eof = 0;
409 	old_ss.first_line = 0;
410 	old_ss.last_line = 0;
411 	new_ss = old_ss;
412 	if (!firstf)
413 		cmd_ret = command(file_name);
414 	else {
415 		firstf = 0;
416 		if (initopt) {
417 			initopt = 0;
418 			new_ss.first_line = initline;
419 			new_ss.last_line = initline + (off_t)window - 1;
420 		} else if (srchopt) {
421 			srchopt = 0;
422 			if (!search(initbuf, (off_t)1))
423 				cmd_ret = command(file_name);
424 		} else {
425 			new_ss.first_line = 1;
426 			new_ss.last_line = (off_t)window;
427 		}
428 	}
429 
430 	for (;;) {
431 		if (cmd_ret)
432 			return (cmd_ret);
433 		if (hadchance && new_ss.last_line >= eofl_no)
434 			return (1);
435 		hadchance = 0;
436 
437 		if (new_ss.last_line < (off_t)window)
438 			new_ss.last_line = (off_t)window;
439 		if (find(0, new_ss.last_line + 1) != EOF)
440 			new_ss.is_eof = 0;
441 		else {
442 			new_ss.is_eof = 1;
443 			new_ss.last_line = eofl_no - 1;
444 			new_ss.first_line = new_ss.last_line -
445 			    (off_t)window + 1;
446 		}
447 
448 		if (new_ss.first_line < 1)
449 			new_ss.first_line = 1;
450 		if (clropt) {
451 			doclear();
452 			start = new_ss.first_line;
453 		} else {
454 			if (new_ss.first_line == old_ss.last_line)
455 				start = new_ss.first_line + 1;
456 			else
457 			if (new_ss.first_line > old_ss.last_line)
458 				start = new_ss.first_line;
459 			else
460 			if (old_ss.first_line < new_ss.first_line)
461 				start = old_ss.last_line + 1;
462 			else
463 				start = new_ss.first_line;
464 
465 			if (start < old_ss.first_line)
466 				sopr(gettext("...skipping backward\n"), 0);
467 			else
468 			if (start > old_ss.last_line + 1)
469 				sopr(gettext("...skipping forward\n"), 0);
470 		}
471 
472 		for (; start <= new_ss.last_line; start++) {
473 			(void) find(0, start);
474 			(void) fputs(Line, stdout);
475 			if (brk_hit) {
476 				new_ss.last_line = find(1, 0);
477 				new_ss.is_eof = 0;
478 				break;
479 			}
480 		}
481 
482 		brk_hit = 0;
483 		(void) fflush(stdout);
484 		if (new_ss.is_eof) {
485 			if (!eof_pause || eofl_no == 1)
486 				return (1);
487 			hadchance++;
488 			error("(EOF)");
489 		}
490 		old_ss = new_ss;
491 		cmd_ret = command((char *)NULL);
492 	}
493 }
494 
495 static	char	cmdbuf[LINSIZ], *cmdptr;
496 #define	BEEP()		if (bell) { (void) putp(bell); (void) fflush(stdout); }
497 #define	BLANKS(p)	while (*p == ' ' || *p == '\t') p++
498 #define	CHECKEND()	BLANKS(cmdptr); if (*cmdptr) { BEEP(); break; }
499 
500 /*
501  * Read a command and do it. A command consists of an optional integer
502  * argument followed by the command character.  Return the number of files
503  * to skip, 0 if we're still talking about the same file.
504  */
505 
506 static int
507 command(filename)
508 char *filename;
509 {
510 	off_t nlines;
511 	FILE *sf;
512 	char *cmdend;
513 	pid_t id;
514 	int skip;
515 	int	len;
516 	wchar_t	wc;
517 	wchar_t	wc_e;
518 	wchar_t	wc_e1;
519 	char	*p;
520 
521 	for (;;) {
522 		/*
523 		 * Wait for output to drain before going on.
524 		 * This is done so that the user will not hit
525 		 * break and quit before he has seen the prompt.
526 		 */
527 		(void) ioctl(1, TCSBRK, 1);
528 		if (setjmp(restore) > 0)
529 			end_it();
530 		inwait = 1;
531 		brk_hit = 0;
532 		if (errors)
533 			errors = 0;
534 		else {
535 			kill_line();
536 			prompt(filename);
537 		}
538 		(void) fflush(stdout);
539 		if (ttyin())
540 			continue;
541 		cmdptr = cmdbuf;
542 		nlines = number();
543 		BLANKS(cmdptr);
544 
545 		if ((len = mbtowc(&wc, cmdptr, MB_CUR_MAX)) <= 0) {
546 			wc = *cmdptr;
547 			len = 1;
548 		}
549 		cmdptr += len;
550 		switch (wc) {
551 		case 'h':
552 			CHECKEND();
553 			help();
554 			break;
555 		case '\014': /* ^L */
556 		case '.':	/* redisplay current window */
557 			CHECKEND();
558 			new_ss.first_line = old_ss.first_line;
559 			new_ss.last_line = old_ss.last_line;
560 			inwait = 0;
561 			return (0);
562 		case 'w':	/* set window size */
563 		case 'z':
564 			if (sign == -1) {
565 				BEEP();
566 				break;
567 			}
568 			CHECKEND();
569 			if (nlines == 0)
570 				nlines = (off_t)window;
571 			else
572 			if (nlines > 1)
573 				window = (int)nlines;
574 			else {
575 				BEEP();
576 				break;
577 			}
578 			new_ss.first_line = old_ss.last_line;
579 			new_ss.last_line = new_ss.first_line +
580 			    (off_t)window - 1;
581 			inwait = 0;
582 			return (0);
583 		case '\004': /* ^D */
584 		case 'd':
585 			CHECKEND();
586 			if (sign == 0)
587 				sign = 1;
588 			new_ss.last_line = old_ss.last_line +
589 			    (off_t)sign*window/2;
590 			new_ss.first_line = new_ss.last_line -
591 			    (off_t)window + 1;
592 			inwait = 0;
593 			return (0);
594 		case 's':
595 			/*
596 			 * save input in filename.
597 			 * Check for filename, access, etc.
598 			 */
599 			BLANKS(cmdptr);
600 			if (!*cmdptr) {
601 				BEEP();
602 				break;
603 			}
604 			if (setjmp(restore) > 0) {
605 				BEEP();
606 			} else {
607 				char outstr[PROMPTSIZE];
608 				if ((sf = fopen(cmdptr, "w")) == NULL) {
609 					error("cannot open save file");
610 					break;
611 				}
612 				kill_line();
613 				(void) sprintf(outstr, gettext(
614 				    "saving file %s"), cmdptr);
615 				sopr(outstr, 1);
616 				(void) fflush(stdout);
617 				save_input(sf);
618 				error("saved");
619 			}
620 			(void) fclose(sf);
621 			break;
622 		case 'q':
623 		case 'Q':
624 			CHECKEND();
625 			inwait = 0;
626 			end_it();
627 			/*FALLTHROUGH*/
628 
629 		case 'f':	/* skip forward screenfuls */
630 			CHECKEND();
631 			if (sign == 0)
632 				sign++;	/* skips are always relative */
633 			if (nlines == 0)
634 				nlines++;
635 			nlines = nlines * (window - 1);
636 			if (sign == 1)
637 				new_ss.first_line = old_ss.last_line + nlines;
638 			else
639 				new_ss.first_line = old_ss.first_line - nlines;
640 			new_ss.last_line = new_ss.first_line +
641 			    (off_t)window - 1;
642 			inwait = 0;
643 			return (0);
644 		case 'l':	/* get a line */
645 			CHECKEND();
646 			if (nlines == 0) {
647 				nlines++;
648 				if (sign == 0)
649 					sign = 1;
650 			}
651 			switch (sign) {
652 			case 1:
653 				new_ss.last_line = old_ss.last_line + nlines;
654 				new_ss.first_line =
655 				    new_ss.last_line - (off_t)window + 1;
656 				break;
657 			case 0:  /* leave addressed line at top */
658 				new_ss.first_line = nlines;
659 				new_ss.last_line = nlines + (off_t)window - 1;
660 				break;
661 			case -1:
662 				new_ss.first_line = old_ss.first_line - nlines;
663 				new_ss.last_line =
664 				    new_ss.first_line + (off_t)window - 1;
665 				break;
666 			}
667 			inwait = 0;
668 			return (0);
669 		case '\0': /* \n or blank */
670 			if (nlines == 0) {
671 				nlines++;
672 				if (sign == 0)
673 					sign = 1;
674 			}
675 			nlines = (nlines - 1) * (window - 1);
676 			switch (sign) {
677 			case 1:
678 				new_ss.first_line = old_ss.last_line + nlines;
679 				new_ss.last_line =
680 				    new_ss.first_line + (off_t)window - 1;
681 				break;
682 			case 0:
683 				new_ss.first_line = nlines + 1;
684 				new_ss.last_line = nlines + (off_t)window;
685 				/*
686 				 * This if statement is to fix the obscure bug
687 				 * where you have a file that has less lines
688 				 * than a screen holds, and the user types '1',
689 				 * expecting to have the 1st page (re)displayed.
690 				 * If we didn't set the new last_line to
691 				 * eofl_no-1, the screen() routine
692 				 * would cause pg to exit.
693 				 */
694 				if (new_ss.first_line == 1 &&
695 				    new_ss.last_line >= eofl_no)
696 					new_ss.last_line = eofl_no - 1;
697 				break;
698 			case -1:
699 				new_ss.last_line = old_ss.first_line - nlines;
700 				new_ss.first_line =
701 				    new_ss.last_line - (off_t)window + 1;
702 				break;
703 			}
704 			inwait = 0;
705 			return (0);
706 		case 'n':	/* switch to next file in arglist */
707 			CHECKEND();
708 			if (sign == 0)
709 				sign = 1;
710 			if (nlines == 0)
711 				nlines++;
712 			if ((skip = skipf(sign *nlines)) == 0) {
713 				BEEP();
714 				break;
715 			}
716 			inwait = 0;
717 			return (skip);
718 		case 'p':	/* switch to previous file in arglist */
719 			CHECKEND();
720 			if (sign == 0)
721 				sign = 1;
722 			if (nlines == 0)
723 				nlines++;
724 			if ((skip = skipf(-sign * nlines)) == 0) {
725 				BEEP();
726 				break;
727 			}
728 			inwait = 0;
729 			return (skip);
730 		case '$':	/* go to end of file */
731 			CHECKEND();
732 			sign = 1;
733 			while (find(1, (off_t)10000) != EOF)
734 				/* any large number will do */;
735 			new_ss.last_line = eofl_no - 1;
736 			new_ss.first_line = eofl_no - (off_t)window;
737 			inwait = 0;
738 			return (0);
739 		case '/':	/* search forward for r.e. */
740 		case '?':	/*   "  backwards */
741 		case '^':	/* this ones a ? for regent100s */
742 			if (sign < 0) {
743 				BEEP();
744 				break;
745 			}
746 			if (nlines == 0)
747 				nlines++;
748 			cmdptr--;
749 			cmdend = cmdptr + (strlen(cmdptr) - 1);
750 			wc_e1 = -1;
751 			wc_e = -1;
752 			for (p = cmdptr; p <= cmdend; p += len) {
753 				wc_e1 = wc_e;
754 				if ((len = mbtowc(&wc_e, p, MB_CUR_MAX)) <= 0) {
755 					wc_e = *p;
756 					len = 1;
757 				}
758 			}
759 
760 			if (cmdend > cmdptr + 1) {
761 				if ((wc_e1 == *cmdptr) &&
762 				    ((wc_e == L't') ||
763 					(wc_e == L'm') || (wc_e == L'b'))) {
764 					leave_search = wc_e;
765 					wc_e = wc_e1;
766 					cmdend--;
767 				}
768 			}
769 			if ((cmdptr < cmdend) && (wc_e == *cmdptr))
770 				*cmdend = '\0';
771 			if (*cmdptr != '/')  /* signify back search by - */
772 				nlines = -nlines;
773 			if (!search(++cmdptr, (off_t)nlines))
774 				break;
775 			else {
776 				inwait = 0;
777 				return (0);
778 			}
779 		case '!':	/* shell escape */
780 			if (rmode) {	/* restricted mode */
781 				(void) fprintf(stderr, gettext(
782 				"!command not allowed in restricted mode.\n"));
783 				break;
784 			}
785 			if (!hard_copy) { /* redisplay the command */
786 				(void) fputs(cmdbuf, stdout);
787 				(void) fputs("\n", stdout);
788 			}
789 			if ((id = fork()) < 0) {
790 				error("cannot fork, try again later");
791 				break;
792 			}
793 			if (id == (pid_t)0) {
794 				/*
795 				 * if stdin is a pipe, need to close it so
796 				 * that the terminal is really stdin for
797 				 * the command
798 				 */
799 				(void) fclose(stdin);
800 				(void) fclose(pg_stdin);
801 				(void) dup(fileno(stdout));
802 				(void) execl(shell, shell, "-c", cmdptr, 0);
803 				(void) perror("exec");
804 				exit(1);
805 			}
806 			(void) signal(SIGINT, SIG_IGN);
807 			(void) signal(SIGQUIT, SIG_IGN);
808 			if (catch_susp)
809 				(void) signal(SIGTSTP, SIG_DFL);
810 			while (wait((int *)0) != id);
811 			{
812 				if (errno == ECHILD)
813 					break;
814 				else
815 					errno = 0;
816 			}
817 			(void) fputs("!\n", stdout);
818 			(void) fflush(stdout);
819 			(void) signal(SIGINT, on_brk);
820 			(void) signal(SIGQUIT, on_brk);
821 			if (catch_susp)
822 				(void) signal(SIGTSTP, onsusp);
823 			break;
824 		default:
825 			BEEP();
826 			break;
827 		}
828 	}
829 }
830 
831 static int
832 number()
833 {
834 	int i;
835 	char *p;
836 
837 	i = 0;
838 	sign = 0;
839 	p = cmdptr;
840 	BLANKS(p);
841 	if (*p == '+') {
842 		p++;
843 		sign = 1;
844 	}
845 	else
846 	if (*p == '-') {
847 		p++;
848 		sign = -1;
849 	}
850 	while (isdigit(*p))
851 		i = i * 10 + *p++ - '0';
852 	cmdptr = p;
853 	return (i);
854 }
855 
856 static int
857 ttyin()
858 {
859 	char *sptr, *p;
860 	wchar_t ch;
861 	int slash = 0;
862 	int state = 0;
863 	int width, length;
864 	char multic[MB_LEN_MAX];
865 	static	int	readch();
866 	int 	len;
867 
868 	(void) fixterm();
869 	/* initialize state processing */
870 	(void) set_state(&state, ' ', (char *)0);
871 	sptr = cmdbuf;
872 	while (state != 10) {
873 		if ((ch = readch()) < 0 || !iswascii(ch) && !iswprint(ch)) {
874 			BEEP();
875 			continue;
876 		}
877 
878 		if ((length = wctomb(multic, ch)) < 0)
879 			length = 0;
880 		multic[length] = 0;
881 
882 		if (ch == '\n' && !slash)
883 			break;
884 		if (ch == erasechar() && !slash) {
885 			if (sptr > cmdbuf) {
886 				char *oldp = cmdbuf;
887 				wchar_t wchar;
888 				p = cmdbuf;
889 				while (p  < sptr) {
890 					oldp = p;
891 					len = mbtowc(&wchar, p, MB_CUR_MAX);
892 					if (len <= 0) {
893 						wchar = (unsigned char)*p;
894 						len = 1;
895 					}
896 					p += len;
897 				}
898 				if ((width = wcwidth(wchar)) <= 0)
899 					/* ascii control character */
900 					width = 2;
901 				promptlen -= width;
902 				while (width--)
903 					(void) fputs("\b \b", stdout);
904 				sptr = oldp;
905 			}
906 			(void) set_state(&state, ch, sptr);
907 			(void) fflush(stdout);
908 			continue;
909 		}
910 		else
911 		if (ch == killchar() && !slash) {
912 			if (hard_copy)
913 				(void) putwchar(ch);
914 			(void) resetterm();
915 			return (1);
916 		}
917 		if (ch < ' ')
918 			width = 2;
919 		else
920 			if ((width = wcwidth(ch)) <= 0)
921 				width = 0;
922 		if (slash) {
923 			slash = 0;
924 			(void) fputs("\b \b", stdout);
925 			sptr--;
926 			promptlen--;
927 		} else /* is there room to keep this character? */
928 		if (sptr >= cmdbuf + sizeof (cmdbuf) ||
929 		    promptlen + width >= columns) {
930 			BEEP();
931 			continue;
932 		}
933 		else
934 		if (ch == '\\')
935 			slash++;
936 		if (set_state(&state, ch, sptr) == 0) {
937 			BEEP();
938 			continue;
939 		}
940 		(void) strncpy(sptr, multic, (size_t)length);
941 		sptr += length;
942 		if (ch < ' ') {
943 			ch += 0100;
944 			multic[0] = '^';
945 			multic[1] = ch;
946 			length = 2;
947 		}
948 		p = multic;
949 		while (length--)
950 			(void) putchar(*p++);
951 		promptlen += width;
952 		(void) fflush(stdout);
953 	}
954 
955 	*sptr = '\0';
956 	kill_line();
957 	(void) fflush(stdout);
958 	(void) resetterm();
959 	return (0);
960 }
961 
962 static	int
963 set_state(pstate, c, pc)
964 int *pstate;
965 wchar_t c;
966 char *pc;
967 {
968 	static char *psign;
969 	static char *pnumber;
970 	static char *pcommand;
971 	static int slash;
972 
973 	if (*pstate == 0) {
974 		psign = (char *)NULL;
975 		pnumber = (char *)NULL;
976 		pcommand = (char *)NULL;
977 		*pstate = 1;
978 		slash = 0;
979 		return (1);
980 	}
981 	if (c == '\\' && !slash) {
982 		slash++;
983 		return (1);
984 	}
985 	if (c == erasechar() && !slash)
986 		switch (*pstate) {
987 		case 4:
988 			if (pc > pcommand)
989 				return (1);
990 			pcommand = (char *)NULL;
991 			/*FALLTHROUGH*/
992 
993 		case 3:
994 			if (pnumber && pc > pnumber) {
995 				*pstate = 3;
996 				return (1);
997 			}
998 			pnumber = (char *)NULL;
999 			/*FALLTHROUGH*/
1000 
1001 		case 2:
1002 			if (psign && pc > psign) {
1003 				*pstate = 2;
1004 				return (1);
1005 			}
1006 			psign = (char *)NULL;
1007 			/*FALLTHROUGH*/
1008 
1009 		case 1:
1010 			*pstate = 1;
1011 			return (1);
1012 		}
1013 
1014 	slash = 0;
1015 	switch (*pstate) {
1016 	case 1: /* before recieving anything interesting */
1017 		if (c == '\t' || (!nflag && c == ' '))
1018 			return (1);
1019 		if (c == '+' || c == '-') {
1020 			psign = pc;
1021 			*pstate = 2;
1022 			return (1);
1023 		}
1024 		/*FALLTHROUGH*/
1025 
1026 	case 2: /* recieved sign, waiting for digit */
1027 		if (iswascii(c) && isdigit(c)) {
1028 			pnumber = pc;
1029 			*pstate = 3;
1030 			return (1);
1031 		}
1032 		/*FALLTHROUGH*/
1033 
1034 	case 3: /* recieved digit, waiting for the rest of the number */
1035 		if (iswascii(c) && isdigit(c))
1036 			return (1);
1037 		if (iswascii(c) && pg_strchr("h\014.wz\004dqQfl np$", c)) {
1038 			pcommand = pc;
1039 			if (nflag)
1040 				*pstate = 10;
1041 			else
1042 				*pstate = 4;
1043 			return (1);
1044 		}
1045 		if (iswascii(c) && pg_strchr("s/^?!", c)) {
1046 			pcommand = pc;
1047 			*pstate = 4;
1048 			return (1);
1049 		}
1050 		return (0);
1051 	case 4:
1052 		return (1);
1053 	}
1054 	return (0);
1055 }
1056 
1057 static	int
1058 readch()
1059 {
1060 	return (fgetwc(pg_stdin));
1061 }
1062 
1063 static void
1064 help()
1065 {
1066 	if (clropt)
1067 		doclear();
1068 
1069 	(void) fputs(gettext(
1070 "-------------------------------------------------------\n"
1071 "  h                     help\n"
1072 "  q or Q                quit\n"
1073 "  <blank> or <newline>  next page\n"
1074 "  l                     next line\n"
1075 "  d or <^D>             display half a page more\n"
1076 "  . or <^L>             redisplay current page\n"
1077 "  f                     skip the next page forward\n"
1078 "  n                     next file\n"
1079 "  p                     previous file\n"
1080 "  $                     last page\n"
1081 "  w or z                set window size and display next page\n"
1082 "  s savefile            save current file in savefile\n"
1083 "  /pattern/             search forward for pattern\n"
1084 "  ?pattern? or\n"
1085 "  ^pattern^             search backward for pattern\n"
1086 "  !command              execute command\n"
1087 "\n"
1088 "Most commands can be preceeded by a number, as in:\n"
1089 "+1<newline> (next page); -1<newline> (previous page); 1<newline> (page 1).\n"
1090 "\n"
1091 "See the manual page for more detail.\n"
1092 "-------------------------------------------------------\n"),
1093 		stdout);
1094 }
1095 
1096 /*
1097  * Skip nskip files in the file list (from the command line). Nskip may be
1098  * negative.
1099  */
1100 
1101 static int
1102 skipf(nskip)
1103 int nskip;
1104 {
1105 	if (fnum + nskip < 0) {
1106 		nskip = -fnum;
1107 		if (nskip == 0)
1108 			error("No previous file");
1109 	}
1110 	else
1111 	if (fnum + nskip > nfiles - 1) {
1112 		nskip = (nfiles - 1) - fnum;
1113 		if (nskip == 0)
1114 			error("No next file");
1115 	}
1116 	return (nskip);
1117 }
1118 
1119 /*
1120  * Check whether the file named by fs is a file which the user may
1121  * access.  If it is, return the opened file. Otherwise return NULL.
1122  */
1123 
1124 static FILE *
1125 checkf(fs)
1126 char *fs;
1127 {
1128 	struct stat stbuf;
1129 	FILE *f;
1130 	int fd;
1131 	int f_was_opened;
1132 
1133 	pipe_in = 0;
1134 	if (strcmp(fs, "-") == 0) {
1135 		if (tmp_fin == NULL)
1136 			f = stdin;
1137 		else {
1138 			rewind(tmp_fin);
1139 			f = tmp_fin;
1140 		}
1141 		f_was_opened = 0;
1142 	} else {
1143 		if ((f = fopen(fs, "r")) == (FILE *)NULL) {
1144 			(void) fflush(stdout);
1145 			perror(fs);
1146 			return ((FILE *)NULL);
1147 		}
1148 		f_was_opened = 1;
1149 	}
1150 	if (fstat(fileno(f), &stbuf) == -1) {
1151 		if (f_was_opened)
1152 			(void) fclose(f);
1153 		(void) fflush(stdout);
1154 		perror(fs);
1155 		return ((FILE *)NULL);
1156 	}
1157 	if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
1158 		if (f_was_opened)
1159 			(void) fclose(f);
1160 		(void) fprintf(stderr, "pg: ");
1161 		(void) fprintf(stderr, gettext("%s is a directory\n"), fs);
1162 		return ((FILE *)NULL);
1163 	}
1164 	if ((stbuf.st_mode & S_IFMT) == S_IFREG) {
1165 		if (f == stdin)		/* It may have been read from */
1166 			rewind(f);	/* already, and not reopened  */
1167 	} else {
1168 		if (f != stdin) {
1169 			if (f_was_opened)
1170 				(void) fclose(f);
1171 			(void) fprintf(stderr, "pg: ");
1172 			(void) fprintf(stderr, gettext(
1173 			"special files only handled as standard input\n"));
1174 			return ((FILE *)NULL);
1175 		} else {
1176 			if ((fd = mkstemp(tmp_name)) < 0) {
1177 			    (void) perror(tmp_name);
1178 			    return ((FILE *)NULL);
1179 			}
1180 			(void) close(fd);
1181 			if ((tmp_fou = fopen(tmp_name, "w")) == NULL) {
1182 				(void) perror(tmp_name);
1183 				return ((FILE *)NULL);
1184 			}
1185 			if ((tmp_fin = fopen(tmp_name, "r")) == NULL) {
1186 				(void) perror(tmp_name);
1187 				return ((FILE *)NULL);
1188 			}
1189 			pipe_in = 1;
1190 		}
1191 	}
1192 	lineset(BOF);
1193 	return (f);
1194 }
1195 
1196 static void
1197 copy_file(f, out)
1198 FILE *f, *out;
1199 {
1200 	int c;
1201 
1202 	while ((c = getc(f)) != EOF)
1203 		(void) putc(c, out);
1204 
1205 }
1206 
1207 static void
1208 re_error(i)
1209 int i;
1210 {
1211 	int j;
1212 	static struct messages {
1213 		char *message;
1214 		int number;
1215 		} re_errmsg[] = {
1216 		"Pattern not found",				1,
1217 		"Range endpoint too large",			11,
1218 		"Bad number",					16,
1219 		"`\\digit' out of range",			25,
1220 		"No remembered search string",  		41,
1221 		"\\( \\) imbalance",				42,
1222 		"Too many \\(",					43,
1223 		"More than two numbers given in \\{ \\}",  	44,
1224 		"} expected after \\",				45,
1225 		"First number exceeds second in \\{ \\}",  	46,
1226 		"[] imbalance",					49,
1227 		"Regular expression overflow",			50,
1228 		"Illegal byte sequence",			67,
1229 		"Bad regular expression",			0
1230 		};
1231 
1232 	for (j = 0; re_errmsg[j].number != 0; j++)
1233 		if (re_errmsg[j].number == i)
1234 			break;
1235 	error(re_errmsg[j].message);
1236 	longjmp(restore, 1);  /* restore to search() */
1237 }
1238 
1239 /*
1240  * Search for nth ocurrence of regular expression contained in buf in the file
1241  *	negative n implies backward search
1242  *	n 'guaranteed' non-zero
1243  */
1244 
1245 
1246 static int
1247 search(buf, n)
1248 char buf[];
1249 off_t n;
1250 {
1251 	int direction;
1252 	static char *expbuf;
1253 	char *nexpbuf;
1254 	int END_COND;
1255 
1256 	if (setjmp(restore) <= 0) {
1257 		nexpbuf = compile(buf, (char *)0, (char *)0);
1258 		if (regerrno) {
1259 			if (regerrno != 41 || expbuf == NULL)
1260 				re_error(regerrno);
1261 		} else {
1262 			if (expbuf)
1263 				free(expbuf);
1264 			expbuf = nexpbuf;
1265 		}
1266 
1267 		if (n < 0) {	/* search back */
1268 			direction = -1;
1269 			(void) find(0, old_ss.first_line);
1270 			END_COND = BOF;
1271 		} else {
1272 			direction = 1;
1273 			(void) find(0, old_ss.last_line);
1274 			END_COND = EOF;
1275 		}
1276 
1277 		while (find(1, direction) != END_COND) {
1278 			if (brk_hit)
1279 				break;
1280 			if (step(Line, expbuf))
1281 				if ((n -= direction) == 0) {
1282 					switch (leave_search) {
1283 					case 't':
1284 						new_ss.first_line =
1285 						    find(1, (off_t)0);
1286 						new_ss.last_line =
1287 						    new_ss.first_line +
1288 						    (off_t)window
1289 						    - 1;
1290 						break;
1291 					case 'b':
1292 						new_ss.last_line =
1293 						    find(1, (off_t)0);
1294 						new_ss.first_line =
1295 						    new_ss.last_line -
1296 						    (off_t)window
1297 						    + 1;
1298 						break;
1299 					case 'm':
1300 						new_ss.first_line =
1301 						    find(1, (off_t)0) -
1302 						    ((off_t)window - 1)/2;
1303 						new_ss.last_line =
1304 						    new_ss.first_line +
1305 						    (off_t)window
1306 						    - 1;
1307 						break;
1308 					}
1309 					return (1);
1310 				}
1311 		}
1312 		re_error(1); /* Pattern not found */
1313 	}
1314 	BEEP();
1315 	return (0);
1316 }
1317 
1318 /*
1319  *	find -- find line in file f, subject to certain constraints.
1320  *
1321  *	This is the reason for all the funny stuff with sign and nlines.
1322  *	We need to be able to differentiate between relative and abosolute
1323  *	address specifications.
1324  *
1325  *	So...there are basically three cases that this routine
1326  *	handles. Either line is zero, which  means there is to be
1327  *	no motion (because line numbers start at one), or
1328  *	how and line specify a number, or line itself is negative,
1329  *	which is the same as having how == -1 and line == abs(line).
1330  *
1331  *	Then, figure where exactly it is that we are going (an absolute
1332  *	line number). Find out if it is within what we have read,
1333  *	if so, go there without further ado. Otherwise, do some
1334  *	magic to get there, saving all the intervening lines,
1335  *	in case the user wants to see them some time later.
1336  *
1337  *	In any case, return the line number that we end up at.
1338  *	(This is used by search() and screen()). If we go past EOF,
1339  *	return EOF.
1340  *	This EOF will go away eventually, as pg is expanded to
1341  *	handle multiple files as one huge one. Then EOF will
1342  *	mean we have run off the file list.
1343  *	If the requested line number is too far back, return BOF.
1344  */
1345 
1346 static off_t
1347 find(how, line)	/* find the line and seek there */
1348 int how;
1349 off_t line;
1350 {
1351 	/* no compacted memory yet */
1352 	FILE *f = in_file;
1353 	off_t where;
1354 
1355 	if (how == 0)
1356 		where = line;
1357 	else
1358 		if (dot == zero - 1)
1359 			where = how * line;
1360 		else
1361 			where = how * line + dot->l_no;
1362 
1363 	/* now, where is either at, before, or after dol */
1364 	/* most likely case is after, so do it first */
1365 
1366 	eoflag = 0;
1367 	if (where >= dol->l_no) {
1368 		if (doliseof) {
1369 			dot = dol;
1370 			eoflag++;
1371 			return (EOF);
1372 		}
1373 		if (pipe_in)
1374 			in_file = f = stdin;
1375 		else
1376 			(void) fseeko(f, (off_t)dol->l_addr, SEEK_SET);
1377 		dot = dol - 1;
1378 		while ((nchars = getline(f)) != EOF) {
1379 			dot++;
1380 			newdol(f);
1381 			if (where == dot->l_no || brk_hit)
1382 				break;
1383 		}
1384 		if (nchars != EOF)
1385 			return (dot->l_no);
1386 		else { /* EOF */
1387 			dot = dol;
1388 			eoflag++;
1389 			doliseof++;
1390 			eofl_no = dol->l_no;
1391 			return (EOF);
1392 		}
1393 	} else { /* where < dol->l_no */
1394 		if (pipe_in) {
1395 			(void) fflush(tmp_fou);
1396 			in_file = f = tmp_fin;
1397 		}
1398 		if (where < zero->l_no) {
1399 			(void) fseeko(f, (off_t)zero->l_addr, SEEK_SET);
1400 			dot = zero - 1;
1401 			return (BOF);
1402 		} else {
1403 			dot = zero + where - 1;
1404 			(void) fseeko(f, (off_t)dot->l_addr, SEEK_SET);
1405 			nchars = getline(f);
1406 			return (dot->l_no);
1407 		}
1408 	}
1409 }
1410 
1411 static FILE *fileptr;
1412 static int (*rdchar)();
1413 
1414 static int
1415 mrdchar()
1416 {
1417 	return (rdchar(fileptr));
1418 }
1419 
1420 /*
1421  * Get a logical line
1422  */
1423 
1424 static off_t
1425 getline(f)
1426 FILE *f;
1427 {
1428 	char	*p;
1429 	int	column;
1430 	static char multic[MB_LEN_MAX];
1431 	static int savlength;
1432 	wchar_t c;
1433 	int length, width;
1434 
1435 	if (pipe_in && f == stdin)
1436 		rdchar = fgetputc;
1437 	else
1438 		rdchar = (int (*)())fgetwc;
1439 
1440 	fileptr = f;
1441 	/* copy overlap from previous call to getline */
1442 	if (savlength)
1443 		(void) strncpy(Line, multic, (size_t)savlength);
1444 	for (column = 0, p = Line + savlength; ; ) {
1445 		if ((c = mrdchar()) <= 0) {
1446 			clearerr(f);
1447 			if (p > Line) {	/* last line doesn't have '\n', */
1448 				*p++ = '\n';
1449 				*p = '\0';	/* print it any way */
1450 				return (column);
1451 			}
1452 			return (EOF);
1453 		}
1454 		length = wctomb(multic, c);
1455 		if (length < 0) {
1456 			length = -length;
1457 			c = 0;
1458 		}
1459 		if ((width = wcwidth(c)) < 0)
1460 			width = 0;
1461 		if (column + width > columns && !fflag)
1462 			break;
1463 
1464 		if (p + length > &Line[LINSIZ - 2] && c != '\n')
1465 			break;
1466 		(void) strncpy(p, multic, (size_t)length);
1467 		p += length;
1468 		column += width;
1469 		/* don't have any overlap here */
1470 		length = 0;
1471 		switch (c) {
1472 		case '\t': /* just a guess */
1473 			column = 1 + (column | 7);
1474 			break;
1475 		case '\b':
1476 			if (column > 0)
1477 				column--;
1478 			break;
1479 		case '\r':
1480 			column = 0;
1481 			break;
1482 		}
1483 		if (c == '\n')
1484 			break;
1485 		if (column >= columns && !fflag)
1486 			break;
1487 	}
1488 	if (c != '\n') { /* We're stopping in the middle of the line */
1489 		if (column != columns || !auto_right_margin)
1490 			*p++ = '\n';	/* for the display */
1491 		/* save overlap for next call to getline */
1492 		savlength = length;
1493 		if (savlength == 0) {
1494 			/*
1495 			 * check if following byte is newline and get
1496 			 * it if it is
1497 			 */
1498 			c = fgetwc(f);
1499 			if (c == '\n') {
1500 				/* gobble and copy (if necessary) newline */
1501 				(void) ungetwc(c, f);
1502 				(void) (*rdchar)(f);
1503 			} else if (c == EOF)
1504 				clearerr(f);
1505 			else
1506 				(void) ungetwc(c, f);
1507 		}
1508 	} else
1509 		savlength = 0;
1510 	*p = 0;
1511 	return (column);
1512 }
1513 
1514 static void
1515 save_input(f)
1516 FILE *f;
1517 {
1518 	if (pipe_in) {
1519 		save_pipe();
1520 		in_file = tmp_fin;
1521 		pipe_in = 0;
1522 	}
1523 	(void) fseeko(in_file, (off_t)0, SEEK_SET);
1524 	copy_file(in_file, f);
1525 }
1526 
1527 static void
1528 save_pipe()
1529 {
1530 	if (!doliseof)
1531 		while (fgetputc(stdin) != EOF)
1532 			if (brk_hit) {
1533 				brk_hit = 0;
1534 				error("Piped input only partially saved");
1535 				break;
1536 			}
1537 	(void) fclose(tmp_fou);
1538 }
1539 
1540 static int
1541 fgetputc(f)	/* copy anything read from a pipe to tmp_fou */
1542 FILE *f;
1543 {
1544 	int c;
1545 
1546 	if ((c = fgetwc(f)) != EOF)
1547 		(void) fputwc(c, tmp_fou);
1548 	return (c);
1549 }
1550 
1551 static	void
1552 lineset(how)	/* initialize line memory */
1553 int how;
1554 {
1555 	if (zero == NULL) {
1556 		nlall = 128;
1557 		zero = (LINE *) malloc(nlall * sizeof (LINE));
1558 	}
1559 	dol = contig = zero;
1560 	zero->l_no = 1;
1561 	zero->l_addr = 0l;
1562 	if (how == BOF) {
1563 		dot = zero - 1;
1564 		eoflag = 0;
1565 		doliseof = 0;
1566 		eofl_no = -1;
1567 	} else {
1568 		dot = dol;
1569 		eoflag = 1;
1570 		doliseof = 1;
1571 		eofl_no = 1;
1572 	}
1573 }
1574 
1575 static void
1576 newdol(f)	/* add address of new 'dol' */
1577 		/* assumes that f is currently at beginning of said line */
1578 		/* updates dol */
1579 FILE *f;
1580 {
1581 	int diff;
1582 
1583 	if ((dol - zero) + 1 >= nlall) {
1584 		LINE *ozero = zero;
1585 
1586 		nlall += 512;
1587 		if ((zero = (LINE *)realloc((char *)zero,
1588 		    (unsigned)(nlall * sizeof (LINE)))) == NULL) {
1589 			zero = ozero;
1590 			compact();
1591 		}
1592 		diff = (int)((int)zero - (int)ozero);
1593 		dot = (LINE *)((int)dot + diff);
1594 		dol = (LINE *)((int)dol + diff);
1595 		contig = (LINE *)((int)contig + diff);
1596 	}
1597 	dol++;
1598 	if (!pipe_in)
1599 		dol->l_addr = (off_t)ftello(f);
1600 	else {
1601 		(void) fflush(tmp_fou);
1602 		dol->l_addr = (off_t)ftello(tmp_fou);
1603 	}
1604 	dol->l_no = (dol-1)->l_no + 1;
1605 }
1606 
1607 static void
1608 compact()
1609 {
1610 	(void) perror("realloc");
1611 	end_it();
1612 
1613 }
1614 
1615 static void
1616 terminit()	/* set up terminal dependencies from termlib */
1617 {
1618 	int err_ret;
1619 	struct termio ntty;
1620 
1621 	for (;;) {
1622 		gid_t my_tgid;
1623 		my_tgid = (gid_t)tcgetpgrp(1);
1624 		if (my_tgid < (gid_t)0 || my_tgid == my_pgid)
1625 			break;
1626 		(void) kill(-my_pgid, SIGTTOU);
1627 	}
1628 
1629 	if ((freopen("/dev/tty", "r+", stdout)) == NULL) {
1630 		(void) perror("open");
1631 		exit(1);
1632 	}
1633 	(void) ioctl(fileno(stdout), TCGETA, &otty);
1634 	termflg = 1;
1635 
1636 	(void) setupterm(0, fileno(stdout), &err_ret);
1637 	(void) ioctl(fileno(stdout), TCGETA, &ntty);
1638 	ntty.c_lflag &= ~(ECHONL | ECHO | ICANON);
1639 	ntty.c_cc[VMIN] = 1;
1640 	ntty.c_cc[VTIME] = 1;
1641 	(void) ioctl(fileno(stdout), TCSETAW, &ntty);
1642 	pg_stdin = fdopen(dup(fileno(stdout)), "r");
1643 	(void) saveterm();
1644 	(void) resetterm();
1645 	if (lines <= 0 || hard_copy) {
1646 		hard_copy = 1;
1647 		lines = 24;
1648 	}
1649 	if (columns <= 0)
1650 		columns = 80;
1651 	if (clropt && !clear_screen)
1652 		clropt = 0;
1653 	if ((shell = getenv("SHELL")) == (char *)NULL)
1654 			shell = "/usr/bin/sh";
1655 }
1656 
1657 static void
1658 error(mess)
1659 char *mess;
1660 {
1661 	kill_line();
1662 	sopr(gettext(mess), 1);
1663 	prompt((char *)NULL);
1664 	errors++;
1665 }
1666 
1667 static void
1668 prompt(filename)
1669 char *filename;
1670 {
1671 	char outstr[PROMPTSIZE+6];
1672 	int pagenum;
1673 	if (filename != NULL) {
1674 		/*
1675 		 * TRANSLATION_NOTE
1676 		 * 	%s is a filename.
1677 		 */
1678 		(void) sprintf(outstr, gettext("(Next file: %s)"), filename);
1679 	} else {
1680 		if ((pagenum = (int)((new_ss.last_line-2)/(window-1)+1))
1681 						> 999999)
1682 			pagenum = 999999;
1683 		(void) sprintf(outstr, promptstr, pagenum);
1684 	}
1685 	sopr(outstr, 1);
1686 	(void) fflush(stdout);
1687 }
1688 
1689 /*
1690  *  sopr puts out the message (please no \n's) surrounded by standout
1691  *  begins and ends
1692  */
1693 
1694 static void
1695 sopr(m, count)
1696 	char *m;
1697 	int count;
1698 {
1699 	wchar_t	wc;
1700 	int	len, n;
1701 	char	*p;
1702 
1703 	if (count) {
1704 		p = m;
1705 		for (; *p; p += len) {
1706 			if ((len = mbtowc(&wc, p, MB_CUR_MAX)) <= 0) {
1707 				len = 1;
1708 				continue;
1709 			}
1710 			if ((n = wcwidth(wc)) > 0)
1711 				promptlen += n;
1712 		}
1713 	}
1714 	if (soflag && enter_standout_mode && exit_standout_mode) {
1715 		(void) putp(enter_standout_mode);
1716 		(void) fputs(m, stdout);
1717 		(void) putp(exit_standout_mode);
1718 	}
1719 	else
1720 		(void) fputs(m, stdout);
1721 }
1722 
1723 static void
1724 doclear()
1725 {
1726 	if (clear_screen)
1727 		(void) putp(clear_screen);
1728 	(void) putchar('\r');  /* this resets the terminal drivers character */
1729 			/* count in case it is trying to expand tabs  */
1730 
1731 }
1732 
1733 static void
1734 kill_line()
1735 {
1736 	erase_line(0);
1737 	if (!clr_eol) (void) putchar('\r');
1738 
1739 }
1740 
1741 /* erase from after col to end of prompt */
1742 static void
1743 erase_line(col)
1744 int col;
1745 {
1746 
1747 	if (promptlen == 0)
1748 		return;
1749 	if (hard_copy)
1750 		(void) putchar('\n');
1751 	else {
1752 		if (col == 0)
1753 			(void) putchar('\r');
1754 		if (clr_eol) {
1755 			(void) putp(clr_eol);
1756 			/* for the terminal driver again */
1757 			(void) putchar('\r');
1758 		}
1759 		else
1760 			for (col = promptlen - col; col > 0; col--)
1761 				(void) putchar(' ');
1762 	}
1763 	promptlen = 0;
1764 }
1765 
1766 /*
1767  * Come here if a quit or interrupt signal is received
1768  */
1769 
1770 static void
1771 on_brk(sno)
1772 	int sno;	/* signal number generated */
1773 {
1774 	(void) signal(sno, on_brk);
1775 	if (!inwait) {
1776 		BEEP();
1777 		brk_hit = 1;
1778 	} else {
1779 		brk_hit = 0;
1780 		longjmp(restore, 1);
1781 	}
1782 }
1783 
1784 /*
1785  * Clean up terminal state and exit.
1786  */
1787 
1788 void
1789 end_it()
1790 {
1791 
1792 	if (out_is_tty) {
1793 		kill_line();
1794 		(void) resetterm();
1795 		if (termflg)
1796 			(void) ioctl(fileno(stdout), TCSETAW, &otty);
1797 	}
1798 	if (tmp_fin)
1799 		(void) fclose(tmp_fin);
1800 	if (tmp_fou)
1801 		(void) fclose(tmp_fou);
1802 	if (tmp_fou || tmp_fin)
1803 		(void) unlink(tmp_name);
1804 	exit(status);
1805 }
1806 
1807 void
1808 onsusp()
1809 {
1810 	int ttou_is_dfl;
1811 
1812 	/* ignore SIGTTOU so following resetterm and flush works */
1813 	ttou_is_dfl = (signal(SIGTTOU, SIG_IGN) == SIG_DFL);
1814 	(void) resetterm();
1815 	(void) fflush(stdout);
1816 	if (ttou_is_dfl)
1817 		(void) signal(SIGTTOU, SIG_DFL);
1818 
1819 	/* send SIGTSTP to stop this process group */
1820 	(void) signal(SIGTSTP, SIG_DFL);
1821 	(void) kill(-my_pgid, SIGTSTP);
1822 
1823 	/* continued - reset the terminal */
1824 #ifdef __STDC__
1825 	(void) signal(SIGTSTP, (void (*)(int))onsusp);
1826 #else
1827 	(void) signal(SIGTSTP, (void (*))onsusp);
1828 #endif
1829 	(void) resetterm();
1830 	if (inwait)
1831 		longjmp(restore, -1);
1832 
1833 }
1834 
1835 static char *
1836 pg_strchr(str, c)
1837 char	*str;
1838 wchar_t	c;
1839 {
1840 	while (*str) {
1841 		if (c == *str)
1842 			return (str);
1843 		str++;
1844 	}
1845 	return (0);
1846 }
1847 
1848 void
1849 usage()
1850 {
1851 	(void) fprintf(stderr, gettext(
1852 "Usage: pg [-number] [-p string] [-cefnrs] [+line] [+/pattern/] files\n"));
1853 	exit(1);
1854 }
1855