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>
62 #if defined(HAVE_NCURSESW_NCURSES_H)
63 # include <ncursesw/ncurses.h>
64 #elif defined(HAVE_NCURSES_NCURSES_H)
65 # include <ncurses/ncurses.h>
66 #elif defined(HAVE_NCURSES_H)
70 #if defined(HAVE_NCURSESW_TERM_H)
71 # include <ncursesw/term.h>
72 #elif defined(HAVE_NCURSES_TERM_H)
73 # include <ncurses/term.h>
74 #elif defined(HAVE_TERM_H)
82 #include "closestream.h"
85 #define READBUF LINE_MAX /* size of input buffer */
86 #define CMDBUF 255 /* size of command buffer */
87 #define PG_TABSIZE 8 /* spaces consumed by tab character */
89 #define cuc(c) ((c) & 0377)
91 enum { FORWARD
= 1, BACKWARD
= 2 }; /* search direction */
92 enum { TOP
, MIDDLE
, BOTTOM
}; /* position of matching line */
94 /* States for syntax-aware command line editor. */
106 /* Current command */
108 char cmdline
[CMDBUF
];
112 char pattern
[CMDBUF
];
116 /* Position of file arguments on argv[] to main() */
123 static void (*oldint
) (int); /* old SIGINT handler */
124 static void (*oldquit
) (int); /* old SIGQUIT handler */
125 static void (*oldterm
) (int); /* old SIGTERM handler */
126 static char *tty
; /* result of ttyname(1) */
127 static unsigned ontty
; /* whether running on tty device */
128 static unsigned exitstatus
; /* exit status */
129 static int pagelen
= 23; /* lines on a single screen page */
130 static int ttycols
= 79; /* screen columns (starting at 0) */
131 static struct termios otio
; /* old termios settings */
132 static int tinfostat
= -1; /* terminfo routines initialized */
133 static int searchdisplay
= TOP
; /* matching line position */
134 static regex_t re
; /* regular expression to search for */
135 static int remembered
; /* have a remembered search string */
136 static int cflag
; /* clear screen before each page */
137 static int eflag
; /* suppress (EOF) */
138 static int fflag
; /* do not split lines */
139 static int nflag
; /* no newline for commands required */
140 static int rflag
; /* "restricted" pg */
141 static int sflag
; /* use standout mode */
142 static const char *pstring
= ":"; /* prompt string */
143 static char *searchfor
; /* search pattern from argv[] */
144 static int havepagelen
; /* page length is manually defined */
145 static long startline
; /* start line from argv[] */
146 static int nextfile
= 1; /* files to advance */
147 static jmp_buf jmpenv
; /* jump from signal handlers */
148 static int canjump
; /* jmpenv is valid */
149 static wchar_t wbuf
[READBUF
]; /* used in several widechar routines */
151 static char *copyright
;
152 static const char *helpscreen
= N_("\
153 -------------------------------------------------------\n\
155 q or Q quit program\n\
156 <newline> next page\n\
157 f skip a page forward\n\
158 d or ^D next halfpage\n\
161 /regex/ search forward for regex\n\
162 ?regex? or ^regex^ search backward for regex\n\
163 . or ^L redraw screen\n\
164 w or z set page size and go to next page\n\
165 s filename save current file to filename\n\
166 !command shell escape\n\
167 p go to previous file\n\
170 Many commands accept preceding numbers, for example:\n\
171 +1<newline> (next page); -1<newline> (previous page); 1<newline> (first page).\n\
173 See pg(1) for more information.\n\
174 -------------------------------------------------------\n");
177 static int fseeko(FILE *f
, off_t off
, int whence
)
179 return fseek(f
, (long)off
, whence
);
182 static off_t
ftello(FILE *f
)
184 return (off_t
) ftell(f
);
188 #ifdef USE_SIGSET /* never defined */
189 /* sigset and sigrelse are obsolete - use when POSIX stuff is unavailable */
190 # define my_sigset sigset
191 # define my_sigrelse sigrelse
193 static int my_sigrelse(int sig
)
197 if (sigemptyset(&sigs
) || sigaddset(&sigs
, sig
))
199 return sigprocmask(SIG_UNBLOCK
, &sigs
, NULL
);
202 typedef void (*my_sighandler_t
) (int);
203 static my_sighandler_t
my_sigset(int sig
, my_sighandler_t disp
)
205 struct sigaction act
, oact
;
207 act
.sa_handler
= disp
;
208 if (sigemptyset(&act
.sa_mask
))
211 if (sigaction(sig
, &act
, &oact
))
213 if (my_sigrelse(sig
))
215 return oact
.sa_handler
;
217 #endif /* USE_SIGSET */
220 static void __attribute__((__noreturn__
)) quit(int status
)
222 exit(status
< 0100 ? status
: 077);
225 /* Usage message and similar routines. */
226 static void __attribute__((__noreturn__
)) usage(FILE *out
)
228 fputs(USAGE_HEADER
, out
);
230 _(" %s [options] [+line] [+/pattern/] [files]\n"),
231 program_invocation_short_name
);
233 fputs(USAGE_SEPARATOR
, out
);
234 fputs(_("Browse pagewise through text files.\n"), out
);
236 fputs(USAGE_OPTIONS
, out
);
237 fputs(_(" -number lines per page\n"), out
);
238 fputs(_(" -c clear screen before displaying\n"), out
);
239 fputs(_(" -e do not pause at end of a file\n"), out
);
240 fputs(_(" -f do not split long lines\n"), out
);
241 fputs(_(" -n terminate command with new line\n"), out
);
242 fputs(_(" -p <prompt> specify prompt\n"), out
);
243 fputs(_(" -r disallow shell escape\n"), out
);
244 fputs(_(" -s print messages to stdout\n"), out
);
245 fputs(_(" +number start at the given line\n"), out
);
246 fputs(_(" +/pattern/ start at the line containing pattern\n"), out
);
248 fputs(USAGE_SEPARATOR
, out
);
249 fputs(USAGE_HELP
, out
);
250 fputs(USAGE_VERSION
, out
);
252 fprintf(out
, USAGE_MAN_TAIL("pg(1)"));
253 quit(out
== stderr
? 2 : 0);
256 static void __attribute__((__noreturn__
)) needarg(const char *s
)
258 warnx(_("option requires an argument -- %s"), s
);
262 static void __attribute__((__noreturn__
)) invopt(const char *s
)
264 warnx(_("illegal option -- %s"), s
);
269 /* A mbstowcs()-alike function that transparently handles invalid
271 static size_t xmbstowcs(wchar_t * pwcs
, const char *s
, size_t nwcs
)
276 ignore_result(mbtowc(pwcs
, NULL
, MB_CUR_MAX
)); /* reset shift state */
278 if ((c
= mbtowc(pwcs
, s
, MB_CUR_MAX
)) < 0) {
288 ignore_result(mbtowc(pwcs
, NULL
, MB_CUR_MAX
));
293 /* Helper function for tputs(). */
294 static int outcap(int i
)
297 return write_all(STDOUT_FILENO
, &c
, 1) == 0 ? 1 : -1;
300 /* Write messages to terminal. */
301 static void mesg(const char *message
)
305 if (*message
!= '\n' && sflag
)
306 vidputs(A_STANDOUT
, outcap
);
307 write_all(STDOUT_FILENO
, message
, strlen(message
));
308 if (*message
!= '\n' && sflag
)
309 vidputs(A_NORMAL
, outcap
);
312 /* Get the window size. */
313 static void getwinsize(void)
315 static int initialized
, envlines
, envcols
, deflines
, defcols
;
317 struct winsize winsz
;
322 if (initialized
== 0) {
323 if ((p
= getenv("LINES")) != NULL
&& *p
!= '\0')
324 if ((envlines
= atoi(p
)) < 0)
326 if ((p
= getenv("COLUMNS")) != NULL
&& *p
!= '\0')
327 if ((envcols
= atoi(p
)) < 0)
329 /* terminfo values. */
330 if (tinfostat
!= 1 || columns
== 0)
334 if (tinfostat
!= 1 || lines
== 0)
341 badioctl
= ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &winsz
);
344 ttycols
= envcols
- 1;
347 ttycols
= winsz
.ws_col
- 1;
350 ttycols
= defcols
- 1;
351 if (havepagelen
== 0) {
353 pagelen
= envlines
- 1;
356 pagelen
= winsz
.ws_row
- 1;
359 pagelen
= deflines
- 1;
363 /* Message if skipping parts of files. */
364 static void skip(int direction
)
367 mesg(_("...skipping forward\n"));
369 mesg(_("...skipping backward\n"));
372 /* Signal handler while reading from input file. */
373 static void sighandler(int signum
)
375 if (canjump
&& (signum
== SIGINT
|| signum
== SIGQUIT
))
376 longjmp(jmpenv
, signum
);
377 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &otio
);
381 /* Check whether the requested file was specified on the command line. */
382 static int checkf(void)
384 if (files
.current
+ nextfile
>= files
.last
) {
385 mesg(_("No next file"));
388 if (files
.current
+ nextfile
< files
.first
) {
389 mesg(_("No previous file"));
396 /* Return the last character that will fit on the line at col columns in
397 * case MB_CUR_MAX > 1. */
398 static char *endline_for_mb(unsigned col
, char *s
)
406 if ((wl
= xmbstowcs(wbuf
, t
, sizeof wbuf
- 1)) == (size_t)-1)
409 while (*p
!= L
'\0') {
416 /* No cursor movement. */
428 pos
+= PG_TABSIZE
- (pos
% PG_TABSIZE
);
434 pos
+= wcwidth(L
'?');
439 else if (pos
> col
+ 1)
440 /* wcwidth() found a character that has
441 * multiple columns. What happens now?
442 * Assume the terminal will print the
443 * entire character onto the next row. */
456 if ((pos
= wcstombs(NULL
, p
, READBUF
)) == (size_t)-1)
460 #endif /* HAVE_WIDECHAR */
462 /* Return the last character that will fit on the line at col columns. */
463 static char *endline(unsigned col
, char *s
)
470 return endline_for_mb(col
, s
);
480 /* No cursor movement. */
492 pos
+= PG_TABSIZE
- (pos
% PG_TABSIZE
);
512 /* Clear the current line on the terminal's screen. */
513 static void cline(void)
515 char *buf
= xmalloc(ttycols
+ 2);
516 memset(buf
, ' ', ttycols
+ 2);
518 buf
[ttycols
+ 1] = '\r';
519 write_all(STDOUT_FILENO
, buf
, ttycols
+ 2);
523 /* Evaluate a command character's semantics. */
524 static int getstate(int c
)
571 tputs(bell
, STDOUT_FILENO
, outcap
);
577 /* Get the count and ignore last character of string. */
578 static int getcount(char *cmdstr
)
586 buf
= xmalloc(strlen(cmdstr
) + 1);
588 if (cmd
.key
!= '\0') {
589 if (cmd
.key
== '/' || cmd
.key
== '?' || cmd
.key
== '^') {
590 if ((p
= strchr(buf
, cmd
.key
)) != NULL
)
593 *(buf
+ strlen(buf
) - 1) = '\0';
599 if (buf
[0] == '-' && buf
[1] == '\0') {
611 /* Read what the user writes at the prompt. This is tricky because we
612 * check for valid input. */
613 static void prompt(long long pageno
)
619 char b
[LINE_MAX
], *p
;
622 if ((p
= strstr(pstring
, "%d")) == NULL
) {
626 sprintf(b
+ (p
- pstring
), "%lld", pageno
);
631 cmd
.key
= cmd
.addon
= cmd
.cmdline
[0] = '\0';
633 tcgetattr(STDOUT_FILENO
, &tio
);
634 tio
.c_lflag
&= ~(ICANON
| ECHO
);
637 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &tio
);
638 tcflush(STDOUT_FILENO
, TCIFLUSH
);
640 switch (read(STDOUT_FILENO
, &key
, 1)) {
647 if (key
== tio
.c_cc
[VERASE
]) {
649 write_all(STDOUT_FILENO
, "\b \b", 3);
650 cmd
.cmdline
[--cmd
.cmdlen
] = '\0';
664 if (cmd
.cmdline
[cmd
.cmdlen
- 1] == '\\') {
666 while (cmd
.cmdline
[cmd
.cmdlen
673 if (strchr(cmd
.cmdline
, cmd
.key
)
682 if (cmd
.cmdlen
== 0) {
688 if (key
== tio
.c_cc
[VKILL
]) {
691 cmd
.cmdline
[0] = '\0';
696 if (key
== '\n' || (nflag
&& state
== COUNT
&& key
== ' '))
698 if (cmd
.cmdlen
>= CMDBUF
- 1)
713 if (getstate(key
) != ADDON_FIN
)
722 searchdisplay
= MIDDLE
;
725 searchdisplay
= BOTTOM
;
733 state
= getstate(key
);
736 if (cmd
.cmdlen
!= 0) {
751 write_all(STDOUT_FILENO
, &key
, 1);
752 cmd
.cmdline
[cmd
.cmdlen
++] = key
;
753 cmd
.cmdline
[cmd
.cmdlen
] = '\0';
754 if (nflag
&& state
== CMD_FIN
)
758 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &otio
);
760 cmd
.count
= getcount(cmd
.cmdline
);
764 /* Remove backspace formatting, for searches in case MB_CUR_MAX > 1. */
765 static char *colb_for_mb(char *s
)
769 size_t l
= strlen(s
), wl
;
772 if ((wl
= xmbstowcs(wbuf
, p
, sizeof wbuf
)) == (size_t)-1)
774 for (wp
= wbuf
, wq
= wbuf
, i
= 0; *wp
!= L
'\0' && i
< wl
; wp
++, wq
++) {
785 wcstombs(s
, wp
, l
+ 1);
791 /* Remove backspace formatting, for searches. */
792 static char *colb(char *s
)
798 return colb_for_mb(s
);
801 for (q
= s
; *p
!= '\0'; p
++, q
++) {
816 /* Convert non-printable characters to spaces in case MB_CUR_MAX > 1. */
817 static void makeprint_for_mb(char *s
, size_t l
)
823 if ((wl
= xmbstowcs(wbuf
, t
, sizeof wbuf
)) == (size_t)-1)
826 if (!iswprint(*wp
) && *wp
!= L
'\n' && *wp
!= L
'\r'
827 && *wp
!= L
'\b' && *wp
!= L
'\t')
836 /* Convert non-printable characters to spaces. */
837 static void makeprint(char *s
, size_t l
)
840 if (MB_CUR_MAX
> 1) {
841 makeprint_for_mb(s
, l
);
847 if (!isprint(cuc(*s
)) && *s
!= '\n' && *s
!= '\r'
848 && *s
!= '\b' && *s
!= '\t')
854 /* Strip backslash characters from the given string. */
855 static void striprs(char *s
)
864 } while (*s
++ != '\0');
867 /* Extract the search pattern off the command line. */
868 static char *makepat(void)
872 if (cmd
.addon
== '\0')
873 p
= cmd
.cmdline
+ strlen(cmd
.cmdline
) - 1;
875 p
= cmd
.cmdline
+ strlen(cmd
.cmdline
) - 2;
880 if ((p
= strchr(cmd
.cmdline
, cmd
.key
)) != NULL
) {
887 /* Process errors that occurred in temporary file operations. */
888 static void __attribute__((__noreturn__
)) tmperr(FILE *f
, const char *ftype
)
891 warn(_("Read error from %s file"), ftype
);
893 /* Most likely '\0' in input. */
894 warnx(_("Unexpected EOF in %s file"), ftype
);
896 warn(_("Unknown error in %s file"), ftype
);
900 /* Read the file and respond to user input. Beware: long and ugly. */
901 static void pgfile(FILE *f
, const char *name
)
903 off_t pos
, oldpos
, fpos
;
904 /* These are the line counters:
905 * line the line desired to display
906 * fline the current line of the input file
907 * bline the current line of the file buffer
908 * oldline the line before a search was started
909 * eofline the last line of the file if it is already reached
910 * dline the line on the display */
911 off_t line
= 0, fline
= 0, bline
= 0, oldline
= 0, eofline
= 0;
914 unsigned searchcount
= 0;
915 /* Advance to EOF immediately. */
917 /* EOF has been reached by `line'. */
919 /* f and fbuf refer to the same file. */
926 /* fbuf an exact copy of the input file as it gets read
927 * find index table for input, one entry per line
928 * save for the s command, to save to a file */
929 FILE *fbuf
, *find
, *save
;
932 /* Just copy stdin to stdout. */
933 while ((sz
= fread(b
, sizeof *b
, READBUF
, f
)) != 0)
934 write_all(STDOUT_FILENO
, b
, sz
);
941 if ((fpos
= fseeko(f
, (off_t
)0, SEEK_SET
)) == -1)
948 if (fbuf
== NULL
|| find
== NULL
) {
949 warn(_("Cannot create temporary file"));
956 rerror
= regcomp(&re
, searchfor
, REG_NOSUB
| REG_NEWLINE
);
958 mesg(_("RE error: "));
959 regerror(rerror
, &re
, b
, READBUF
);
966 for (line
= startline
;;) {
967 /* Get a line from input file or buffer. */
969 fseeko(find
, line
* sizeof pos
, SEEK_SET
);
970 if (fread(&pos
, sizeof pos
, 1, find
) == 0)
971 tmperr(find
, "index");
972 fseeko(find
, (off_t
)0, SEEK_END
);
973 fseeko(fbuf
, pos
, SEEK_SET
);
974 if (fgets(b
, READBUF
, fbuf
) == NULL
)
975 tmperr(fbuf
, "buffer");
976 } else if (eofline
== 0) {
977 fseeko(find
, (off_t
)0, SEEK_END
);
980 fseeko(fbuf
, (off_t
)0, SEEK_END
);
982 if ((sig
= setjmp(jmpenv
)) != 0) {
983 /* We got a signal. */
986 fseeko(fbuf
, pos
, SEEK_SET
);
992 fseeko(f
, fpos
, SEEK_SET
);
994 p
= fgets(b
, READBUF
, f
);
996 if ((fpos
= ftello(f
)) == -1)
1000 if (p
== NULL
|| *b
== '\0') {
1009 fwrite_all(&pos
, sizeof pos
, 1, find
);
1013 while (*(p
= endline(ttycols
,
1016 pos
= oldpos
+ (p
- b
);
1026 } while (line
> bline
++);
1031 if (search
== FORWARD
&& remembered
== 1) {
1034 search
= searchcount
= 0;
1035 mesg(_("Pattern not found"));
1041 if (regexec(&re
, b
, 0, NULL
, 0) == 0) {
1044 if (searchcount
== 0) {
1046 switch (searchdisplay
) {
1051 line
-= pagelen
/ 2 + 1;
1061 /* We are not searching. */
1063 } else if (*b
!= '\0') {
1064 if (cflag
&& clear_screen
) {
1067 tputs(clear_screen
, STDOUT_FILENO
,
1073 if (eofline
&& line
== eofline
)
1076 if ((sig
= setjmp(jmpenv
)) != 0) {
1077 /* We got a signal. */
1082 p
= endline(ttycols
, b
);
1086 write_all(STDOUT_FILENO
, b
, sz
);
1090 if (dline
>= pagelen
|| eof
) {
1091 /* Time for prompting! */
1092 if (eof
&& seekeof
) {
1094 if (line
>= pagelen
)
1103 if (fline
== 0 || eflag
)
1107 prompt((line
- 1) / pagelen
+ 1);
1110 /* Search forward. */
1113 searchcount
= cmd
.count
;
1115 if (p
!= NULL
&& *p
) {
1116 if (remembered
== 1)
1118 rerror
= regcomp(&re
, p
,
1122 mesg(_("RE error: "));
1123 sz
= regerror(rerror
, &re
,
1129 } else if (remembered
== 0) {
1130 mesg(_("No remembered search string"));
1136 /* Search backward. */
1139 searchcount
= cmd
.count
;
1141 if (p
!= NULL
&& *p
) {
1142 if (remembered
== 1)
1144 rerror
= regcomp(&re
, p
,
1148 mesg(_("RE error: "));
1149 regerror(rerror
, &re
,
1155 } else if (remembered
== 0) {
1156 mesg(_("No remembered search string"));
1163 fseeko(find
, --line
* sizeof pos
,
1165 if (fread(&pos
, sizeof pos
, 1, find
) ==
1167 tmperr(find
, "index");
1168 fseeko(find
, (off_t
)0, SEEK_END
);
1169 fseeko(fbuf
, pos
, SEEK_SET
);
1170 if (fgets(b
, READBUF
, fbuf
) == NULL
)
1171 tmperr(fbuf
, "buffer");
1173 if (regexec(&re
, b
, 0, NULL
, 0) == 0)
1175 if (searchcount
== 0)
1180 search
= searchcount
= 0;
1181 mesg(_("Pattern not found"));
1184 eof
= search
= dline
= 0;
1186 switch (searchdisplay
) {
1191 line
-= pagelen
/ 2;
1205 while (*++p
== ' ') ;
1208 save
= fopen(p
, "wb");
1211 mesg(_("cannot open "));
1214 mesg(strerror(cmd
.count
));
1217 /* Advance to EOF. */
1218 fseeko(find
, (off_t
)0, SEEK_END
);
1221 fseeko(fbuf
, (off_t
)0,
1224 if (fgets(b
, READBUF
, f
) == NULL
) {
1230 fwrite_all(&pos
, sizeof pos
, 1, find
);
1234 while (*(p
= endline(ttycols
,
1237 pos
= oldpos
+ (p
- b
);
1248 fseeko(fbuf
, (off_t
)0, SEEK_SET
);
1249 while ((sz
= fread(b
, sizeof *b
, READBUF
,
1251 /* No error check for compat. */
1252 fwrite_all(b
, sizeof *b
, sz
, save
);
1254 if (close_stream(save
) != 0) {
1256 mesg(_("write failed"));
1259 mesg(strerror(cmd
.count
));
1262 fseeko(fbuf
, (off_t
)0, SEEK_END
);
1267 if (*cmd
.cmdline
!= 'l')
1270 cmd
.count
= 1; /* compat */
1271 if (isdigit(cuc(*cmd
.cmdline
))) {
1272 line
= cmd
.count
- 2;
1275 if (cmd
.count
!= 1) {
1276 line
+= cmd
.count
- 1 - pagelen
;
1280 /* Nothing to do if (count == 1) */
1284 /* Half screen forward. */
1285 case '\004': /* ^D */
1286 if (*cmd
.cmdline
!= cmd
.key
)
1289 cmd
.count
= 1; /* compat */
1290 line
+= (cmd
.count
* pagelen
/ 2)
1298 cmd
.count
= 1; /* compat */
1299 line
+= cmd
.count
* pagelen
- 2;
1302 if (*cmd
.cmdline
!= 'f')
1306 if (eofline
&& line
>= eofline
)
1312 /* Just a number, or '-', or <newline>. */
1314 cmd
.count
= 1; /* compat */
1315 if (isdigit(cuc(*cmd
.cmdline
)))
1316 line
= (cmd
.count
- 1) * pagelen
- 2;
1318 line
+= (cmd
.count
- 1)
1319 * (pagelen
- 1) - 2;
1320 if (*cmd
.cmdline
!= '\0')
1322 if (cmd
.count
!= 1) {
1331 /* Advance to EOF. */
1340 case '\014': /* ^L */
1341 /* Repaint screen. */
1343 if (line
>= pagelen
)
1352 mesg(program_invocation_short_name
);
1353 mesg(_(": !command not allowed in "
1358 write_all(STDOUT_FILENO
, cmd
.cmdline
,
1359 strlen(cmd
.cmdline
));
1360 write_all(STDOUT_FILENO
, "\n", 1);
1361 my_sigset(SIGINT
, SIG_IGN
);
1362 my_sigset(SIGQUIT
, SIG_IGN
);
1363 switch (cpid
= fork()) {
1366 const char *sh
= getenv("SHELL");
1372 if (isatty(0) == 0) {
1374 open(tty
, O_RDONLY
);
1378 my_sigset(SIGINT
, oldint
);
1379 my_sigset(SIGQUIT
, oldquit
);
1380 my_sigset(SIGTERM
, oldterm
);
1382 cmd
.cmdline
+ 1, NULL
);
1383 warn(_("failed to execute %s"), sh
);
1388 mesg(_("fork() failed, "
1389 "try again later\n"));
1392 while (wait(NULL
) != cpid
) ;
1394 my_sigset(SIGINT
, sighandler
);
1395 my_sigset(SIGQUIT
, sighandler
);
1402 const char *help
= _(helpscreen
);
1403 write_all(STDOUT_FILENO
, copyright
,
1405 write_all(STDOUT_FILENO
, help
,
1413 nextfile
= cmd
.count
;
1421 /* Previous file. */
1424 nextfile
= 0 - cmd
.count
;
1438 /* Set window size. */
1441 if (*cmd
.cmdline
!= cmd
.key
)
1442 pagelen
= ++cmd
.count
;
1450 if (cflag
&& dline
== 1) {
1463 static int parse_arguments(int arg
, int argc
, char **argv
)
1468 files
.last
= arg
+ argc
- 1;
1469 for (; argv
[arg
]; arg
+= nextfile
) {
1471 files
.current
= arg
;
1473 static int firsttime
;
1475 if (firsttime
> 1) {
1476 mesg(_("(Next file: "));
1487 nextfile
= cmd
.count
;
1495 /* Previous file. */
1498 nextfile
= 0 - cmd
.count
;
1513 if (strcmp(argv
[arg
], "-") == 0)
1516 input
= fopen(argv
[arg
], "r");
1517 if (input
== NULL
) {
1518 warn("%s", argv
[arg
]);
1523 if (ontty
== 0 && argc
> 2) {
1524 /* Use the prefix as specified by SUSv2. */
1525 write_all(STDOUT_FILENO
, "::::::::::::::\n", 15);
1526 write_all(STDOUT_FILENO
, argv
[arg
], strlen(argv
[arg
]));
1527 write_all(STDOUT_FILENO
, "\n::::::::::::::\n", 16);
1529 pgfile(input
, argv
[arg
]);
1536 int main(int argc
, char **argv
)
1541 xasprintf(©right
,
1542 _("%s %s Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.\n"),
1543 program_invocation_short_name
, PACKAGE_VERSION
);
1545 setlocale(LC_ALL
, "");
1546 bindtextdomain(PACKAGE
, LOCALEDIR
);
1547 textdomain(PACKAGE
);
1548 atexit(close_stdout
);
1550 if (tcgetattr(STDOUT_FILENO
, &otio
) == 0) {
1552 oldint
= my_sigset(SIGINT
, sighandler
);
1553 oldquit
= my_sigset(SIGQUIT
, sighandler
);
1554 oldterm
= my_sigset(SIGTERM
, sighandler
);
1555 setlocale(LC_CTYPE
, "");
1556 setlocale(LC_COLLATE
, "");
1557 tty
= ttyname(STDOUT_FILENO
);
1558 setupterm(NULL
, STDOUT_FILENO
, &tinfostat
);
1560 helpscreen
= _(helpscreen
);
1562 for (arg
= 1; argv
[arg
]; arg
++) {
1563 if (*argv
[arg
] == '+')
1565 if (*argv
[arg
] != '-' || argv
[arg
][1] == '\0')
1569 if (!strcmp(argv
[arg
], "--help")) {
1573 if (!strcmp(argv
[arg
], "--version")) {
1574 printf(UTIL_LINUX_VERSION
);
1575 return EXIT_SUCCESS
;
1578 for (i
= 1; argv
[arg
][i
]; i
++) {
1579 switch (argv
[arg
][i
]) {
1581 if (i
!= 1 || argv
[arg
][i
+ 1])
1582 invopt(&argv
[arg
][i
]);
1594 pagelen
= strtol_or_err(argv
[arg
] + 1,
1595 _("failed to parse number of lines per page"));
1611 if (argv
[arg
][i
+ 1]) {
1612 pstring
= &argv
[arg
][i
+ 1];
1613 } else if (argv
[++arg
]) {
1615 pstring
= argv
[arg
];
1628 printf(UTIL_LINUX_VERSION
);
1629 return EXIT_SUCCESS
;
1631 invopt(&argv
[arg
][i
]);
1638 for (arg
= 1; argv
[arg
]; arg
++) {
1639 if (*argv
[arg
] == '-') {
1640 if (argv
[arg
][1] == '-') {
1644 if (argv
[arg
][1] == '\0')
1646 if (argv
[arg
][1] == 'p' && argv
[arg
][2] == '\0')
1650 if (*argv
[arg
] != '+')
1653 switch (*(argv
[arg
] + 1)) {
1667 startline
= strtol_or_err(argv
[arg
] + 1,
1668 _("failed to parse number of lines per page"));
1671 searchfor
= argv
[arg
] + 2;
1672 if (*searchfor
== '\0')
1674 p
= searchfor
+ strlen(searchfor
) - 1;
1677 if (*searchfor
== '\0')
1685 pgfile(stdin
, "stdin");
1687 exitstatus
= parse_arguments(arg
, argc
, argv
);