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