2 * pg - A clone of the System V CRT paging utility.
4 * Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * 4. Neither the name of Gunnar Ritter nor the names of his contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 /* Sccsid @(#)pg.c 1.44 (gritter) 2/8/02 - modified for util-linux */
35 * This command is deprecated. The utility is in maintenance mode,
36 * meaning we keep them in source tree for backward compatibility
37 * only. Do not waste time making this command better, unless the
38 * fix is about security or other very critical issue.
40 * See Documentation/deprecated.txt for more information.
43 #include <sys/types.h>
47 # include <sys/ioctl.h>
49 #include <sys/termios.h>
64 #elif defined(HAVE_NCURSES_NCURSES_H)
65 # include <ncurses/ncurses.h>
74 #include "closestream.h"
77 #define READBUF LINE_MAX /* size of input buffer */
78 #define CMDBUF 255 /* size of command buffer */
79 #define TABSIZE 8 /* spaces consumed by tab character */
81 #define cuc(c) ((c) & 0377)
83 enum { FORWARD
= 1, BACKWARD
= 2 }; /* search direction */
84 enum { TOP
, MIDDLE
, BOTTOM
}; /* position of matching line */
86 /* States for syntax-aware command line editor. */
100 char cmdline
[CMDBUF
];
104 char pattern
[CMDBUF
];
108 /* Position of file arguments on argv[] to main() */
115 void (*oldint
) (int); /* old SIGINT handler */
116 void (*oldquit
) (int); /* old SIGQUIT handler */
117 void (*oldterm
) (int); /* old SIGTERM handler */
118 char *tty
; /* result of ttyname(1) */
119 unsigned ontty
; /* whether running on tty device */
120 unsigned exitstatus
; /* exit status */
121 int pagelen
= 23; /* lines on a single screen page */
122 int ttycols
= 79; /* screen columns (starting at 0) */
123 struct termios otio
; /* old termios settings */
124 int tinfostat
= -1; /* terminfo routines initialized */
125 int searchdisplay
= TOP
; /* matching line position */
126 regex_t re
; /* regular expression to search for */
127 int remembered
; /* have a remembered search string */
128 int cflag
; /* clear screen before each page */
129 int eflag
; /* suppress (EOF) */
130 int fflag
; /* do not split lines */
131 int nflag
; /* no newline for commands required */
132 int rflag
; /* "restricted" pg */
133 int sflag
; /* use standout mode */
134 const char *pstring
= ":"; /* prompt string */
135 char *searchfor
; /* search pattern from argv[] */
136 int havepagelen
; /* page length is manually defined */
137 long startline
; /* start line from argv[] */
138 int nextfile
= 1; /* files to advance */
139 jmp_buf jmpenv
; /* jump from signal handlers */
140 int canjump
; /* jmpenv is valid */
141 wchar_t wbuf
[READBUF
]; /* used in several widechar routines */
144 const char *helpscreen
= N_("\
145 -------------------------------------------------------\n\
147 q or Q quit program\n\
148 <newline> next page\n\
149 f skip a page forward\n\
150 d or ^D next halfpage\n\
153 /regex/ search forward for regex\n\
154 ?regex? or ^regex^ search backward for regex\n\
155 . or ^L redraw screen\n\
156 w or z set page size and go to next page\n\
157 s filename save current file to filename\n\
158 !command shell escape\n\
159 p go to previous file\n\
162 Many commands accept preceding numbers, for example:\n\
163 +1<newline> (next page); -1<newline> (previous page); 1<newline> (first page).\n\
165 See pg(1) for more information.\n\
166 -------------------------------------------------------\n");
169 static int fseeko(FILE *f
, off_t off
, int whence
)
171 return fseek(f
, (long)off
, whence
);
174 static off_t
ftello(FILE *f
)
176 return (off_t
) ftell(f
);
180 #ifdef USE_SIGSET /* never defined */
181 /* sigset and sigrelse are obsolete - use when POSIX stuff is unavailable */
182 # define my_sigset sigset
183 # define my_sigrelse sigrelse
185 static int my_sigrelse(int sig
)
189 if (sigemptyset(&sigs
) || sigaddset(&sigs
, sig
))
191 return sigprocmask(SIG_UNBLOCK
, &sigs
, NULL
);
194 typedef void (*my_sighandler_t
) (int);
195 static my_sighandler_t
my_sigset(int sig
, my_sighandler_t disp
)
197 struct sigaction act
, oact
;
199 act
.sa_handler
= disp
;
200 if (sigemptyset(&act
.sa_mask
))
203 if (sigaction(sig
, &act
, &oact
))
205 if (my_sigrelse(sig
))
207 return oact
.sa_handler
;
209 #endif /* USE_SIGSET */
212 static void __attribute__((__noreturn__
)) quit(int status
)
214 exit(status
< 0100 ? status
: 077);
217 /* Usage message and similar routines. */
218 static void __attribute__((__noreturn__
)) usage(FILE *out
)
220 fputs(USAGE_HEADER
, out
);
222 _(" %s [options] [+line] [+/pattern/] [files]\n"),
223 program_invocation_short_name
);
225 fputs(USAGE_SEPARATOR
, out
);
226 fputs(_("Browse pagewise through text files.\n"), out
);
228 fputs(USAGE_OPTIONS
, out
);
229 fputs(_(" -number lines per page\n"), out
);
230 fputs(_(" -c clear screen before displaying\n"), out
);
231 fputs(_(" -e do not pause at end of a file\n"), out
);
232 fputs(_(" -f do not split long lines\n"), out
);
233 fputs(_(" -n terminate command with new line\n"), out
);
234 fputs(_(" -p <prompt> specify prompt\n"), out
);
235 fputs(_(" -r disallow shell escape\n"), out
);
236 fputs(_(" -s print messages to stdout\n"), out
);
237 fputs(_(" +number start at the given line\n"), out
);
238 fputs(_(" +/pattern/ start at the line containing pattern\n"), out
);
240 fputs(USAGE_SEPARATOR
, out
);
241 fputs(USAGE_HELP
, out
);
242 fputs(USAGE_VERSION
, out
);
244 fprintf(out
, USAGE_MAN_TAIL("pg(1)"));
245 quit(out
== stderr
? 2 : 0);
248 static void __attribute__((__noreturn__
)) needarg(const char *s
)
250 warnx(_("option requires an argument -- %s"), s
);
254 static void __attribute__((__noreturn__
)) invopt(const char *s
)
256 warnx(_("illegal option -- %s"), s
);
261 /* A mbstowcs()-alike function that transparently handles invalid
263 static size_t xmbstowcs(wchar_t * pwcs
, const char *s
, size_t nwcs
)
268 ignore_result(mbtowc(pwcs
, NULL
, MB_CUR_MAX
)); /* reset shift state */
270 if ((c
= mbtowc(pwcs
, s
, MB_CUR_MAX
)) < 0) {
280 ignore_result(mbtowc(pwcs
, NULL
, MB_CUR_MAX
));
285 /* Helper function for tputs(). */
286 static int outcap(int i
)
289 return write_all(STDOUT_FILENO
, &c
, 1) == 0 ? 1 : -1;
292 /* Write messages to terminal. */
293 static void mesg(const char *message
)
297 if (*message
!= '\n' && sflag
)
298 vidputs(A_STANDOUT
, outcap
);
299 write_all(STDOUT_FILENO
, message
, strlen(message
));
300 if (*message
!= '\n' && sflag
)
301 vidputs(A_NORMAL
, outcap
);
304 /* Get the window size. */
305 static void getwinsize(void)
307 static int initialized
, envlines
, envcols
, deflines
, defcols
;
309 struct winsize winsz
;
314 if (initialized
== 0) {
315 if ((p
= getenv("LINES")) != NULL
&& *p
!= '\0')
316 if ((envlines
= atoi(p
)) < 0)
318 if ((p
= getenv("COLUMNS")) != NULL
&& *p
!= '\0')
319 if ((envcols
= atoi(p
)) < 0)
321 /* terminfo values. */
322 if (tinfostat
!= 1 || columns
== 0)
326 if (tinfostat
!= 1 || lines
== 0)
333 badioctl
= ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &winsz
);
336 ttycols
= envcols
- 1;
339 ttycols
= winsz
.ws_col
- 1;
342 ttycols
= defcols
- 1;
343 if (havepagelen
== 0) {
345 pagelen
= envlines
- 1;
348 pagelen
= winsz
.ws_row
- 1;
351 pagelen
= deflines
- 1;
355 /* Message if skipping parts of files. */
356 static void skip(int direction
)
359 mesg(_("...skipping forward\n"));
361 mesg(_("...skipping backward\n"));
364 /* Signal handler while reading from input file. */
365 static void sighandler(int signum
)
367 if (canjump
&& (signum
== SIGINT
|| signum
== SIGQUIT
))
368 longjmp(jmpenv
, signum
);
369 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &otio
);
373 /* Check whether the requested file was specified on the command line. */
374 static int checkf(void)
376 if (files
.current
+ nextfile
>= files
.last
) {
377 mesg(_("No next file"));
380 if (files
.current
+ nextfile
< files
.first
) {
381 mesg(_("No previous file"));
388 /* Return the last character that will fit on the line at col columns in
389 * case MB_CUR_MAX > 1. */
390 static char *endline_for_mb(unsigned col
, char *s
)
398 if ((wl
= xmbstowcs(wbuf
, t
, sizeof wbuf
- 1)) == (size_t)-1)
401 while (*p
!= L
'\0') {
408 /* No cursor movement. */
420 pos
+= TABSIZE
- (pos
% TABSIZE
);
426 pos
+= wcwidth(L
'?');
431 else if (pos
> col
+ 1)
432 /* wcwidth() found a character that has
433 * multiple columns. What happens now?
434 * Assume the terminal will print the
435 * entire character onto the next row. */
448 if ((pos
= wcstombs(NULL
, p
, READBUF
)) == (size_t)-1)
452 #endif /* HAVE_WIDECHAR */
454 /* Return the last character that will fit on the line at col columns. */
455 static char *endline(unsigned col
, char *s
)
462 return endline_for_mb(col
, s
);
472 /* No cursor movement. */
484 pos
+= TABSIZE
- (pos
% TABSIZE
);
504 /* Clear the current line on the terminal's screen. */
505 static void cline(void)
507 char *buf
= xmalloc(ttycols
+ 2);
508 memset(buf
, ' ', ttycols
+ 2);
510 buf
[ttycols
+ 1] = '\r';
511 write_all(STDOUT_FILENO
, buf
, ttycols
+ 2);
515 /* Evaluate a command character's semantics. */
516 static int getstate(int c
)
563 tputs(bell
, STDOUT_FILENO
, outcap
);
569 /* Get the count and ignore last character of string. */
570 static int getcount(char *cmdstr
)
578 buf
= xmalloc(strlen(cmdstr
) + 1);
580 if (cmd
.key
!= '\0') {
581 if (cmd
.key
== '/' || cmd
.key
== '?' || cmd
.key
== '^') {
582 if ((p
= strchr(buf
, cmd
.key
)) != NULL
)
585 *(buf
+ strlen(buf
) - 1) = '\0';
591 if (buf
[0] == '-' && buf
[1] == '\0') {
603 /* Read what the user writes at the prompt. This is tricky because we
604 * check for valid input. */
605 static void prompt(long long pageno
)
611 char b
[LINE_MAX
], *p
;
614 if ((p
= strstr(pstring
, "%d")) == NULL
) {
618 sprintf(b
+ (p
- pstring
), "%lld", pageno
);
623 cmd
.key
= cmd
.addon
= cmd
.cmdline
[0] = '\0';
625 tcgetattr(STDOUT_FILENO
, &tio
);
626 tio
.c_lflag
&= ~(ICANON
| ECHO
);
629 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &tio
);
630 tcflush(STDOUT_FILENO
, TCIFLUSH
);
632 switch (read(STDOUT_FILENO
, &key
, 1)) {
639 if (key
== tio
.c_cc
[VERASE
]) {
641 write_all(STDOUT_FILENO
, "\b \b", 3);
642 cmd
.cmdline
[--cmd
.cmdlen
] = '\0';
656 if (cmd
.cmdline
[cmd
.cmdlen
- 1] == '\\') {
658 while (cmd
.cmdline
[cmd
.cmdlen
665 if (strchr(cmd
.cmdline
, cmd
.key
)
674 if (cmd
.cmdlen
== 0) {
680 if (key
== tio
.c_cc
[VKILL
]) {
683 cmd
.cmdline
[0] = '\0';
688 if (key
== '\n' || (nflag
&& state
== COUNT
&& key
== ' '))
690 if (cmd
.cmdlen
>= CMDBUF
- 1)
705 if (getstate(key
) != ADDON_FIN
)
714 searchdisplay
= MIDDLE
;
717 searchdisplay
= BOTTOM
;
725 state
= getstate(key
);
728 if (cmd
.cmdlen
!= 0) {
743 write_all(STDOUT_FILENO
, &key
, 1);
744 cmd
.cmdline
[cmd
.cmdlen
++] = key
;
745 cmd
.cmdline
[cmd
.cmdlen
] = '\0';
746 if (nflag
&& state
== CMD_FIN
)
750 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &otio
);
752 cmd
.count
= getcount(cmd
.cmdline
);
756 /* Remove backspace formatting, for searches in case MB_CUR_MAX > 1. */
757 static char *colb_for_mb(char *s
)
761 size_t l
= strlen(s
), wl
;
764 if ((wl
= xmbstowcs(wbuf
, p
, sizeof wbuf
)) == (size_t)-1)
766 for (wp
= wbuf
, wq
= wbuf
, i
= 0; *wp
!= L
'\0' && i
< wl
; wp
++, wq
++) {
777 wcstombs(s
, wp
, l
+ 1);
783 /* Remove backspace formatting, for searches. */
784 static char *colb(char *s
)
790 return colb_for_mb(s
);
793 for (q
= s
; *p
!= '\0'; p
++, q
++) {
808 /* Convert nonprintable characters to spaces in case MB_CUR_MAX > 1. */
809 static void makeprint_for_mb(char *s
, size_t l
)
815 if ((wl
= xmbstowcs(wbuf
, t
, sizeof wbuf
)) == (size_t)-1)
818 if (!iswprint(*wp
) && *wp
!= L
'\n' && *wp
!= L
'\r'
819 && *wp
!= L
'\b' && *wp
!= L
'\t')
828 /* Convert nonprintable characters to spaces. */
829 static void makeprint(char *s
, size_t l
)
832 if (MB_CUR_MAX
> 1) {
833 makeprint_for_mb(s
, l
);
839 if (!isprint(cuc(*s
)) && *s
!= '\n' && *s
!= '\r'
840 && *s
!= '\b' && *s
!= '\t')
846 /* Strip backslash characters from the given string. */
847 static void striprs(char *s
)
856 } while (*s
++ != '\0');
859 /* Extract the search pattern off the command line. */
860 static char *makepat(void)
864 if (cmd
.addon
== '\0')
865 p
= cmd
.cmdline
+ strlen(cmd
.cmdline
) - 1;
867 p
= cmd
.cmdline
+ strlen(cmd
.cmdline
) - 2;
872 if ((p
= strchr(cmd
.cmdline
, cmd
.key
)) != NULL
) {
879 /* Process errors that occurred in temporary file operations. */
880 static void __attribute__((__noreturn__
)) tmperr(FILE *f
, const char *ftype
)
883 warn(_("Read error from %s file"), ftype
);
885 /* Most likely '\0' in input. */
886 warnx(_("Unexpected EOF in %s file"), ftype
);
888 warn(_("Unknown error in %s file"), ftype
);
892 /* Read the file and respond to user input. Beware: long and ugly. */
893 static void pgfile(FILE *f
, const char *name
)
895 off_t pos
, oldpos
, fpos
;
896 /* These are the line counters:
897 * line the line desired to display
898 * fline the current line of the input file
899 * bline the current line of the file buffer
900 * oldline the line before a search was started
901 * eofline the last line of the file if it is already reached
902 * dline the line on the display */
903 off_t line
= 0, fline
= 0, bline
= 0, oldline
= 0, eofline
= 0;
906 unsigned searchcount
= 0;
907 /* Advance to EOF immediately. */
909 /* EOF has been reached by `line'. */
911 /* f and fbuf refer to the same file. */
918 /* fbuf an exact copy of the input file as it gets read
919 * find index table for input, one entry per line
920 * save for the s command, to save to a file */
921 FILE *fbuf
, *find
, *save
;
924 /* Just copy stdin to stdout. */
925 while ((sz
= fread(b
, sizeof *b
, READBUF
, f
)) != 0)
926 write_all(STDOUT_FILENO
, b
, sz
);
933 if ((fpos
= fseeko(f
, (off_t
)0, SEEK_SET
)) == -1)
940 if (fbuf
== NULL
|| find
== NULL
) {
941 warn(_("Cannot create tempfile"));
948 rerror
= regcomp(&re
, searchfor
, REG_NOSUB
| REG_NEWLINE
);
950 mesg(_("RE error: "));
951 regerror(rerror
, &re
, b
, READBUF
);
958 for (line
= startline
;;) {
959 /* Get a line from input file or buffer. */
961 fseeko(find
, line
* sizeof pos
, SEEK_SET
);
962 if (fread(&pos
, sizeof pos
, 1, find
) == 0)
963 tmperr(find
, "index");
964 fseeko(find
, (off_t
)0, SEEK_END
);
965 fseeko(fbuf
, pos
, SEEK_SET
);
966 if (fgets(b
, READBUF
, fbuf
) == NULL
)
967 tmperr(fbuf
, "buffer");
968 } else if (eofline
== 0) {
969 fseeko(find
, (off_t
)0, SEEK_END
);
972 fseeko(fbuf
, (off_t
)0, SEEK_END
);
974 if ((sig
= setjmp(jmpenv
)) != 0) {
975 /* We got a signal. */
978 fseeko(fbuf
, pos
, SEEK_SET
);
984 fseeko(f
, fpos
, SEEK_SET
);
986 p
= fgets(b
, READBUF
, f
);
988 if ((fpos
= ftello(f
)) == -1)
992 if (p
== NULL
|| *b
== '\0') {
1001 fwrite_all(&pos
, sizeof pos
, 1, find
);
1005 while (*(p
= endline(ttycols
,
1008 pos
= oldpos
+ (p
- b
);
1018 } while (line
> bline
++);
1023 if (search
== FORWARD
&& remembered
== 1) {
1026 search
= searchcount
= 0;
1027 mesg(_("Pattern not found"));
1033 if (regexec(&re
, b
, 0, NULL
, 0) == 0) {
1036 if (searchcount
== 0) {
1038 switch (searchdisplay
) {
1043 line
-= pagelen
/ 2 + 1;
1053 /* We are not searching. */
1055 } else if (*b
!= '\0') {
1056 if (cflag
&& clear_screen
) {
1059 tputs(clear_screen
, STDOUT_FILENO
,
1065 if (eofline
&& line
== eofline
)
1068 if ((sig
= setjmp(jmpenv
)) != 0) {
1069 /* We got a signal. */
1074 p
= endline(ttycols
, b
);
1078 write_all(STDOUT_FILENO
, b
, sz
);
1082 if (dline
>= pagelen
|| eof
) {
1083 /* Time for prompting! */
1084 if (eof
&& seekeof
) {
1086 if (line
>= pagelen
)
1095 if (fline
== 0 || eflag
)
1099 prompt((line
- 1) / pagelen
+ 1);
1102 /* Search forward. */
1105 searchcount
= cmd
.count
;
1107 if (p
!= NULL
&& *p
) {
1108 if (remembered
== 1)
1110 rerror
= regcomp(&re
, p
,
1114 mesg(_("RE error: "));
1115 sz
= regerror(rerror
, &re
,
1121 } else if (remembered
== 0) {
1122 mesg(_("No remembered search string"));
1128 /* Search backward. */
1131 searchcount
= cmd
.count
;
1133 if (p
!= NULL
&& *p
) {
1134 if (remembered
== 1)
1136 rerror
= regcomp(&re
, p
,
1140 mesg(_("RE error: "));
1141 regerror(rerror
, &re
,
1147 } else if (remembered
== 0) {
1148 mesg(_("No remembered search string"));
1155 fseeko(find
, --line
* sizeof pos
,
1157 if (fread(&pos
, sizeof pos
, 1, find
) ==
1159 tmperr(find
, "index");
1160 fseeko(find
, (off_t
)0, SEEK_END
);
1161 fseeko(fbuf
, pos
, SEEK_SET
);
1162 if (fgets(b
, READBUF
, fbuf
) == NULL
)
1163 tmperr(fbuf
, "buffer");
1165 if (regexec(&re
, b
, 0, NULL
, 0) == 0)
1167 if (searchcount
== 0)
1172 search
= searchcount
= 0;
1173 mesg(_("Pattern not found"));
1176 eof
= search
= dline
= 0;
1178 switch (searchdisplay
) {
1183 line
-= pagelen
/ 2;
1197 while (*++p
== ' ') ;
1200 save
= fopen(p
, "wb");
1203 mesg(_("cannot open "));
1206 mesg(strerror(cmd
.count
));
1209 /* Advance to EOF. */
1210 fseeko(find
, (off_t
)0, SEEK_END
);
1213 fseeko(fbuf
, (off_t
)0,
1216 if (fgets(b
, READBUF
, f
) == NULL
) {
1222 fwrite_all(&pos
, sizeof pos
, 1, find
);
1226 while (*(p
= endline(ttycols
,
1229 pos
= oldpos
+ (p
- b
);
1240 fseeko(fbuf
, (off_t
)0, SEEK_SET
);
1241 while ((sz
= fread(b
, sizeof *b
, READBUF
,
1243 /* No error check for compat. */
1244 fwrite_all(b
, sizeof *b
, sz
, save
);
1246 if (close_stream(save
) != 0) {
1248 mesg(_("write failed"));
1251 mesg(strerror(cmd
.count
));
1254 fseeko(fbuf
, (off_t
)0, SEEK_END
);
1259 if (*cmd
.cmdline
!= 'l')
1262 cmd
.count
= 1; /* compat */
1263 if (isdigit(cuc(*cmd
.cmdline
))) {
1264 line
= cmd
.count
- 2;
1267 if (cmd
.count
!= 1) {
1268 line
+= cmd
.count
- 1 - pagelen
;
1272 /* Nothing to do if (count == 1) */
1276 /* Half screen forward. */
1277 case '\004': /* ^D */
1278 if (*cmd
.cmdline
!= cmd
.key
)
1281 cmd
.count
= 1; /* compat */
1282 line
+= (cmd
.count
* pagelen
/ 2)
1290 cmd
.count
= 1; /* compat */
1291 line
+= cmd
.count
* pagelen
- 2;
1294 if (*cmd
.cmdline
!= 'f')
1298 if (eofline
&& line
>= eofline
)
1304 /* Just a number, or '-', or <newline>. */
1306 cmd
.count
= 1; /* compat */
1307 if (isdigit(cuc(*cmd
.cmdline
)))
1308 line
= (cmd
.count
- 1) * pagelen
- 2;
1310 line
+= (cmd
.count
- 1)
1311 * (pagelen
- 1) - 2;
1312 if (*cmd
.cmdline
!= '\0')
1314 if (cmd
.count
!= 1) {
1323 /* Advance to EOF. */
1332 case '\014': /* ^L */
1333 /* Repaint screen. */
1335 if (line
>= pagelen
)
1344 mesg(program_invocation_short_name
);
1345 mesg(_(": !command not allowed in "
1350 write_all(STDOUT_FILENO
, cmd
.cmdline
,
1351 strlen(cmd
.cmdline
));
1352 write_all(STDOUT_FILENO
, "\n", 1);
1353 my_sigset(SIGINT
, SIG_IGN
);
1354 my_sigset(SIGQUIT
, SIG_IGN
);
1355 switch (cpid
= fork()) {
1358 const char *sh
= getenv("SHELL");
1364 if (isatty(0) == 0) {
1366 open(tty
, O_RDONLY
);
1370 my_sigset(SIGINT
, oldint
);
1371 my_sigset(SIGQUIT
, oldquit
);
1372 my_sigset(SIGTERM
, oldterm
);
1374 cmd
.cmdline
+ 1, NULL
);
1375 warn(_("failed to execute %s"), sh
);
1380 mesg(_("fork() failed, "
1381 "try again later\n"));
1384 while (wait(NULL
) != cpid
) ;
1386 my_sigset(SIGINT
, sighandler
);
1387 my_sigset(SIGQUIT
, sighandler
);
1394 const char *help
= _(helpscreen
);
1395 write_all(STDOUT_FILENO
, copyright
,
1397 write_all(STDOUT_FILENO
, help
,
1405 nextfile
= cmd
.count
;
1413 /* Previous file. */
1416 nextfile
= 0 - cmd
.count
;
1430 /* Set window size. */
1433 if (*cmd
.cmdline
!= cmd
.key
)
1434 pagelen
= ++cmd
.count
;
1442 if (cflag
&& dline
== 1) {
1455 static int parse_arguments(int arg
, int argc
, char **argv
)
1460 files
.last
= arg
+ argc
- 1;
1461 for (; argv
[arg
]; arg
+= nextfile
) {
1463 files
.current
= arg
;
1465 static int firsttime
;
1467 if (firsttime
> 1) {
1468 mesg(_("(Next file: "));
1479 nextfile
= cmd
.count
;
1487 /* Previous file. */
1490 nextfile
= 0 - cmd
.count
;
1505 if (strcmp(argv
[arg
], "-") == 0)
1508 input
= fopen(argv
[arg
], "r");
1509 if (input
== NULL
) {
1510 warn("%s", argv
[arg
]);
1515 if (ontty
== 0 && argc
> 2) {
1516 /* Use the prefix as specified by SUSv2. */
1517 write_all(STDOUT_FILENO
, "::::::::::::::\n", 15);
1518 write_all(STDOUT_FILENO
, argv
[arg
], strlen(argv
[arg
]));
1519 write_all(STDOUT_FILENO
, "\n::::::::::::::\n", 16);
1521 pgfile(input
, argv
[arg
]);
1528 int main(int argc
, char **argv
)
1533 xasprintf(©right
,
1534 _("%s %s Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.\n"),
1535 program_invocation_short_name
, PACKAGE_VERSION
);
1537 setlocale(LC_ALL
, "");
1538 bindtextdomain(PACKAGE
, LOCALEDIR
);
1539 textdomain(PACKAGE
);
1540 atexit(close_stdout
);
1542 if (tcgetattr(STDOUT_FILENO
, &otio
) == 0) {
1544 oldint
= my_sigset(SIGINT
, sighandler
);
1545 oldquit
= my_sigset(SIGQUIT
, sighandler
);
1546 oldterm
= my_sigset(SIGTERM
, sighandler
);
1547 setlocale(LC_CTYPE
, "");
1548 setlocale(LC_COLLATE
, "");
1549 tty
= ttyname(STDOUT_FILENO
);
1550 setupterm(NULL
, STDOUT_FILENO
, &tinfostat
);
1552 helpscreen
= _(helpscreen
);
1554 for (arg
= 1; argv
[arg
]; arg
++) {
1555 if (*argv
[arg
] == '+')
1557 if (*argv
[arg
] != '-' || argv
[arg
][1] == '\0')
1561 if (!strcmp(argv
[arg
], "--help")) {
1565 if (!strcmp(argv
[arg
], "--version")) {
1566 printf(UTIL_LINUX_VERSION
);
1567 return EXIT_SUCCESS
;
1570 for (i
= 1; argv
[arg
][i
]; i
++) {
1571 switch (argv
[arg
][i
]) {
1573 if (i
!= 1 || argv
[arg
][i
+ 1])
1574 invopt(&argv
[arg
][i
]);
1586 pagelen
= strtol_or_err(argv
[arg
] + 1,
1587 _("failed to parse number of lines per page"));
1603 if (argv
[arg
][i
+ 1]) {
1604 pstring
= &argv
[arg
][i
+ 1];
1605 } else if (argv
[++arg
]) {
1607 pstring
= argv
[arg
];
1620 printf(UTIL_LINUX_VERSION
);
1621 return EXIT_SUCCESS
;
1623 invopt(&argv
[arg
][i
]);
1630 for (arg
= 1; argv
[arg
]; arg
++) {
1631 if (*argv
[arg
] == '-') {
1632 if (argv
[arg
][1] == '-') {
1636 if (argv
[arg
][1] == '\0')
1638 if (argv
[arg
][1] == 'p' && argv
[arg
][2] == '\0')
1642 if (*argv
[arg
] != '+')
1645 switch (*(argv
[arg
] + 1)) {
1659 startline
= strtol_or_err(argv
[arg
] + 1,
1660 _("failed to parse number of lines per page"));
1663 searchfor
= argv
[arg
] + 2;
1664 if (*searchfor
== '\0')
1666 p
= searchfor
+ strlen(searchfor
) - 1;
1669 if (*searchfor
== '\0')
1677 pgfile(stdin
, "stdin");
1679 exitstatus
= parse_arguments(arg
, argc
, argv
);