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>
65 #elif defined(HAVE_NCURSES_NCURSES_H)
66 # include <ncurses/ncurses.h>
75 #include "closestream.h"
78 #define READBUF LINE_MAX /* size of input buffer */
79 #define CMDBUF 255 /* size of command buffer */
80 #define TABSIZE 8 /* spaces consumed by tab character */
82 #define cuc(c) ((c) & 0377)
84 enum { FORWARD
= 1, BACKWARD
= 2 }; /* search direction */
85 enum { TOP
, MIDDLE
, BOTTOM
}; /* position of matching line */
87 /* States for syntax-aware command line editor. */
101 char cmdline
[CMDBUF
];
105 char pattern
[CMDBUF
];
109 /* Position of file arguments on argv[] to main() */
116 void (*oldint
) (int); /* old SIGINT handler */
117 void (*oldquit
) (int); /* old SIGQUIT handler */
118 void (*oldterm
) (int); /* old SIGTERM handler */
119 char *tty
; /* result of ttyname(1) */
120 unsigned ontty
; /* whether running on tty device */
121 unsigned exitstatus
; /* exit status */
122 int pagelen
= 23; /* lines on a single screen page */
123 int ttycols
= 79; /* screen columns (starting at 0) */
124 struct termios otio
; /* old termios settings */
125 int tinfostat
= -1; /* terminfo routines initialized */
126 int searchdisplay
= TOP
; /* matching line position */
127 regex_t re
; /* regular expression to search for */
128 int remembered
; /* have a remembered search string */
129 int cflag
; /* clear screen before each page */
130 int eflag
; /* suppress (EOF) */
131 int fflag
; /* do not split lines */
132 int nflag
; /* no newline for commands required */
133 int rflag
; /* "restricted" pg */
134 int sflag
; /* use standout mode */
135 const char *pstring
= ":"; /* prompt string */
136 char *searchfor
; /* search pattern from argv[] */
137 int havepagelen
; /* page length is manually defined */
138 long startline
; /* start line from argv[] */
139 int nextfile
= 1; /* files to advance */
140 jmp_buf jmpenv
; /* jump from signal handlers */
141 int canjump
; /* jmpenv is valid */
142 wchar_t wbuf
[READBUF
]; /* used in several widechar routines */
145 const char *helpscreen
= N_("\
146 -------------------------------------------------------\n\
148 q or Q quit program\n\
149 <newline> next page\n\
150 f skip a page forward\n\
151 d or ^D next halfpage\n\
154 /regex/ search forward for regex\n\
155 ?regex? or ^regex^ search backward for regex\n\
156 . or ^L redraw screen\n\
157 w or z set page size and go to next page\n\
158 s filename save current file to filename\n\
159 !command shell escape\n\
160 p go to previous file\n\
163 Many commands accept preceding numbers, for example:\n\
164 +1<newline> (next page); -1<newline> (previous page); 1<newline> (first page).\n\
166 See pg(1) for more information.\n\
167 -------------------------------------------------------\n");
170 static int fseeko(FILE *f
, off_t off
, int whence
)
172 return fseek(f
, (long)off
, whence
);
175 static off_t
ftello(FILE *f
)
177 return (off_t
) ftell(f
);
181 #ifdef USE_SIGSET /* never defined */
182 /* sigset and sigrelse are obsolete - use when POSIX stuff is unavailable */
183 # define my_sigset sigset
184 # define my_sigrelse sigrelse
186 static int my_sigrelse(int sig
)
190 if (sigemptyset(&sigs
) || sigaddset(&sigs
, sig
))
192 return sigprocmask(SIG_UNBLOCK
, &sigs
, NULL
);
195 typedef void (*my_sighandler_t
) (int);
196 static my_sighandler_t
my_sigset(int sig
, my_sighandler_t disp
)
198 struct sigaction act
, oact
;
200 act
.sa_handler
= disp
;
201 if (sigemptyset(&act
.sa_mask
))
204 if (sigaction(sig
, &act
, &oact
))
206 if (my_sigrelse(sig
))
208 return oact
.sa_handler
;
210 #endif /* USE_SIGSET */
213 static void __attribute__((__noreturn__
)) quit(int status
)
215 exit(status
< 0100 ? status
: 077);
218 /* Usage message and similar routines. */
219 static void __attribute__((__noreturn__
)) usage(FILE *out
)
221 fputs(USAGE_HEADER
, out
);
223 _(" %s [options] [+line] [+/pattern/] [files]\n"),
224 program_invocation_short_name
);
225 fputs(USAGE_OPTIONS
, out
);
226 fputs(_(" -number lines per page\n"), out
);
227 fputs(_(" -c clear screen before displaying\n"), out
);
228 fputs(_(" -e do not pause at end of a file\n"), out
);
229 fputs(_(" -f do not split long lines\n"), out
);
230 fputs(_(" -n terminate command with new line\n"), out
);
231 fputs(_(" -p <prompt> specify prompt\n"), out
);
232 fputs(_(" -r disallow shell escape\n"), out
);
233 fputs(_(" -s print messages to stdout\n"), out
);
234 fputs(_(" +number start at the given line\n"), out
);
235 fputs(_(" +/pattern/ start at the line containing pattern\n"), out
);
237 fputs(USAGE_SEPARATOR
, out
);
238 fputs(USAGE_HELP
, out
);
239 fputs(USAGE_VERSION
, out
);
241 fprintf(out
, USAGE_MAN_TAIL("pg(1)"));
242 quit(out
== stderr
? 2 : 0);
245 static void __attribute__((__noreturn__
)) needarg(const char *s
)
247 warnx(_("option requires an argument -- %s"), s
);
251 static void __attribute__((__noreturn__
)) invopt(const char *s
)
253 warnx(_("illegal option -- %s"), s
);
258 /* A mbstowcs()-alike function that transparently handles invalid
260 static size_t xmbstowcs(wchar_t * pwcs
, const char *s
, size_t nwcs
)
265 ignore_result(mbtowc(pwcs
, NULL
, MB_CUR_MAX
)); /* reset shift state */
267 if ((c
= mbtowc(pwcs
, s
, MB_CUR_MAX
)) < 0) {
277 ignore_result(mbtowc(pwcs
, NULL
, MB_CUR_MAX
));
282 /* Helper function for tputs(). */
283 static int outcap(int i
)
286 return write_all(STDOUT_FILENO
, &c
, 1) == 0 ? 1 : -1;
289 /* Write messages to terminal. */
290 static void mesg(const char *message
)
294 if (*message
!= '\n' && sflag
)
295 vidputs(A_STANDOUT
, outcap
);
296 write_all(STDOUT_FILENO
, message
, strlen(message
));
297 if (*message
!= '\n' && sflag
)
298 vidputs(A_NORMAL
, outcap
);
301 /* Get the window size. */
302 static void getwinsize(void)
304 static int initialized
, envlines
, envcols
, deflines
, defcols
;
306 struct winsize winsz
;
311 if (initialized
== 0) {
312 if ((p
= getenv("LINES")) != NULL
&& *p
!= '\0')
313 if ((envlines
= atoi(p
)) < 0)
315 if ((p
= getenv("COLUMNS")) != NULL
&& *p
!= '\0')
316 if ((envcols
= atoi(p
)) < 0)
318 /* terminfo values. */
319 if (tinfostat
!= 1 || columns
== 0)
323 if (tinfostat
!= 1 || lines
== 0)
330 badioctl
= ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &winsz
);
333 ttycols
= envcols
- 1;
336 ttycols
= winsz
.ws_col
- 1;
339 ttycols
= defcols
- 1;
340 if (havepagelen
== 0) {
342 pagelen
= envlines
- 1;
345 pagelen
= winsz
.ws_row
- 1;
348 pagelen
= deflines
- 1;
352 /* Message if skipping parts of files. */
353 static void skip(int direction
)
356 mesg(_("...skipping forward\n"));
358 mesg(_("...skipping backward\n"));
361 /* Signal handler while reading from input file. */
362 static void sighandler(int signum
)
364 if (canjump
&& (signum
== SIGINT
|| signum
== SIGQUIT
))
365 longjmp(jmpenv
, signum
);
366 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &otio
);
370 /* Check whether the requested file was specified on the command line. */
371 static int checkf(void)
373 if (files
.current
+ nextfile
>= files
.last
) {
374 mesg(_("No next file"));
377 if (files
.current
+ nextfile
< files
.first
) {
378 mesg(_("No previous file"));
385 /* Return the last character that will fit on the line at col columns in
386 * case MB_CUR_MAX > 1. */
387 static char *endline_for_mb(unsigned col
, char *s
)
395 if ((wl
= xmbstowcs(wbuf
, t
, sizeof wbuf
- 1)) == (size_t)-1)
398 while (*p
!= L
'\0') {
405 /* No cursor movement. */
417 pos
+= TABSIZE
- (pos
% TABSIZE
);
423 pos
+= wcwidth(L
'?');
428 else if (pos
> col
+ 1)
429 /* wcwidth() found a character that has
430 * multiple columns. What happens now?
431 * Assume the terminal will print the
432 * entire character onto the next row. */
445 if ((pos
= wcstombs(NULL
, p
, READBUF
)) == (size_t)-1)
449 #endif /* HAVE_WIDECHAR */
451 /* Return the last character that will fit on the line at col columns. */
452 static char *endline(unsigned col
, char *s
)
459 return endline_for_mb(col
, s
);
469 /* No cursor movement. */
481 pos
+= TABSIZE
- (pos
% TABSIZE
);
501 /* Clear the current line on the terminal's screen. */
502 static void cline(void)
504 char *buf
= xmalloc(ttycols
+ 2);
505 memset(buf
, ' ', ttycols
+ 2);
507 buf
[ttycols
+ 1] = '\r';
508 write_all(STDOUT_FILENO
, buf
, ttycols
+ 2);
512 /* Evaluate a command character's semantics. */
513 static int getstate(int c
)
560 tputs(bell
, STDOUT_FILENO
, outcap
);
566 /* Get the count and ignore last character of string. */
567 static int getcount(char *cmdstr
)
575 buf
= xmalloc(strlen(cmdstr
) + 1);
577 if (cmd
.key
!= '\0') {
578 if (cmd
.key
== '/' || cmd
.key
== '?' || cmd
.key
== '^') {
579 if ((p
= strchr(buf
, cmd
.key
)) != NULL
)
582 *(buf
+ strlen(buf
) - 1) = '\0';
588 if (buf
[0] == '-' && buf
[1] == '\0') {
600 /* Read what the user writes at the prompt. This is tricky because we
601 * check for valid input. */
602 static void prompt(long long pageno
)
608 char b
[LINE_MAX
], *p
;
611 if ((p
= strstr(pstring
, "%d")) == NULL
) {
615 sprintf(b
+ (p
- pstring
), "%lld", pageno
);
620 cmd
.key
= cmd
.addon
= cmd
.cmdline
[0] = '\0';
622 tcgetattr(STDOUT_FILENO
, &tio
);
623 tio
.c_lflag
&= ~(ICANON
| ECHO
);
626 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &tio
);
627 tcflush(STDOUT_FILENO
, TCIFLUSH
);
629 switch (read(STDOUT_FILENO
, &key
, 1)) {
636 if (key
== tio
.c_cc
[VERASE
]) {
638 write_all(STDOUT_FILENO
, "\b \b", 3);
639 cmd
.cmdline
[--cmd
.cmdlen
] = '\0';
653 if (cmd
.cmdline
[cmd
.cmdlen
- 1] == '\\') {
655 while (cmd
.cmdline
[cmd
.cmdlen
662 if (strchr(cmd
.cmdline
, cmd
.key
)
671 if (cmd
.cmdlen
== 0) {
677 if (key
== tio
.c_cc
[VKILL
]) {
680 cmd
.cmdline
[0] = '\0';
685 if (key
== '\n' || (nflag
&& state
== COUNT
&& key
== ' '))
687 if (cmd
.cmdlen
>= CMDBUF
- 1)
702 if (getstate(key
) != ADDON_FIN
)
711 searchdisplay
= MIDDLE
;
714 searchdisplay
= BOTTOM
;
722 state
= getstate(key
);
725 if (cmd
.cmdlen
!= 0) {
740 write_all(STDOUT_FILENO
, &key
, 1);
741 cmd
.cmdline
[cmd
.cmdlen
++] = key
;
742 cmd
.cmdline
[cmd
.cmdlen
] = '\0';
743 if (nflag
&& state
== CMD_FIN
)
747 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &otio
);
749 cmd
.count
= getcount(cmd
.cmdline
);
753 /* Remove backspace formatting, for searches in case MB_CUR_MAX > 1. */
754 static char *colb_for_mb(char *s
)
758 size_t l
= strlen(s
), wl
;
761 if ((wl
= xmbstowcs(wbuf
, p
, sizeof wbuf
)) == (size_t)-1)
763 for (wp
= wbuf
, wq
= wbuf
, i
= 0; *wp
!= L
'\0' && i
< wl
; wp
++, wq
++) {
774 wcstombs(s
, wp
, l
+ 1);
780 /* Remove backspace formatting, for searches. */
781 static char *colb(char *s
)
787 return colb_for_mb(s
);
790 for (q
= s
; *p
!= '\0'; p
++, q
++) {
805 /* Convert nonprintable characters to spaces in case MB_CUR_MAX > 1. */
806 static void makeprint_for_mb(char *s
, size_t l
)
812 if ((wl
= xmbstowcs(wbuf
, t
, sizeof wbuf
)) == (size_t)-1)
815 if (!iswprint(*wp
) && *wp
!= L
'\n' && *wp
!= L
'\r'
816 && *wp
!= L
'\b' && *wp
!= L
'\t')
825 /* Convert nonprintable characters to spaces. */
826 static void makeprint(char *s
, size_t l
)
829 if (MB_CUR_MAX
> 1) {
830 makeprint_for_mb(s
, l
);
836 if (!isprint(cuc(*s
)) && *s
!= '\n' && *s
!= '\r'
837 && *s
!= '\b' && *s
!= '\t')
843 /* Strip backslash characters from the given string. */
844 static void striprs(char *s
)
853 } while (*s
++ != '\0');
856 /* Extract the search pattern off the command line. */
857 static char *makepat(void)
861 if (cmd
.addon
== '\0')
862 p
= cmd
.cmdline
+ strlen(cmd
.cmdline
) - 1;
864 p
= cmd
.cmdline
+ strlen(cmd
.cmdline
) - 2;
869 if ((p
= strchr(cmd
.cmdline
, cmd
.key
)) != NULL
) {
876 /* Process errors that occurred in temporary file operations. */
877 static void __attribute__((__noreturn__
)) tmperr(FILE *f
, const char *ftype
)
880 warn(_("Read error from %s file"), ftype
);
882 /* Most likely '\0' in input. */
883 warnx(_("Unexpected EOF in %s file"), ftype
);
885 warn(_("Unknown error in %s file"), ftype
);
889 /* Read the file and respond to user input. Beware: long and ugly. */
890 static void pgfile(FILE *f
, const char *name
)
892 off_t pos
, oldpos
, fpos
;
893 /* These are the line counters:
894 * line the line desired to display
895 * fline the current line of the input file
896 * bline the current line of the file buffer
897 * oldline the line before a search was started
898 * eofline the last line of the file if it is already reached
899 * dline the line on the display */
900 off_t line
= 0, fline
= 0, bline
= 0, oldline
= 0, eofline
= 0;
903 unsigned searchcount
= 0;
904 /* Advance to EOF immediately. */
906 /* EOF has been reached by `line'. */
908 /* f and fbuf refer to the same file. */
915 /* fbuf an exact copy of the input file as it gets read
916 * find index table for input, one entry per line
917 * save for the s command, to save to a file */
918 FILE *fbuf
, *find
, *save
;
921 /* Just copy stdin to stdout. */
922 while ((sz
= fread(b
, sizeof *b
, READBUF
, f
)) != 0)
923 write_all(STDOUT_FILENO
, b
, sz
);
930 if ((fpos
= fseeko(f
, (off_t
)0, SEEK_SET
)) == -1)
937 if (fbuf
== NULL
|| find
== NULL
) {
938 warn(_("Cannot create tempfile"));
945 rerror
= regcomp(&re
, searchfor
, REG_NOSUB
| REG_NEWLINE
);
947 mesg(_("RE error: "));
948 regerror(rerror
, &re
, b
, READBUF
);
955 for (line
= startline
;;) {
956 /* Get a line from input file or buffer. */
958 fseeko(find
, line
* sizeof pos
, SEEK_SET
);
959 if (fread(&pos
, sizeof pos
, 1, find
) == 0)
960 tmperr(find
, "index");
961 fseeko(find
, (off_t
)0, SEEK_END
);
962 fseeko(fbuf
, pos
, SEEK_SET
);
963 if (fgets(b
, READBUF
, fbuf
) == NULL
)
964 tmperr(fbuf
, "buffer");
965 } else if (eofline
== 0) {
966 fseeko(find
, (off_t
)0, SEEK_END
);
969 fseeko(fbuf
, (off_t
)0, SEEK_END
);
971 if ((sig
= setjmp(jmpenv
)) != 0) {
972 /* We got a signal. */
975 fseeko(fbuf
, pos
, SEEK_SET
);
981 fseeko(f
, fpos
, SEEK_SET
);
983 p
= fgets(b
, READBUF
, f
);
985 if ((fpos
= ftello(f
)) == -1)
989 if (p
== NULL
|| *b
== '\0') {
998 fwrite_all(&pos
, sizeof pos
, 1, find
);
1002 while (*(p
= endline(ttycols
,
1005 pos
= oldpos
+ (p
- b
);
1015 } while (line
> bline
++);
1020 if (search
== FORWARD
&& remembered
== 1) {
1023 search
= searchcount
= 0;
1024 mesg(_("Pattern not found"));
1030 if (regexec(&re
, b
, 0, NULL
, 0) == 0) {
1033 if (searchcount
== 0) {
1035 switch (searchdisplay
) {
1040 line
-= pagelen
/ 2 + 1;
1050 /* We are not searching. */
1052 } else if (*b
!= '\0') {
1053 if (cflag
&& clear_screen
) {
1056 tputs(clear_screen
, STDOUT_FILENO
,
1062 if (eofline
&& line
== eofline
)
1065 if ((sig
= setjmp(jmpenv
)) != 0) {
1066 /* We got a signal. */
1071 p
= endline(ttycols
, b
);
1075 write_all(STDOUT_FILENO
, b
, sz
);
1079 if (dline
>= pagelen
|| eof
) {
1080 /* Time for prompting! */
1081 if (eof
&& seekeof
) {
1083 if (line
>= pagelen
)
1092 if (fline
== 0 || eflag
)
1096 prompt((line
- 1) / pagelen
+ 1);
1099 /* Search forward. */
1102 searchcount
= cmd
.count
;
1104 if (p
!= NULL
&& *p
) {
1105 if (remembered
== 1)
1107 rerror
= regcomp(&re
, p
,
1111 mesg(_("RE error: "));
1112 sz
= regerror(rerror
, &re
,
1118 } else if (remembered
== 0) {
1119 mesg(_("No remembered search string"));
1125 /* Search backward. */
1128 searchcount
= cmd
.count
;
1130 if (p
!= NULL
&& *p
) {
1131 if (remembered
== 1)
1133 rerror
= regcomp(&re
, p
,
1137 mesg(_("RE error: "));
1138 regerror(rerror
, &re
,
1144 } else if (remembered
== 0) {
1145 mesg(_("No remembered search string"));
1152 fseeko(find
, --line
* sizeof pos
,
1154 if (fread(&pos
, sizeof pos
, 1, find
) ==
1156 tmperr(find
, "index");
1157 fseeko(find
, (off_t
)0, SEEK_END
);
1158 fseeko(fbuf
, pos
, SEEK_SET
);
1159 if (fgets(b
, READBUF
, fbuf
) == NULL
)
1160 tmperr(fbuf
, "buffer");
1162 if (regexec(&re
, b
, 0, NULL
, 0) == 0)
1164 if (searchcount
== 0)
1169 search
= searchcount
= 0;
1170 mesg(_("Pattern not found"));
1173 eof
= search
= dline
= 0;
1175 switch (searchdisplay
) {
1180 line
-= pagelen
/ 2;
1194 while (*++p
== ' ') ;
1197 save
= fopen(p
, "wb");
1200 mesg(_("cannot open "));
1203 mesg(strerror(cmd
.count
));
1206 /* Advance to EOF. */
1207 fseeko(find
, (off_t
)0, SEEK_END
);
1210 fseeko(fbuf
, (off_t
)0,
1213 if (fgets(b
, READBUF
, f
) == NULL
) {
1219 fwrite_all(&pos
, sizeof pos
, 1, find
);
1223 while (*(p
= endline(ttycols
,
1226 pos
= oldpos
+ (p
- b
);
1237 fseeko(fbuf
, (off_t
)0, SEEK_SET
);
1238 while ((sz
= fread(b
, sizeof *b
, READBUF
,
1240 /* No error check for compat. */
1241 fwrite_all(b
, sizeof *b
, sz
, save
);
1243 if (close_stream(save
) != 0) {
1245 mesg(_("write failed"));
1248 mesg(strerror(cmd
.count
));
1251 fseeko(fbuf
, (off_t
)0, SEEK_END
);
1256 if (*cmd
.cmdline
!= 'l')
1259 cmd
.count
= 1; /* compat */
1260 if (isdigit(cuc(*cmd
.cmdline
))) {
1261 line
= cmd
.count
- 2;
1264 if (cmd
.count
!= 1) {
1265 line
+= cmd
.count
- 1 - pagelen
;
1269 /* Nothing to do if (count == 1) */
1273 /* Half screen forward. */
1274 case '\004': /* ^D */
1275 if (*cmd
.cmdline
!= cmd
.key
)
1278 cmd
.count
= 1; /* compat */
1279 line
+= (cmd
.count
* pagelen
/ 2)
1287 cmd
.count
= 1; /* compat */
1288 line
+= cmd
.count
* pagelen
- 2;
1291 if (*cmd
.cmdline
!= 'f')
1295 if (eofline
&& line
>= eofline
)
1301 /* Just a number, or '-', or <newline>. */
1303 cmd
.count
= 1; /* compat */
1304 if (isdigit(cuc(*cmd
.cmdline
)))
1305 line
= (cmd
.count
- 1) * pagelen
- 2;
1307 line
+= (cmd
.count
- 1)
1308 * (pagelen
- 1) - 2;
1309 if (*cmd
.cmdline
!= '\0')
1311 if (cmd
.count
!= 1) {
1320 /* Advance to EOF. */
1329 case '\014': /* ^L */
1330 /* Repaint screen. */
1332 if (line
>= pagelen
)
1341 mesg(program_invocation_short_name
);
1342 mesg(_(": !command not allowed in "
1347 write_all(STDOUT_FILENO
, cmd
.cmdline
,
1348 strlen(cmd
.cmdline
));
1349 write_all(STDOUT_FILENO
, "\n", 1);
1350 my_sigset(SIGINT
, SIG_IGN
);
1351 my_sigset(SIGQUIT
, SIG_IGN
);
1352 switch (cpid
= fork()) {
1355 const char *sh
= getenv("SHELL");
1361 if (isatty(0) == 0) {
1363 open(tty
, O_RDONLY
);
1367 my_sigset(SIGINT
, oldint
);
1368 my_sigset(SIGQUIT
, oldquit
);
1369 my_sigset(SIGTERM
, oldterm
);
1371 cmd
.cmdline
+ 1, NULL
);
1372 warn(_("failed to execute %s"), sh
);
1377 mesg(_("fork() failed, "
1378 "try again later\n"));
1381 while (wait(NULL
) != cpid
) ;
1383 my_sigset(SIGINT
, sighandler
);
1384 my_sigset(SIGQUIT
, sighandler
);
1391 const char *help
= _(helpscreen
);
1392 write_all(STDOUT_FILENO
, copyright
,
1394 write_all(STDOUT_FILENO
, help
,
1402 nextfile
= cmd
.count
;
1410 /* Previous file. */
1413 nextfile
= 0 - cmd
.count
;
1427 /* Set window size. */
1430 if (*cmd
.cmdline
!= cmd
.key
)
1431 pagelen
= ++cmd
.count
;
1439 if (cflag
&& dline
== 1) {
1452 static int parse_arguments(int arg
, int argc
, char **argv
)
1457 files
.last
= arg
+ argc
- 1;
1458 for (; argv
[arg
]; arg
+= nextfile
) {
1460 files
.current
= arg
;
1462 static int firsttime
;
1464 if (firsttime
> 1) {
1465 mesg(_("(Next file: "));
1476 nextfile
= cmd
.count
;
1484 /* Previous file. */
1487 nextfile
= 0 - cmd
.count
;
1502 if (strcmp(argv
[arg
], "-") == 0)
1505 input
= fopen(argv
[arg
], "r");
1506 if (input
== NULL
) {
1507 warn("%s", argv
[arg
]);
1512 if (ontty
== 0 && argc
> 2) {
1513 /* Use the prefix as specified by SUSv2. */
1514 write_all(STDOUT_FILENO
, "::::::::::::::\n", 15);
1515 write_all(STDOUT_FILENO
, argv
[arg
], strlen(argv
[arg
]));
1516 write_all(STDOUT_FILENO
, "\n::::::::::::::\n", 16);
1518 pgfile(input
, argv
[arg
]);
1525 int main(int argc
, char **argv
)
1530 xasprintf(©right
,
1531 _("%s %s Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.\n"),
1532 program_invocation_short_name
, PACKAGE_VERSION
);
1534 setlocale(LC_ALL
, "");
1535 bindtextdomain(PACKAGE
, LOCALEDIR
);
1536 textdomain(PACKAGE
);
1537 atexit(close_stdout
);
1539 if (tcgetattr(STDOUT_FILENO
, &otio
) == 0) {
1541 oldint
= my_sigset(SIGINT
, sighandler
);
1542 oldquit
= my_sigset(SIGQUIT
, sighandler
);
1543 oldterm
= my_sigset(SIGTERM
, sighandler
);
1544 setlocale(LC_CTYPE
, "");
1545 setlocale(LC_COLLATE
, "");
1546 tty
= ttyname(STDOUT_FILENO
);
1547 setupterm(NULL
, STDOUT_FILENO
, &tinfostat
);
1549 helpscreen
= _(helpscreen
);
1551 for (arg
= 1; argv
[arg
]; arg
++) {
1552 if (*argv
[arg
] == '+')
1554 if (*argv
[arg
] != '-' || argv
[arg
][1] == '\0')
1558 if (!strcmp(argv
[arg
], "--help")) {
1562 if (!strcmp(argv
[arg
], "--version")) {
1563 printf(UTIL_LINUX_VERSION
);
1564 return EXIT_SUCCESS
;
1567 for (i
= 1; argv
[arg
][i
]; i
++) {
1568 switch (argv
[arg
][i
]) {
1570 if (i
!= 1 || argv
[arg
][i
+ 1])
1571 invopt(&argv
[arg
][i
]);
1583 pagelen
= strtol_or_err(argv
[arg
] + 1,
1584 _("failed to parse number of lines per page"));
1600 if (argv
[arg
][i
+ 1]) {
1601 pstring
= &argv
[arg
][i
+ 1];
1602 } else if (argv
[++arg
]) {
1604 pstring
= argv
[arg
];
1617 printf(UTIL_LINUX_VERSION
);
1618 return EXIT_SUCCESS
;
1620 invopt(&argv
[arg
][i
]);
1627 for (arg
= 1; argv
[arg
]; arg
++) {
1628 if (*argv
[arg
] == '-') {
1629 if (argv
[arg
][1] == '-') {
1633 if (argv
[arg
][1] == '\0')
1635 if (argv
[arg
][1] == 'p' && argv
[arg
][2] == '\0')
1639 if (*argv
[arg
] != '+')
1642 switch (*(argv
[arg
] + 1)) {
1656 startline
= strtol_or_err(argv
[arg
] + 1,
1657 _("failed to parse number of lines per page"));
1660 searchfor
= argv
[arg
] + 2;
1661 if (*searchfor
== '\0')
1663 p
= searchfor
+ strlen(searchfor
) - 1;
1666 if (*searchfor
== '\0')
1674 pgfile(stdin
, "stdin");
1676 exitstatus
= parse_arguments(arg
, argc
, argv
);