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