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