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.
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.
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]
23 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
32 #include <sys/types.h>
33 #include <sys/dirent.h>
41 #include <sys/termio.h>
47 #include <wctype.h> /* iswprint() */
56 * pg -- paginator for crt terminals
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.
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.
71 #define BOF (EOF - 1) /* Begining of File */
72 #define STOP (EOF - 2)
73 #define PROMPTSIZE 256
76 * Function definitions
78 static void lineset(int);
79 static char *setprompt();
80 static int set_state(int *, wchar_t, char *);
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 *);
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);
104 static int command(char *);
105 static int screen(char *);
106 static int fgetputc();
107 static char *pg_strchr();
110 struct line
{ /* how line addresses are stored */
111 off_t l_addr
; /* file offset */
112 off_t l_no
; /* line number in file */
115 typedef struct line LINE
;
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 */
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";
128 static short sign
; /* sign of command input */
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
;
135 static void on_brk(),
137 static short brk_hit
; /* interrupt handling is pending flag */
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 */
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 */
157 static char initbuf
[BUFSIZ
];
158 static wchar_t leave_search
= L
't';
159 /* where on the page to leave a found string */
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];
167 static int catch_susp
;
169 static void onsusp();
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 */
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
;
189 main(int argc
, char **argv
)
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 */
201 (void) textdomain(TEXT_DOMAIN
);
203 /* check for non-standard "-#" option */
204 for (i
= 1; i
< argc
; i
++) {
205 if (strcmp(argv
[i
], "--") == 0)
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"));
216 window
= (int)strtol(&argv
[i
][1], (char **)NULL
, 10);
219 argv
[i
] = argv
[i
+ 1];
227 /* check for non-standard + option */
228 for (i
= 1; i
< argc
; i
++) {
229 if (strcmp(argv
[i
], "--") == 0)
232 if (argv
[i
][0] == '+') {
233 if (argv
[i
][1] == '/') {
236 for (s
= &argv
[i
][2], p
= initbuf
; *s
!= '\0'; )
237 if (p
< initbuf
+ sizeof (initbuf
))
240 (void) fprintf(stderr
, gettext(
241 "pg: pattern too long\n"));
249 for (; isdigit(*s
); s
++)
250 initline
= initline
*10 + *s
-'0';
256 argv
[i
] = argv
[i
+ 1];
264 while ((opt
= getopt(argc
, argv
, "cefnrsp:")) != EOF
) {
283 rmode
= 1; /* restricted mode */
287 soflag
= 1; /* standout mode */
291 promptstr
= setprompt(optarg
);
299 nfiles
= argc
- optind
;
300 fnames
= &argv
[optind
];
302 (void) signal(SIGQUIT
, end_it
);
303 (void) signal(SIGINT
, end_it
);
304 out_is_tty
= isatty(1);
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
);
328 while (fnum
< nfiles
) {
329 if (strcmp(fnames
[fnum
], "") == 0)
331 if ((in_file
= checkf(fnames
[fnum
])) == NULL
) {
337 fnum
+= screen(fnames
[fnum
]);
340 (void) fputs("::::::::::::::\n",
342 (void) fputs(fnames
[fnum
], stdout
);
343 (void) fputs("\n::::::::::::::\n",
346 copy_file(in_file
, stdout
);
349 (void) fflush(stdout
);
353 if (in_file
!= tmp_fin
)
354 (void) fclose(in_file
);
368 static char pstr
[PROMPTSIZE
];
370 while (i
< PROMPTSIZE
- 2)
371 switch (pstr
[i
++] = *s
++) {
375 if (*s
== 'd' && !pct_d
) {
377 } else if (*s
!= '%')
379 if ((pstr
[i
++] = *s
++) == '\0')
385 (void) fprintf(stderr
, gettext("pg: prompt too long\n"));
392 * Print out the contents of the file f, one screenful at a time.
396 screen(char *file_name
)
403 old_ss
.first_line
= 0;
404 old_ss
.last_line
= 0;
407 cmd_ret
= command(file_name
);
412 new_ss
.first_line
= initline
;
413 new_ss
.last_line
= initline
+ (off_t
)window
- 1;
414 } else if (srchopt
) {
416 if (!search(initbuf
, (off_t
)1))
417 cmd_ret
= command(file_name
);
419 new_ss
.first_line
= 1;
420 new_ss
.last_line
= (off_t
)window
;
427 if (hadchance
&& new_ss
.last_line
>= eofl_no
)
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
)
437 new_ss
.last_line
= eofl_no
- 1;
438 new_ss
.first_line
= new_ss
.last_line
-
442 if (new_ss
.first_line
< 1)
443 new_ss
.first_line
= 1;
446 start
= new_ss
.first_line
;
448 if (new_ss
.first_line
== old_ss
.last_line
)
449 start
= new_ss
.first_line
+ 1;
451 if (new_ss
.first_line
> old_ss
.last_line
)
452 start
= new_ss
.first_line
;
454 if (old_ss
.first_line
< new_ss
.first_line
)
455 start
= old_ss
.last_line
+ 1;
457 start
= new_ss
.first_line
;
459 if (start
< old_ss
.first_line
)
460 sopr(gettext("...skipping backward\n"), 0);
462 if (start
> old_ss
.last_line
+ 1)
463 sopr(gettext("...skipping forward\n"), 0);
466 for (; start
<= new_ss
.last_line
; start
++) {
467 (void) find(0, start
);
468 (void) fputs(Line
, stdout
);
470 new_ss
.last_line
= find(1, 0);
477 (void) fflush(stdout
);
479 if (!eof_pause
|| eofl_no
== 1)
485 cmd_ret
= command(NULL
);
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; }
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.
501 command(char *filename
)
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.
520 (void) ioctl(1, TCSBRK
, 1);
521 if (setjmp(restore
) > 0)
531 (void) fflush(stdout
);
538 if ((len
= mbtowc(&wc
, cmdptr
, MB_CUR_MAX
)) <= 0) {
548 case '\014': /* ^L */
549 case '.': /* redisplay current window */
551 new_ss
.first_line
= old_ss
.first_line
;
552 new_ss
.last_line
= old_ss
.last_line
;
555 case 'w': /* set window size */
563 nlines
= (off_t
)window
;
566 window
= (int)nlines
;
571 new_ss
.first_line
= old_ss
.last_line
;
572 new_ss
.last_line
= new_ss
.first_line
+
576 case '\004': /* ^D */
581 new_ss
.last_line
= old_ss
.last_line
+
582 (off_t
)sign
*window
/2;
583 new_ss
.first_line
= new_ss
.last_line
-
589 * save input in filename.
590 * Check for filename, access, etc.
597 if (setjmp(restore
) > 0) {
600 char outstr
[PROMPTSIZE
];
601 if ((sf
= fopen(cmdptr
, "w")) == NULL
) {
602 error("cannot open save file");
606 (void) sprintf(outstr
, gettext(
607 "saving file %s"), cmdptr
);
609 (void) fflush(stdout
);
622 case 'f': /* skip forward screenfuls */
625 sign
++; /* skips are always relative */
628 nlines
= nlines
* (window
- 1);
630 new_ss
.first_line
= old_ss
.last_line
+ nlines
;
632 new_ss
.first_line
= old_ss
.first_line
- nlines
;
633 new_ss
.last_line
= new_ss
.first_line
+
637 case 'l': /* get a line */
646 new_ss
.last_line
= old_ss
.last_line
+ nlines
;
648 new_ss
.last_line
- (off_t
)window
+ 1;
650 case 0: /* leave addressed line at top */
651 new_ss
.first_line
= nlines
;
652 new_ss
.last_line
= nlines
+ (off_t
)window
- 1;
655 new_ss
.first_line
= old_ss
.first_line
- nlines
;
657 new_ss
.first_line
+ (off_t
)window
- 1;
662 case '\0': /* \n or blank */
668 nlines
= (nlines
- 1) * (window
- 1);
671 new_ss
.first_line
= old_ss
.last_line
+ nlines
;
673 new_ss
.first_line
+ (off_t
)window
- 1;
676 new_ss
.first_line
= nlines
+ 1;
677 new_ss
.last_line
= nlines
+ (off_t
)window
;
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.
687 if (new_ss
.first_line
== 1 &&
688 new_ss
.last_line
>= eofl_no
)
689 new_ss
.last_line
= eofl_no
- 1;
692 new_ss
.last_line
= old_ss
.first_line
- nlines
;
694 new_ss
.last_line
- (off_t
)window
+ 1;
699 case 'n': /* switch to next file in arglist */
705 if ((skip
= skipf(sign
*nlines
)) == 0) {
711 case 'p': /* switch to previous file in arglist */
717 if ((skip
= skipf(-sign
* nlines
)) == 0) {
723 case '$': /* go to end of file */
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
;
732 case '/': /* search forward for r.e. */
733 case '?': /* " backwards */
734 case '^': /* this ones a ? for regent100s */
742 cmdend
= cmdptr
+ (strlen(cmdptr
) - 1);
745 for (p
= cmdptr
; p
<= cmdend
; p
+= len
) {
747 if ((len
= mbtowc(&wc_e
, p
, MB_CUR_MAX
)) <= 0) {
753 if (cmdend
> cmdptr
+ 1) {
754 if ((wc_e1
== *cmdptr
) &&
756 (wc_e
== L
'm') || (wc_e
== L
'b'))) {
762 if ((cmdptr
< cmdend
) && (wc_e
== *cmdptr
))
764 if (*cmdptr
!= '/') /* signify back search by - */
766 if (!search(++cmdptr
, (off_t
)nlines
))
772 case '!': /* shell escape */
773 if (rmode
) { /* restricted mode */
774 (void) fprintf(stderr
, gettext(
775 "!command not allowed in restricted mode.\n"));
778 if (!hard_copy
) { /* redisplay the command */
779 (void) fputs(cmdbuf
, stdout
);
780 (void) fputs("\n", stdout
);
782 if ((id
= fork()) < 0) {
783 error("cannot fork, try again later");
786 if (id
== (pid_t
)0) {
788 * if stdin is a pipe, need to close it so
789 * that the terminal is really stdin for
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");
799 (void) signal(SIGINT
, SIG_IGN
);
800 (void) signal(SIGQUIT
, SIG_IGN
);
802 (void) signal(SIGTSTP
, SIG_DFL
);
803 while (wait(NULL
) != id
) {
809 (void) fputs("!\n", stdout
);
810 (void) fflush(stdout
);
811 (void) signal(SIGINT
, on_brk
);
812 (void) signal(SIGQUIT
, on_brk
);
814 (void) signal(SIGTSTP
, onsusp
);
843 i
= i
* 10 + *p
++ - '0';
856 char multic
[MB_LEN_MAX
];
860 /* initialize state processing */
861 (void) set_state(&state
, ' ', (char *)0);
863 while (state
!= 10) {
864 if ((ch
= readch()) < 0 || !iswascii(ch
) && !iswprint(ch
)) {
869 if ((length
= wctomb(multic
, ch
)) < 0)
873 if (ch
== '\n' && !slash
)
875 if (ch
== erasechar() && !slash
) {
882 len
= mbtowc(&wchar
, p
, MB_CUR_MAX
);
884 wchar
= (unsigned char)*p
;
889 if ((width
= wcwidth(wchar
)) <= 0)
890 /* ascii control character */
894 (void) fputs("\b \b", stdout
);
897 (void) set_state(&state
, ch
, sptr
);
898 (void) fflush(stdout
);
902 if (ch
== killchar() && !slash
) {
911 if ((width
= wcwidth(ch
)) <= 0)
915 (void) fputs("\b \b", stdout
);
918 } else /* is there room to keep this character? */
919 if (sptr
>= cmdbuf
+ sizeof (cmdbuf
) ||
920 promptlen
+ width
>= columns
) {
927 if (set_state(&state
, ch
, sptr
) == 0) {
931 (void) strncpy(sptr
, multic
, (size_t)length
);
941 (void) putchar(*p
++);
943 (void) fflush(stdout
);
948 (void) fflush(stdout
);
954 set_state(int *pstate
, wchar_t c
, char *pc
)
957 static char *pnumber
;
958 static char *pcommand
;
969 if (c
== '\\' && !slash
) {
973 if (c
== erasechar() && !slash
)
982 if (pnumber
&& pc
> pnumber
) {
990 if (psign
&& pc
> psign
) {
1004 case 1: /* before recieving anything interesting */
1005 if (c
== '\t' || (!nflag
&& c
== ' '))
1007 if (c
== '+' || c
== '-') {
1014 case 2: /* recieved sign, waiting for digit */
1015 if (iswascii(c
) && isdigit(c
)) {
1022 case 3: /* recieved digit, waiting for the rest of the number */
1023 if (iswascii(c
) && isdigit(c
))
1025 if (iswascii(c
) && pg_strchr("h\014.wz\004dqQfl np$", c
)) {
1033 if (iswascii(c
) && pg_strchr("s/^?!", c
)) {
1048 return (fgetwc(pg_stdin
));
1057 (void) fputs(gettext(
1058 "-------------------------------------------------------\n"
1061 " <blank> or <newline> next page\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"
1067 " p previous file\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"
1073 " ^pattern^ search backward for pattern\n"
1074 " !command execute command\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"
1079 "See the manual page for more detail.\n"
1080 "-------------------------------------------------------\n"),
1085 * Skip nskip files in the file list (from the command line). Nskip may be
1092 if (fnum
+ nskip
< 0) {
1095 error("No previous file");
1098 if (fnum
+ nskip
> nfiles
- 1) {
1099 nskip
= (nfiles
- 1) - fnum
;
1101 error("No next file");
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.
1120 if (strcmp(fs
, "-") == 0) {
1121 if (tmp_fin
== NULL
)
1129 if ((f
= fopen(fs
, "r")) == NULL
) {
1130 (void) fflush(stdout
);
1136 if (fstat(fileno(f
), &stbuf
) == -1) {
1139 (void) fflush(stdout
);
1143 if ((stbuf
.st_mode
& S_IFMT
) == S_IFDIR
) {
1146 (void) fprintf(stderr
, "pg: ");
1147 (void) fprintf(stderr
, gettext("%s is a directory\n"), fs
);
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 */
1157 (void) fprintf(stderr
, "pg: ");
1158 (void) fprintf(stderr
, gettext(
1159 "special files only handled as standard input\n"));
1162 if ((fd
= mkstemp(tmp_name
)) < 0) {
1163 (void) perror(tmp_name
);
1167 if ((tmp_fou
= fopen(tmp_name
, "w")) == NULL
) {
1168 (void) perror(tmp_name
);
1171 if ((tmp_fin
= fopen(tmp_name
, "r")) == NULL
) {
1172 (void) perror(tmp_name
);
1183 copy_file(FILE *f
, FILE *out
)
1187 while ((c
= getc(f
)) != EOF
)
1188 (void) putc(c
, out
);
1196 static struct messages
{
1200 "Pattern not found", 1,
1201 "Range endpoint too large", 11,
1203 "`\\digit' out of range", 25,
1204 "No remembered search string", 41,
1205 "\\( \\) imbalance", 42,
1207 "More than two numbers given in \\{ \\}", 44,
1208 "} expected after \\", 45,
1209 "First number exceeds second in \\{ \\}", 46,
1211 "Regular expression overflow", 50,
1212 "Illegal byte sequence", 67,
1213 "Bad regular expression", 0
1216 for (j
= 0; re_errmsg
[j
].number
!= 0; j
++)
1217 if (re_errmsg
[j
].number
== i
)
1219 error(re_errmsg
[j
].message
);
1220 longjmp(restore
, 1); /* restore to search() */
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
1229 search(char *buf
, off_t n
)
1232 static char *expbuf
;
1236 if (setjmp(restore
) <= 0) {
1237 nexpbuf
= compile(buf
, (char *)0, (char *)0);
1239 if (regerrno
!= 41 || expbuf
== NULL
)
1247 if (n
< 0) { /* search back */
1249 (void) find(0, old_ss
.first_line
);
1253 (void) find(0, old_ss
.last_line
);
1257 while (find(1, direction
) != END_COND
) {
1260 if (step(Line
, expbuf
))
1261 if ((n
-= direction
) == 0) {
1262 switch (leave_search
) {
1282 ((off_t
)window
- 1)/2;
1292 re_error(1); /* Pattern not found */
1299 * find -- find line in file f, subject to certain constraints.
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.
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).
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.
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,
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.
1327 find(int how
, off_t line
)
1329 /* no compacted memory yet */
1336 if (dot
== zero
- 1)
1339 where
= how
* line
+ dot
->l_no
;
1341 /* now, where is either at, before, or after dol */
1342 /* most likely case is after, so do it first */
1345 if (where
>= dol
->l_no
) {
1352 in_file
= f
= stdin
;
1354 (void) fseeko(f
, (off_t
)dol
->l_addr
, SEEK_SET
);
1356 while ((nchars
= getaline(f
)) != EOF
) {
1359 if (where
== dot
->l_no
|| brk_hit
)
1368 eofl_no
= dol
->l_no
;
1371 } else { /* where < dol->l_no */
1373 (void) fflush(tmp_fou
);
1374 in_file
= f
= tmp_fin
;
1376 if (where
< zero
->l_no
) {
1377 (void) fseeko(f
, (off_t
)zero
->l_addr
, SEEK_SET
);
1381 dot
= zero
+ where
- 1;
1382 (void) fseeko(f
, (off_t
)dot
->l_addr
, SEEK_SET
);
1383 nchars
= getaline(f
);
1389 static FILE *fileptr
;
1390 static int (*rdchar
)();
1395 return (rdchar(fileptr
));
1399 * Get a logical line
1406 static char multic
[MB_LEN_MAX
];
1407 static int savlength
;
1411 if (pipe_in
&& f
== stdin
)
1414 rdchar
= (int (*)())fgetwc
;
1417 /* copy overlap from previous call to getaline */
1419 (void) strncpy(Line
, multic
, (size_t)savlength
);
1420 for (column
= 0, p
= Line
+ savlength
; ; ) {
1421 if ((c
= mrdchar()) <= 0) {
1423 if (p
> Line
) { /* last line doesn't have '\n', */
1425 *p
= '\0'; /* print it any way */
1430 length
= wctomb(multic
, c
);
1435 if ((width
= wcwidth(c
)) < 0)
1437 if (column
+ width
> columns
&& !fflag
)
1440 if (p
+ length
> &Line
[LINSIZ
- 2] && c
!= '\n')
1442 (void) strncpy(p
, multic
, (size_t)length
);
1445 /* don't have any overlap here */
1448 case '\t': /* just a guess */
1449 column
= 1 + (column
| 7);
1461 if (column
>= columns
&& !fflag
)
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 */
1469 if (savlength
== 0) {
1471 * check if following byte is newline and get
1476 /* gobble and copy (if necessary) newline */
1477 (void) ungetwc(c
, f
);
1478 (void) (*rdchar
)(f
);
1479 } else if (c
== EOF
)
1482 (void) ungetwc(c
, f
);
1498 (void) fseeko(in_file
, (off_t
)0, SEEK_SET
);
1499 copy_file(in_file
, f
);
1506 while (fgetputc(stdin
) != EOF
)
1509 error("Piped input only partially saved");
1512 (void) fclose(tmp_fou
);
1516 * copy anything read from a pipe to tmp_fou
1523 if ((c
= fgetwc(f
)) != EOF
)
1524 (void) fputwc(c
, tmp_fou
);
1529 * initialize line memory
1536 zero
= (LINE
*) malloc(nlall
* sizeof (LINE
));
1538 dol
= contig
= zero
;
1555 * add address of new 'dol'
1556 * assumes that f is currently at beginning of said line
1564 if ((dol
- zero
) + 1 >= nlall
) {
1568 if ((zero
= (LINE
*)realloc((char *)zero
,
1569 (unsigned)(nlall
* sizeof (LINE
)))) == NULL
) {
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
);
1580 dol
->l_addr
= (off_t
)ftello(f
);
1582 (void) fflush(tmp_fou
);
1583 dol
->l_addr
= (off_t
)ftello(tmp_fou
);
1585 dol
->l_no
= (dol
-1)->l_no
+ 1;
1591 (void) perror("realloc");
1597 terminit(void) /* set up terminal dependencies from termlib */
1604 my_tgid
= tcgetpgrp(1);
1605 if (my_tgid
== -1 || my_tgid
== my_pgid
)
1607 (void) kill(-my_pgid
, SIGTTOU
);
1610 if ((freopen("/dev/tty", "r+", stdout
)) == NULL
) {
1611 (void) perror("open");
1614 (void) ioctl(fileno(stdout
), TCGETA
, &otty
);
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");
1626 if (lines
<= 0 || hard_copy
) {
1632 if (clropt
&& !clear_screen
)
1634 if ((shell
= getenv("SHELL")) == NULL
)
1635 shell
= "/usr/bin/sh";
1642 sopr(gettext(mess
), 1);
1648 prompt(char *filename
)
1650 char outstr
[PROMPTSIZE
+6];
1652 if (filename
!= NULL
) {
1657 (void) sprintf(outstr
, gettext("(Next file: %s)"), filename
);
1659 if ((pagenum
= (int)((new_ss
.last_line
-2)/(window
-1)+1)) >
1663 (void) sprintf(outstr
, promptstr
, pagenum
);
1666 (void) fflush(stdout
);
1670 * sopr puts out the message (please no \n's) surrounded by standout
1675 sopr(char *m
, int count
)
1683 for (; *p
; p
+= len
) {
1684 if ((len
= mbtowc(&wc
, p
, MB_CUR_MAX
)) <= 0) {
1688 if ((n
= wcwidth(wc
)) > 0)
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
);
1698 (void) fputs(m
, stdout
);
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 */
1715 if (!clr_eol
) (void) putchar('\r');
1719 /* erase from after col to end of prompt */
1727 (void) putchar('\n');
1730 (void) putchar('\r');
1732 (void) putp(clr_eol
);
1733 /* for the terminal driver again */
1734 (void) putchar('\r');
1737 for (col
= promptlen
- col
; col
> 0; col
--)
1738 (void) putchar(' ');
1744 * Come here if a quit or interrupt signal is received
1750 (void) signal(sno
, on_brk
);
1756 longjmp(restore
, 1);
1761 * Clean up terminal state and exit.
1772 (void) ioctl(fileno(stdout
), TCSETAW
, &otty
);
1775 (void) fclose(tmp_fin
);
1777 (void) fclose(tmp_fou
);
1778 if (tmp_fou
|| tmp_fin
)
1779 (void) unlink(tmp_name
);
1788 /* ignore SIGTTOU so following resetterm and flush works */
1789 ttou_is_dfl
= (signal(SIGTTOU
, SIG_IGN
) == SIG_DFL
);
1791 (void) fflush(stdout
);
1793 (void) signal(SIGTTOU
, SIG_DFL
);
1795 /* send SIGTSTP to stop this process group */
1796 (void) signal(SIGTSTP
, SIG_DFL
);
1797 (void) kill(-my_pgid
, SIGTSTP
);
1799 /* continued - reset the terminal */
1801 (void) signal(SIGTSTP
, (void (*)(int))onsusp
);
1803 (void) signal(SIGTSTP
, (void (*))onsusp
);
1807 longjmp(restore
, -1);
1812 pg_strchr(char *str
, wchar_t c
)
1825 (void) fprintf(stderr
, gettext(
1826 "Usage: pg [-number] [-p string] [-cefnrs] [+line] [+/pattern/] files\n"));