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
);
226 fputs(USAGE_SEPARATOR
, out
);
227 fputs(_("Browse pagewise through text files.\n"), out
);
229 fputs(USAGE_OPTIONS
, out
);
230 fputs(_(" -number lines per page\n"), out
);
231 fputs(_(" -c clear screen before displaying\n"), out
);
232 fputs(_(" -e do not pause at end of a file\n"), out
);
233 fputs(_(" -f do not split long lines\n"), out
);
234 fputs(_(" -n terminate command with new line\n"), out
);
235 fputs(_(" -p <prompt> specify prompt\n"), out
);
236 fputs(_(" -r disallow shell escape\n"), out
);
237 fputs(_(" -s print messages to stdout\n"), out
);
238 fputs(_(" +number start at the given line\n"), out
);
239 fputs(_(" +/pattern/ start at the line containing pattern\n"), out
);
241 fputs(USAGE_SEPARATOR
, out
);
242 fputs(USAGE_HELP
, out
);
243 fputs(USAGE_VERSION
, out
);
245 fprintf(out
, USAGE_MAN_TAIL("pg(1)"));
246 quit(out
== stderr
? 2 : 0);
249 static void __attribute__((__noreturn__
)) needarg(const char *s
)
251 warnx(_("option requires an argument -- %s"), s
);
255 static void __attribute__((__noreturn__
)) invopt(const char *s
)
257 warnx(_("illegal option -- %s"), s
);
262 /* A mbstowcs()-alike function that transparently handles invalid
264 static size_t xmbstowcs(wchar_t * pwcs
, const char *s
, size_t nwcs
)
269 ignore_result(mbtowc(pwcs
, NULL
, MB_CUR_MAX
)); /* reset shift state */
271 if ((c
= mbtowc(pwcs
, s
, MB_CUR_MAX
)) < 0) {
281 ignore_result(mbtowc(pwcs
, NULL
, MB_CUR_MAX
));
286 /* Helper function for tputs(). */
287 static int outcap(int i
)
290 return write_all(STDOUT_FILENO
, &c
, 1) == 0 ? 1 : -1;
293 /* Write messages to terminal. */
294 static void mesg(const char *message
)
298 if (*message
!= '\n' && sflag
)
299 vidputs(A_STANDOUT
, outcap
);
300 write_all(STDOUT_FILENO
, message
, strlen(message
));
301 if (*message
!= '\n' && sflag
)
302 vidputs(A_NORMAL
, outcap
);
305 /* Get the window size. */
306 static void getwinsize(void)
308 static int initialized
, envlines
, envcols
, deflines
, defcols
;
310 struct winsize winsz
;
315 if (initialized
== 0) {
316 if ((p
= getenv("LINES")) != NULL
&& *p
!= '\0')
317 if ((envlines
= atoi(p
)) < 0)
319 if ((p
= getenv("COLUMNS")) != NULL
&& *p
!= '\0')
320 if ((envcols
= atoi(p
)) < 0)
322 /* terminfo values. */
323 if (tinfostat
!= 1 || columns
== 0)
327 if (tinfostat
!= 1 || lines
== 0)
334 badioctl
= ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &winsz
);
337 ttycols
= envcols
- 1;
340 ttycols
= winsz
.ws_col
- 1;
343 ttycols
= defcols
- 1;
344 if (havepagelen
== 0) {
346 pagelen
= envlines
- 1;
349 pagelen
= winsz
.ws_row
- 1;
352 pagelen
= deflines
- 1;
356 /* Message if skipping parts of files. */
357 static void skip(int direction
)
360 mesg(_("...skipping forward\n"));
362 mesg(_("...skipping backward\n"));
365 /* Signal handler while reading from input file. */
366 static void sighandler(int signum
)
368 if (canjump
&& (signum
== SIGINT
|| signum
== SIGQUIT
))
369 longjmp(jmpenv
, signum
);
370 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &otio
);
374 /* Check whether the requested file was specified on the command line. */
375 static int checkf(void)
377 if (files
.current
+ nextfile
>= files
.last
) {
378 mesg(_("No next file"));
381 if (files
.current
+ nextfile
< files
.first
) {
382 mesg(_("No previous file"));
389 /* Return the last character that will fit on the line at col columns in
390 * case MB_CUR_MAX > 1. */
391 static char *endline_for_mb(unsigned col
, char *s
)
399 if ((wl
= xmbstowcs(wbuf
, t
, sizeof wbuf
- 1)) == (size_t)-1)
402 while (*p
!= L
'\0') {
409 /* No cursor movement. */
421 pos
+= TABSIZE
- (pos
% TABSIZE
);
427 pos
+= wcwidth(L
'?');
432 else if (pos
> col
+ 1)
433 /* wcwidth() found a character that has
434 * multiple columns. What happens now?
435 * Assume the terminal will print the
436 * entire character onto the next row. */
449 if ((pos
= wcstombs(NULL
, p
, READBUF
)) == (size_t)-1)
453 #endif /* HAVE_WIDECHAR */
455 /* Return the last character that will fit on the line at col columns. */
456 static char *endline(unsigned col
, char *s
)
463 return endline_for_mb(col
, s
);
473 /* No cursor movement. */
485 pos
+= TABSIZE
- (pos
% TABSIZE
);
505 /* Clear the current line on the terminal's screen. */
506 static void cline(void)
508 char *buf
= xmalloc(ttycols
+ 2);
509 memset(buf
, ' ', ttycols
+ 2);
511 buf
[ttycols
+ 1] = '\r';
512 write_all(STDOUT_FILENO
, buf
, ttycols
+ 2);
516 /* Evaluate a command character's semantics. */
517 static int getstate(int c
)
564 tputs(bell
, STDOUT_FILENO
, outcap
);
570 /* Get the count and ignore last character of string. */
571 static int getcount(char *cmdstr
)
579 buf
= xmalloc(strlen(cmdstr
) + 1);
581 if (cmd
.key
!= '\0') {
582 if (cmd
.key
== '/' || cmd
.key
== '?' || cmd
.key
== '^') {
583 if ((p
= strchr(buf
, cmd
.key
)) != NULL
)
586 *(buf
+ strlen(buf
) - 1) = '\0';
592 if (buf
[0] == '-' && buf
[1] == '\0') {
604 /* Read what the user writes at the prompt. This is tricky because we
605 * check for valid input. */
606 static void prompt(long long pageno
)
612 char b
[LINE_MAX
], *p
;
615 if ((p
= strstr(pstring
, "%d")) == NULL
) {
619 sprintf(b
+ (p
- pstring
), "%lld", pageno
);
624 cmd
.key
= cmd
.addon
= cmd
.cmdline
[0] = '\0';
626 tcgetattr(STDOUT_FILENO
, &tio
);
627 tio
.c_lflag
&= ~(ICANON
| ECHO
);
630 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &tio
);
631 tcflush(STDOUT_FILENO
, TCIFLUSH
);
633 switch (read(STDOUT_FILENO
, &key
, 1)) {
640 if (key
== tio
.c_cc
[VERASE
]) {
642 write_all(STDOUT_FILENO
, "\b \b", 3);
643 cmd
.cmdline
[--cmd
.cmdlen
] = '\0';
657 if (cmd
.cmdline
[cmd
.cmdlen
- 1] == '\\') {
659 while (cmd
.cmdline
[cmd
.cmdlen
666 if (strchr(cmd
.cmdline
, cmd
.key
)
675 if (cmd
.cmdlen
== 0) {
681 if (key
== tio
.c_cc
[VKILL
]) {
684 cmd
.cmdline
[0] = '\0';
689 if (key
== '\n' || (nflag
&& state
== COUNT
&& key
== ' '))
691 if (cmd
.cmdlen
>= CMDBUF
- 1)
706 if (getstate(key
) != ADDON_FIN
)
715 searchdisplay
= MIDDLE
;
718 searchdisplay
= BOTTOM
;
726 state
= getstate(key
);
729 if (cmd
.cmdlen
!= 0) {
744 write_all(STDOUT_FILENO
, &key
, 1);
745 cmd
.cmdline
[cmd
.cmdlen
++] = key
;
746 cmd
.cmdline
[cmd
.cmdlen
] = '\0';
747 if (nflag
&& state
== CMD_FIN
)
751 tcsetattr(STDOUT_FILENO
, TCSADRAIN
, &otio
);
753 cmd
.count
= getcount(cmd
.cmdline
);
757 /* Remove backspace formatting, for searches in case MB_CUR_MAX > 1. */
758 static char *colb_for_mb(char *s
)
762 size_t l
= strlen(s
), wl
;
765 if ((wl
= xmbstowcs(wbuf
, p
, sizeof wbuf
)) == (size_t)-1)
767 for (wp
= wbuf
, wq
= wbuf
, i
= 0; *wp
!= L
'\0' && i
< wl
; wp
++, wq
++) {
778 wcstombs(s
, wp
, l
+ 1);
784 /* Remove backspace formatting, for searches. */
785 static char *colb(char *s
)
791 return colb_for_mb(s
);
794 for (q
= s
; *p
!= '\0'; p
++, q
++) {
809 /* Convert nonprintable characters to spaces in case MB_CUR_MAX > 1. */
810 static void makeprint_for_mb(char *s
, size_t l
)
816 if ((wl
= xmbstowcs(wbuf
, t
, sizeof wbuf
)) == (size_t)-1)
819 if (!iswprint(*wp
) && *wp
!= L
'\n' && *wp
!= L
'\r'
820 && *wp
!= L
'\b' && *wp
!= L
'\t')
829 /* Convert nonprintable characters to spaces. */
830 static void makeprint(char *s
, size_t l
)
833 if (MB_CUR_MAX
> 1) {
834 makeprint_for_mb(s
, l
);
840 if (!isprint(cuc(*s
)) && *s
!= '\n' && *s
!= '\r'
841 && *s
!= '\b' && *s
!= '\t')
847 /* Strip backslash characters from the given string. */
848 static void striprs(char *s
)
857 } while (*s
++ != '\0');
860 /* Extract the search pattern off the command line. */
861 static char *makepat(void)
865 if (cmd
.addon
== '\0')
866 p
= cmd
.cmdline
+ strlen(cmd
.cmdline
) - 1;
868 p
= cmd
.cmdline
+ strlen(cmd
.cmdline
) - 2;
873 if ((p
= strchr(cmd
.cmdline
, cmd
.key
)) != NULL
) {
880 /* Process errors that occurred in temporary file operations. */
881 static void __attribute__((__noreturn__
)) tmperr(FILE *f
, const char *ftype
)
884 warn(_("Read error from %s file"), ftype
);
886 /* Most likely '\0' in input. */
887 warnx(_("Unexpected EOF in %s file"), ftype
);
889 warn(_("Unknown error in %s file"), ftype
);
893 /* Read the file and respond to user input. Beware: long and ugly. */
894 static void pgfile(FILE *f
, const char *name
)
896 off_t pos
, oldpos
, fpos
;
897 /* These are the line counters:
898 * line the line desired to display
899 * fline the current line of the input file
900 * bline the current line of the file buffer
901 * oldline the line before a search was started
902 * eofline the last line of the file if it is already reached
903 * dline the line on the display */
904 off_t line
= 0, fline
= 0, bline
= 0, oldline
= 0, eofline
= 0;
907 unsigned searchcount
= 0;
908 /* Advance to EOF immediately. */
910 /* EOF has been reached by `line'. */
912 /* f and fbuf refer to the same file. */
919 /* fbuf an exact copy of the input file as it gets read
920 * find index table for input, one entry per line
921 * save for the s command, to save to a file */
922 FILE *fbuf
, *find
, *save
;
925 /* Just copy stdin to stdout. */
926 while ((sz
= fread(b
, sizeof *b
, READBUF
, f
)) != 0)
927 write_all(STDOUT_FILENO
, b
, sz
);
934 if ((fpos
= fseeko(f
, (off_t
)0, SEEK_SET
)) == -1)
941 if (fbuf
== NULL
|| find
== NULL
) {
942 warn(_("Cannot create tempfile"));
949 rerror
= regcomp(&re
, searchfor
, REG_NOSUB
| REG_NEWLINE
);
951 mesg(_("RE error: "));
952 regerror(rerror
, &re
, b
, READBUF
);
959 for (line
= startline
;;) {
960 /* Get a line from input file or buffer. */
962 fseeko(find
, line
* sizeof pos
, SEEK_SET
);
963 if (fread(&pos
, sizeof pos
, 1, find
) == 0)
964 tmperr(find
, "index");
965 fseeko(find
, (off_t
)0, SEEK_END
);
966 fseeko(fbuf
, pos
, SEEK_SET
);
967 if (fgets(b
, READBUF
, fbuf
) == NULL
)
968 tmperr(fbuf
, "buffer");
969 } else if (eofline
== 0) {
970 fseeko(find
, (off_t
)0, SEEK_END
);
973 fseeko(fbuf
, (off_t
)0, SEEK_END
);
975 if ((sig
= setjmp(jmpenv
)) != 0) {
976 /* We got a signal. */
979 fseeko(fbuf
, pos
, SEEK_SET
);
985 fseeko(f
, fpos
, SEEK_SET
);
987 p
= fgets(b
, READBUF
, f
);
989 if ((fpos
= ftello(f
)) == -1)
993 if (p
== NULL
|| *b
== '\0') {
1002 fwrite_all(&pos
, sizeof pos
, 1, find
);
1006 while (*(p
= endline(ttycols
,
1009 pos
= oldpos
+ (p
- b
);
1019 } while (line
> bline
++);
1024 if (search
== FORWARD
&& remembered
== 1) {
1027 search
= searchcount
= 0;
1028 mesg(_("Pattern not found"));
1034 if (regexec(&re
, b
, 0, NULL
, 0) == 0) {
1037 if (searchcount
== 0) {
1039 switch (searchdisplay
) {
1044 line
-= pagelen
/ 2 + 1;
1054 /* We are not searching. */
1056 } else if (*b
!= '\0') {
1057 if (cflag
&& clear_screen
) {
1060 tputs(clear_screen
, STDOUT_FILENO
,
1066 if (eofline
&& line
== eofline
)
1069 if ((sig
= setjmp(jmpenv
)) != 0) {
1070 /* We got a signal. */
1075 p
= endline(ttycols
, b
);
1079 write_all(STDOUT_FILENO
, b
, sz
);
1083 if (dline
>= pagelen
|| eof
) {
1084 /* Time for prompting! */
1085 if (eof
&& seekeof
) {
1087 if (line
>= pagelen
)
1096 if (fline
== 0 || eflag
)
1100 prompt((line
- 1) / pagelen
+ 1);
1103 /* Search forward. */
1106 searchcount
= cmd
.count
;
1108 if (p
!= NULL
&& *p
) {
1109 if (remembered
== 1)
1111 rerror
= regcomp(&re
, p
,
1115 mesg(_("RE error: "));
1116 sz
= regerror(rerror
, &re
,
1122 } else if (remembered
== 0) {
1123 mesg(_("No remembered search string"));
1129 /* Search backward. */
1132 searchcount
= cmd
.count
;
1134 if (p
!= NULL
&& *p
) {
1135 if (remembered
== 1)
1137 rerror
= regcomp(&re
, p
,
1141 mesg(_("RE error: "));
1142 regerror(rerror
, &re
,
1148 } else if (remembered
== 0) {
1149 mesg(_("No remembered search string"));
1156 fseeko(find
, --line
* sizeof pos
,
1158 if (fread(&pos
, sizeof pos
, 1, find
) ==
1160 tmperr(find
, "index");
1161 fseeko(find
, (off_t
)0, SEEK_END
);
1162 fseeko(fbuf
, pos
, SEEK_SET
);
1163 if (fgets(b
, READBUF
, fbuf
) == NULL
)
1164 tmperr(fbuf
, "buffer");
1166 if (regexec(&re
, b
, 0, NULL
, 0) == 0)
1168 if (searchcount
== 0)
1173 search
= searchcount
= 0;
1174 mesg(_("Pattern not found"));
1177 eof
= search
= dline
= 0;
1179 switch (searchdisplay
) {
1184 line
-= pagelen
/ 2;
1198 while (*++p
== ' ') ;
1201 save
= fopen(p
, "wb");
1204 mesg(_("cannot open "));
1207 mesg(strerror(cmd
.count
));
1210 /* Advance to EOF. */
1211 fseeko(find
, (off_t
)0, SEEK_END
);
1214 fseeko(fbuf
, (off_t
)0,
1217 if (fgets(b
, READBUF
, f
) == NULL
) {
1223 fwrite_all(&pos
, sizeof pos
, 1, find
);
1227 while (*(p
= endline(ttycols
,
1230 pos
= oldpos
+ (p
- b
);
1241 fseeko(fbuf
, (off_t
)0, SEEK_SET
);
1242 while ((sz
= fread(b
, sizeof *b
, READBUF
,
1244 /* No error check for compat. */
1245 fwrite_all(b
, sizeof *b
, sz
, save
);
1247 if (close_stream(save
) != 0) {
1249 mesg(_("write failed"));
1252 mesg(strerror(cmd
.count
));
1255 fseeko(fbuf
, (off_t
)0, SEEK_END
);
1260 if (*cmd
.cmdline
!= 'l')
1263 cmd
.count
= 1; /* compat */
1264 if (isdigit(cuc(*cmd
.cmdline
))) {
1265 line
= cmd
.count
- 2;
1268 if (cmd
.count
!= 1) {
1269 line
+= cmd
.count
- 1 - pagelen
;
1273 /* Nothing to do if (count == 1) */
1277 /* Half screen forward. */
1278 case '\004': /* ^D */
1279 if (*cmd
.cmdline
!= cmd
.key
)
1282 cmd
.count
= 1; /* compat */
1283 line
+= (cmd
.count
* pagelen
/ 2)
1291 cmd
.count
= 1; /* compat */
1292 line
+= cmd
.count
* pagelen
- 2;
1295 if (*cmd
.cmdline
!= 'f')
1299 if (eofline
&& line
>= eofline
)
1305 /* Just a number, or '-', or <newline>. */
1307 cmd
.count
= 1; /* compat */
1308 if (isdigit(cuc(*cmd
.cmdline
)))
1309 line
= (cmd
.count
- 1) * pagelen
- 2;
1311 line
+= (cmd
.count
- 1)
1312 * (pagelen
- 1) - 2;
1313 if (*cmd
.cmdline
!= '\0')
1315 if (cmd
.count
!= 1) {
1324 /* Advance to EOF. */
1333 case '\014': /* ^L */
1334 /* Repaint screen. */
1336 if (line
>= pagelen
)
1345 mesg(program_invocation_short_name
);
1346 mesg(_(": !command not allowed in "
1351 write_all(STDOUT_FILENO
, cmd
.cmdline
,
1352 strlen(cmd
.cmdline
));
1353 write_all(STDOUT_FILENO
, "\n", 1);
1354 my_sigset(SIGINT
, SIG_IGN
);
1355 my_sigset(SIGQUIT
, SIG_IGN
);
1356 switch (cpid
= fork()) {
1359 const char *sh
= getenv("SHELL");
1365 if (isatty(0) == 0) {
1367 open(tty
, O_RDONLY
);
1371 my_sigset(SIGINT
, oldint
);
1372 my_sigset(SIGQUIT
, oldquit
);
1373 my_sigset(SIGTERM
, oldterm
);
1375 cmd
.cmdline
+ 1, NULL
);
1376 warn(_("failed to execute %s"), sh
);
1381 mesg(_("fork() failed, "
1382 "try again later\n"));
1385 while (wait(NULL
) != cpid
) ;
1387 my_sigset(SIGINT
, sighandler
);
1388 my_sigset(SIGQUIT
, sighandler
);
1395 const char *help
= _(helpscreen
);
1396 write_all(STDOUT_FILENO
, copyright
,
1398 write_all(STDOUT_FILENO
, help
,
1406 nextfile
= cmd
.count
;
1414 /* Previous file. */
1417 nextfile
= 0 - cmd
.count
;
1431 /* Set window size. */
1434 if (*cmd
.cmdline
!= cmd
.key
)
1435 pagelen
= ++cmd
.count
;
1443 if (cflag
&& dline
== 1) {
1456 static int parse_arguments(int arg
, int argc
, char **argv
)
1461 files
.last
= arg
+ argc
- 1;
1462 for (; argv
[arg
]; arg
+= nextfile
) {
1464 files
.current
= arg
;
1466 static int firsttime
;
1468 if (firsttime
> 1) {
1469 mesg(_("(Next file: "));
1480 nextfile
= cmd
.count
;
1488 /* Previous file. */
1491 nextfile
= 0 - cmd
.count
;
1506 if (strcmp(argv
[arg
], "-") == 0)
1509 input
= fopen(argv
[arg
], "r");
1510 if (input
== NULL
) {
1511 warn("%s", argv
[arg
]);
1516 if (ontty
== 0 && argc
> 2) {
1517 /* Use the prefix as specified by SUSv2. */
1518 write_all(STDOUT_FILENO
, "::::::::::::::\n", 15);
1519 write_all(STDOUT_FILENO
, argv
[arg
], strlen(argv
[arg
]));
1520 write_all(STDOUT_FILENO
, "\n::::::::::::::\n", 16);
1522 pgfile(input
, argv
[arg
]);
1529 int main(int argc
, char **argv
)
1534 xasprintf(©right
,
1535 _("%s %s Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.\n"),
1536 program_invocation_short_name
, PACKAGE_VERSION
);
1538 setlocale(LC_ALL
, "");
1539 bindtextdomain(PACKAGE
, LOCALEDIR
);
1540 textdomain(PACKAGE
);
1541 atexit(close_stdout
);
1543 if (tcgetattr(STDOUT_FILENO
, &otio
) == 0) {
1545 oldint
= my_sigset(SIGINT
, sighandler
);
1546 oldquit
= my_sigset(SIGQUIT
, sighandler
);
1547 oldterm
= my_sigset(SIGTERM
, sighandler
);
1548 setlocale(LC_CTYPE
, "");
1549 setlocale(LC_COLLATE
, "");
1550 tty
= ttyname(STDOUT_FILENO
);
1551 setupterm(NULL
, STDOUT_FILENO
, &tinfostat
);
1553 helpscreen
= _(helpscreen
);
1555 for (arg
= 1; argv
[arg
]; arg
++) {
1556 if (*argv
[arg
] == '+')
1558 if (*argv
[arg
] != '-' || argv
[arg
][1] == '\0')
1562 if (!strcmp(argv
[arg
], "--help")) {
1566 if (!strcmp(argv
[arg
], "--version")) {
1567 printf(UTIL_LINUX_VERSION
);
1568 return EXIT_SUCCESS
;
1571 for (i
= 1; argv
[arg
][i
]; i
++) {
1572 switch (argv
[arg
][i
]) {
1574 if (i
!= 1 || argv
[arg
][i
+ 1])
1575 invopt(&argv
[arg
][i
]);
1587 pagelen
= strtol_or_err(argv
[arg
] + 1,
1588 _("failed to parse number of lines per page"));
1604 if (argv
[arg
][i
+ 1]) {
1605 pstring
= &argv
[arg
][i
+ 1];
1606 } else if (argv
[++arg
]) {
1608 pstring
= argv
[arg
];
1621 printf(UTIL_LINUX_VERSION
);
1622 return EXIT_SUCCESS
;
1624 invopt(&argv
[arg
][i
]);
1631 for (arg
= 1; argv
[arg
]; arg
++) {
1632 if (*argv
[arg
] == '-') {
1633 if (argv
[arg
][1] == '-') {
1637 if (argv
[arg
][1] == '\0')
1639 if (argv
[arg
][1] == 'p' && argv
[arg
][2] == '\0')
1643 if (*argv
[arg
] != '+')
1646 switch (*(argv
[arg
] + 1)) {
1660 startline
= strtol_or_err(argv
[arg
] + 1,
1661 _("failed to parse number of lines per page"));
1664 searchfor
= argv
[arg
] + 2;
1665 if (*searchfor
== '\0')
1667 p
= searchfor
+ strlen(searchfor
) - 1;
1670 if (*searchfor
== '\0')
1678 pgfile(stdin
, "stdin");
1680 exitstatus
= parse_arguments(arg
, argc
, argv
);