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 */
34 #include <sys/types.h>
38 #include <sys/ioctl.h>
40 #include <sys/termios.h>
56 #elif defined(HAVE_NCURSES_NCURSES_H)
57 #include <ncurses/ncurses.h>
67 #define READBUF LINE_MAX /* size of input buffer */
68 #define CMDBUF 255 /* size of command buffer */
69 #define TABSIZE 8 /* spaces consumed by tab character */
72 * Avoid the message "`var' might be clobbered by `longjmp' or `vfork'"
74 #define CLOBBGRD(a) (void)(&(a));
76 #define cuc(c) ((c) & 0377)
78 enum { FORWARD = 1, BACKWARD = 2 }; /* search direction */
79 enum { TOP, MIDDLE, BOTTOM }; /* position of matching line */
82 * States for syntax-aware command line editor.
103 char pattern[CMDBUF];
108 * 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 char *progname; /* program name */
121 unsigned ontty; /* whether running on tty device */
122 unsigned exitstatus; /* exit status */
123 int pagelen = 23; /* lines on a single screen page */
124 int ttycols = 79; /* screen columns (starting at 0) */
125 struct termios otio; /* old termios settings */
126 int tinfostat = -1; /* terminfo routines initialized */
127 int searchdisplay = TOP; /* matching line position */
128 regex_t re; /* regular expression to search for */
129 int remembered; /* have a remembered search string */
130 int cflag; /* clear screen before each page */
131 int eflag; /* suppress (EOF) */
132 int fflag; /* do not split lines */
133 int nflag; /* no newline for commands required */
134 int rflag; /* "restricted" pg */
135 int sflag; /* use standout mode */
136 char *pstring = ":"; /* prompt string */
137 char *searchfor; /* search pattern from argv[] */
138 int havepagelen; /* page length is manually defined */
139 long startline; /* start line from argv[] */
140 int nextfile = 1; /* files to advance */
141 jmp_buf jmpenv; /* jump from signal handlers */
142 int canjump; /* jmpenv is valid */
143 wchar_t wbuf[READBUF]; /* used in several widechar routines */
145 const char *copyright =
146 "@(#)pg 1.44 2/8/02. Copyright (c) 2000-2001 Gunnar Ritter. ";
147 const char *helpscreen = N_("All rights reserved.\n\
148 -------------------------------------------------------\n\
150 q or Q quit program\n\
151 <newline> next page\n\
152 f skip a page forward\n\
153 d or ^D next halfpage\n\
156 /regex/ search forward for regex\n\
157 ?regex? or ^regex^ search backward for regex\n\
158 . or ^L redraw screen\n\
159 w or z set page size and go to next page\n\
160 s filename save current file to filename\n\
161 !command shell escape\n\
162 p go to previous file\n\
165 Many commands accept preceding numbers, for example:\n\
166 +1<newline> (next page); -1<newline> (previous page); 1<newline> (first page).\n\
168 See pg(1) for more information.\n\
169 -------------------------------------------------------\n");
172 static int fseeko(FILE *f, off_t off, int whence) {
173 return fseek(f, (long) off, whence);
175 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) {
188 if (sigemptyset(&sigs) || sigaddset(&sigs, sig))
190 return sigprocmask(SIG_UNBLOCK, &sigs, NULL);
192 typedef void (*my_sighandler_t)(int);
193 static my_sighandler_t my_sigset(int sig, my_sighandler_t disp) {
194 struct sigaction act, oact;
196 act.sa_handler = disp;
197 if (sigemptyset(&act.sa_mask))
200 if (sigaction(sig, &act, &oact))
202 if (my_sigrelse(sig))
204 return oact.sa_handler;
214 exit(status < 0100 ? status : 077);
218 * Usage message and similar routines.
223 fprintf(stderr, _("%s: Usage: %s [-number] [-p string] [-cefnrs] "
224 "[+line] [+/pattern/] [files]\n"),
232 fprintf(stderr, _("%s: option requires an argument -- %s\n"),
240 fprintf(stderr, _("%s: illegal option -- %s\n"), progname, s);
246 * A mbstowcs()-alike function that transparently handles invalid sequences.
249 xmbstowcs(wchar_t *pwcs, const char *s, size_t nwcs)
254 ignore_result( mbtowc(pwcs, NULL, MB_CUR_MAX) ); /* reset shift state */
256 if ((c = mbtowc(pwcs, s, MB_CUR_MAX)) < 0) {
266 ignore_result( mbtowc(pwcs, NULL, MB_CUR_MAX) );
272 * Helper function for tputs().
278 return write_all(1, &c, 1) == 0 ? 1 : -1;
282 * Write messages to terminal.
289 if (*message != '\n' && sflag)
290 vidputs(A_STANDOUT, outcap);
291 write_all(1, message, strlen(message));
292 if (*message != '\n' && sflag)
293 vidputs(A_NORMAL, outcap);
297 * Get the window size.
302 static int initialized, envlines, envcols, deflines, defcols;
304 struct winsize winsz;
309 if (initialized == 0) {
310 if ((p = getenv("LINES")) != NULL && *p != '\0')
311 if ((envlines = atoi(p)) < 0)
313 if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
314 if ((envcols = atoi(p)) < 0)
316 /* terminfo values. */
317 if (tinfostat != 1 || columns == 0)
321 if (tinfostat != 1 || lines == 0)
328 badioctl = ioctl(1, TIOCGWINSZ, &winsz);
331 ttycols = envcols - 1;
334 ttycols = winsz.ws_col - 1;
337 ttycols = defcols - 1;
338 if (havepagelen == 0) {
340 pagelen = envlines - 1;
343 pagelen = winsz.ws_row - 1;
346 pagelen = deflines - 1;
351 * Message if skipping parts of files.
357 mesg(_("...skipping forward\n"));
359 mesg(_("...skipping backward\n"));
363 * Signal handler while reading from input file.
366 sighandler(int signum)
368 if (canjump && (signum == SIGINT || signum == SIGQUIT))
369 longjmp(jmpenv, signum);
370 tcsetattr(1, TCSADRAIN, &otio);
375 * Check whether the requested file was specified on the command line.
380 if (files.current + nextfile >= files.last) {
381 mesg(_("No next file"));
384 if (files.current + nextfile < files.first) {
385 mesg(_("No previous file"));
393 * Return the last character that will fit on the line at col columns
394 * in case MB_CUR_MAX > 1.
397 endline_for_mb(unsigned col, char *s)
405 if ((wl = xmbstowcs(wbuf, t, sizeof wbuf - 1)) == (size_t)-1)
408 while (*p != L'\0') {
418 * No cursor movement.
435 pos += TABSIZE - (pos % TABSIZE);
441 pos += wcwidth(L'?');
446 else if (pos > col + 1)
448 * wcwidth() found a character that
449 * has multiple columns. What happens
450 * now? Assume the terminal will print
451 * the entire character onto the next
466 if ((pos = wcstombs(NULL, p, READBUF)) == (size_t) -1)
473 * Return the last character that will fit on the line at col columns.
476 endline(unsigned col, char *s)
483 return endline_for_mb(col, s);
496 * No cursor movement.
513 pos += TABSIZE - (pos % TABSIZE);
534 * Clear the current line on the terminal's screen.
539 char *buf = xmalloc(ttycols + 2);
540 memset(buf, ' ', ttycols + 2);
542 buf[ttycols + 1] = '\r';
543 write_all(1, buf, ttycols + 2);
548 * Evaluate a command character's semantics.
554 case '1': case '2': case '3': case '4': case '5':
555 case '6': case '7': case '8': case '9': case '0':
560 case 'l': case 'd': case '\004': case 'f': case 'z':
561 case '.': case '\014': case '$': case 'n': case 'p':
562 case 'w': case 'h': case 'q': case 'Q':
564 case '/': case '?': case '^':
568 case 'm': case 'b': case 't':
573 tputs(bell, 1, outcap);
580 * Get the count and ignore last character of string.
583 getcount(char *cmdstr)
591 buf = xmalloc(strlen(cmdstr) + 1);
593 if (cmd.key != '\0') {
594 if (cmd.key == '/' || cmd.key == '?' || cmd.key == '^') {
595 if ((p = strchr(buf, cmd.key)) != NULL)
598 *(buf + strlen(buf) - 1) = '\0';
602 if (buf[0] == '-' && buf[1] == '\0') {
615 * Read what the user writes at the prompt. This is tricky because
616 * we check for valid input.
619 prompt(long long pageno)
625 char b[LINE_MAX], *p;
628 if ((p = strstr(pstring, "%d")) == NULL) {
632 sprintf(b + (p - pstring), "%lld", pageno);
637 cmd.key = cmd.addon = cmd.cmdline[0] = '\0';
640 tio.c_lflag &= ~(ICANON | ECHO);
643 tcsetattr(1, TCSADRAIN, &tio);
644 tcflush(1, TCIFLUSH);
646 switch (read(1, &key, 1)) {
651 if (key == tio.c_cc[VERASE]) {
653 write_all(1, "\b \b", 3);
654 cmd.cmdline[--cmd.cmdlen] = '\0';
668 if (cmd.cmdline[cmd.cmdlen - 1]
671 while(cmd.cmdline[cmd.cmdlen
678 if (strchr(cmd.cmdline, cmd.key)
687 if (cmd.cmdlen == 0) {
693 if (key == tio.c_cc[VKILL]) {
696 cmd.cmdline[0] = '\0';
701 if (key == '\n' || (nflag && state == COUNT && key == ' '))
703 if (cmd.cmdlen >= CMDBUF - 1)
718 if (getstate(key) != ADDON_FIN)
727 searchdisplay = MIDDLE;
730 searchdisplay = BOTTOM;
738 state = getstate(key);
741 if (cmd.cmdlen != 0) {
756 write_all(1, &key, 1);
757 cmd.cmdline[cmd.cmdlen++] = key;
758 cmd.cmdline[cmd.cmdlen] = '\0';
759 if (nflag && state == CMD_FIN)
763 tcsetattr(1, TCSADRAIN, &otio);
765 cmd.count = getcount(cmd.cmdline);
770 * Remove backspace formatting, for searches
771 * in case MB_CUR_MAX > 1.
778 size_t l = strlen(s), wl;
781 if ((wl = xmbstowcs(wbuf, p, sizeof wbuf)) == (size_t)-1)
783 for (wp = wbuf, wq = wbuf, i = 0; *wp != L'\0' && i < wl;
795 wcstombs(s, wp, l + 1);
802 * Remove backspace formatting, for searches.
811 return colb_for_mb(s);
814 for (q = s; *p != '\0'; p++, q++) {
830 * Convert nonprintable characters to spaces
831 * in case MB_CUR_MAX > 1.
834 makeprint_for_mb(char *s, size_t l)
840 if ((wl = xmbstowcs(wbuf, t, sizeof wbuf)) == (size_t)-1)
843 if (!iswprint(*wp) && *wp != L'\n' && *wp != L'\r'
844 && *wp != L'\b' && *wp != L'\t')
854 * Convert nonprintable characters to spaces.
857 makeprint(char *s, size_t l)
860 if (MB_CUR_MAX > 1) {
861 makeprint_for_mb(s, l);
867 if (!isprint(cuc(*s)) && *s != '\n' && *s != '\r'
868 && *s != '\b' && *s != '\t')
875 * Strip backslash characters from the given string.
887 } while (*s++ != '\0');
891 * Extract the search pattern off the command line.
898 if (cmd.addon == '\0')
899 p = cmd.cmdline + strlen(cmd.cmdline) - 1;
901 p = cmd.cmdline + strlen(cmd.cmdline) - 2;
906 if ((p = strchr(cmd.cmdline, cmd.key)) != NULL) {
914 * Process errors that occurred in temporary file operations.
917 tmperr(FILE *f, char *ftype)
920 fprintf(stderr, _("%s: Read error from %s file\n"),
924 * Most likely '\0' in input.
926 fprintf(stderr, _("%s: Unexpected EOF in %s file\n"),
929 fprintf(stderr, _("%s: Unknown error in %s file\n"),
935 * perror()-like, but showing the program's name.
938 pgerror(int eno, char *string)
940 fprintf(stderr, "%s: %s: %s\n", progname, string, strerror(eno));
944 * Read the file and respond to user input.
945 * Beware: long and ugly.
948 pgfile(FILE *f, char *name)
950 off_t pos, oldpos, fpos;
951 off_t line = 0, fline = 0, bline = 0, oldline = 0, eofline = 0;
954 * These are the line counters:
955 * line the line desired to display
956 * fline the current line of the input file
957 * bline the current line of the file buffer
958 * oldline the line before a search was started
959 * eofline the last line of the file if it is already reached
960 * dline the line on the display
963 unsigned searchcount = 0;
965 * Advance to EOF immediately.
969 * EOF has been reached by `line'.
973 * f and fbuf refer to the same file.
982 * fbuf an exact copy of the input file as it gets read
983 * find index table for input, one entry per line
984 * save for the s command, to save to a file
986 FILE *fbuf, *find, *save;
988 /* silence compiler - it may warn about longjmp() */
997 CLOBBGRD(searchcount);
1006 * Just copy stdin to stdout.
1008 while ((sz = fread(b, sizeof *b, READBUF, f)) != 0)
1009 write_all(1, b, sz);
1011 pgerror(errno, name);
1016 if ((fpos = fseeko(f, (off_t)0, SEEK_SET)) == -1)
1023 if (fbuf == NULL || find == NULL) {
1024 fprintf(stderr, _("%s: Cannot create tempfile\n"), progname);
1031 rerror = regcomp(&re, searchfor, REG_NOSUB | REG_NEWLINE);
1033 mesg(_("RE error: "));
1034 regerror(rerror, &re, b, READBUF);
1041 for (line = startline; ; ) {
1043 * Get a line from input file or buffer.
1046 fseeko(find, line * sizeof pos, SEEK_SET);
1047 if (fread(&pos, sizeof pos, 1, find) == 0)
1048 tmperr(find, "index");
1049 fseeko(find, (off_t)0, SEEK_END);
1050 fseeko(fbuf, pos, SEEK_SET);
1051 if (fgets(b, READBUF, fbuf) == NULL)
1052 tmperr(fbuf, "buffer");
1053 } else if (eofline == 0) {
1054 fseeko(find, (off_t)0, SEEK_END);
1057 fseeko(fbuf, (off_t)0, SEEK_END);
1059 if ((sig = setjmp(jmpenv)) != 0) {
1065 fseeko(fbuf, pos, SEEK_SET);
1071 fseeko(f, fpos, SEEK_SET);
1073 p = fgets(b, READBUF, f);
1075 if ((fpos = ftello(f)) == -1)
1076 pgerror(errno, name);
1079 if (p == NULL || *b == '\0') {
1081 pgerror(errno, name);
1088 fwrite_all(&pos, sizeof pos, 1, find);
1092 while (*(p = endline(ttycols,
1095 pos = oldpos + (p - b);
1105 } while (line > bline++);
1112 if (search == FORWARD && remembered == 1) {
1115 search = searchcount = 0;
1116 mesg(_("Pattern not found"));
1122 if (regexec(&re, b, 0, NULL, 0) == 0) {
1125 if (searchcount == 0) {
1127 switch (searchdisplay) {
1132 line -= pagelen / 2 + 1;
1141 } else if (eof) { /*
1142 * We are not searching.
1145 } else if (*b != '\0') {
1146 if (cflag && clear_screen) {
1149 tputs(clear_screen, 1, outcap);
1154 if (eofline && line == eofline)
1157 if ((sig = setjmp(jmpenv)) != 0) {
1165 p = endline(ttycols, b);
1169 write_all(1, b, sz);
1173 if (dline >= pagelen || eof) {
1175 * Time for prompting!
1177 if (eof && seekeof) {
1179 if (line >= pagelen)
1188 if (fline == 0 || eflag)
1192 prompt((line - 1) / pagelen + 1);
1200 searchcount = cmd.count;
1202 if (p != NULL && *p) {
1203 if (remembered == 1)
1205 rerror = regcomp(&re, p,
1206 REG_NOSUB | REG_NEWLINE);
1208 mesg(_("RE error: "));
1209 sz = regerror(rerror, &re,
1215 } else if (remembered == 0) {
1216 mesg(_("No remembered search string"));
1227 searchcount = cmd.count;
1229 if (p != NULL && *p) {
1230 if (remembered == 1)
1232 rerror = regcomp(&re, p,
1233 REG_NOSUB | REG_NEWLINE);
1235 mesg(_("RE error: "));
1236 regerror(rerror, &re,
1242 } else if (remembered == 0) {
1243 mesg(_("No remembered search string"));
1250 fseeko(find, --line * sizeof pos,
1252 if(fread(&pos, sizeof pos, 1,find)==0)
1253 tmperr(find, "index");
1254 fseeko(find, (off_t)0, SEEK_END);
1255 fseeko(fbuf, pos, SEEK_SET);
1256 if (fgets(b, READBUF, fbuf) == NULL)
1257 tmperr(fbuf, "buffer");
1259 if (regexec(&re, b, 0, NULL, 0) == 0)
1261 if (searchcount == 0)
1266 search = searchcount = 0;
1267 mesg(_("Pattern not found"));
1270 eof = search = dline = 0;
1272 switch (searchdisplay) {
1277 line -= pagelen / 2;
1293 while (*++p == ' ');
1296 save = fopen(p, "wb");
1299 mesg(_("Cannot open "));
1302 mesg(strerror(cmd.count));
1308 fseeko(find, (off_t)0, SEEK_END);
1311 fseeko(fbuf,(off_t)0,SEEK_END);
1313 if (fgets(b, READBUF, f) == NULL) {
1319 fwrite_all(&pos, sizeof pos, 1, find);
1323 while (*(p = endline(ttycols,
1326 pos = oldpos + (p - b);
1337 fseeko(fbuf, (off_t)0, SEEK_SET);
1338 while ((sz = fread(b, sizeof *b, READBUF,
1341 * No error check for compat.
1343 fwrite_all(b, sizeof *b, sz, save);
1346 fseeko(fbuf, (off_t)0, SEEK_END);
1353 if (*cmd.cmdline != 'l')
1356 cmd.count = 1; /* compat */
1357 if (isdigit(cuc(*cmd.cmdline))) {
1358 line = cmd.count - 2;
1361 if (cmd.count != 1) {
1362 line += cmd.count - 1
1368 * Nothing to do if count==1.
1374 * Half screen forward.
1376 case '\004': /* ^D */
1377 if (*cmd.cmdline != cmd.key)
1380 cmd.count = 1; /* compat */
1381 line += (cmd.count * pagelen / 2)
1391 cmd.count = 1; /* compat */
1392 line += cmd.count * pagelen - 2;
1395 if (*cmd.cmdline != 'f')
1399 if (eofline && line >= eofline)
1406 * Just a number, or '-', or <newline>.
1409 cmd.count = 1; /* compat */
1410 if (isdigit(cuc(*cmd.cmdline)))
1411 line = (cmd.count - 1) * pagelen - 2;
1413 line += (cmd.count - 1)
1414 * (pagelen - 1) - 2;
1415 if (*cmd.cmdline != '\0')
1417 if (cmd.count != 1) {
1437 case '\014': /* ^L */
1442 if (line >= pagelen)
1454 mesg(_(": !command not allowed in "
1459 write_all(1, cmd.cmdline,
1460 strlen(cmd.cmdline));
1461 write_all(1, "\n", 1);
1462 my_sigset(SIGINT, SIG_IGN);
1463 my_sigset(SIGQUIT, SIG_IGN);
1464 switch (cpid = fork()) {
1466 p = getenv("SHELL");
1467 if (p == NULL) p = "/bin/sh";
1471 if (isatty(0) == 0) {
1473 open(tty, O_RDONLY);
1477 my_sigset(SIGINT, oldint);
1478 my_sigset(SIGQUIT, oldquit);
1479 my_sigset(SIGTERM, oldterm);
1481 cmd.cmdline + 1, NULL);
1486 mesg(_("fork() failed, "
1487 "try again later\n"));
1490 while (wait(NULL) != cpid);
1492 my_sigset(SIGINT, sighandler);
1493 my_sigset(SIGQUIT, sighandler);
1502 const char *help = _(helpscreen);
1503 write_all(1, copyright + 4, strlen(copyright + 4));
1504 write_all(1, help, strlen(help));
1513 nextfile = cmd.count;
1526 nextfile = 0 - cmd.count;
1547 if (*cmd.cmdline != cmd.key)
1548 pagelen = ++cmd.count;
1556 if (cflag && dline == 1) {
1570 main(int argc, char **argv)
1576 progname = basename(argv[0]);
1578 setlocale(LC_MESSAGES, "");
1579 bindtextdomain(PACKAGE, LOCALEDIR);
1580 textdomain(PACKAGE);
1582 if (tcgetattr(1, &otio) == 0) {
1584 oldint = my_sigset(SIGINT, sighandler);
1585 oldquit = my_sigset(SIGQUIT, sighandler);
1586 oldterm = my_sigset(SIGTERM, sighandler);
1587 setlocale(LC_CTYPE, "");
1588 setlocale(LC_COLLATE, "");
1590 setupterm(NULL, 1, &tinfostat);
1592 helpscreen = _(helpscreen);
1594 for (arg = 1; argv[arg]; arg++) {
1595 if (*argv[arg] == '+')
1597 if (*argv[arg] != '-' || argv[arg][1] == '\0')
1600 for (i = 1; argv[arg][i]; i++) {
1601 switch (argv[arg][i]) {
1603 if (i != 1 || argv[arg][i + 1])
1604 invopt(&argv[arg][i]);
1606 case '1': case '2': case '3': case '4': case '5':
1607 case '6': case '7': case '8': case '9': case '0':
1608 pagelen = atoi(argv[arg] + i);
1624 if (argv[arg][i + 1]) {
1625 pstring = &argv[arg][i + 1];
1626 } else if (argv[++arg]) {
1628 pstring = argv[arg];
1639 invopt(&argv[arg][i]);
1646 for (arg = 1; argv[arg]; arg++) {
1647 if (*argv[arg] == '-') {
1648 if (argv[arg][1] == '-') {
1652 if (argv[arg][1] == '\0')
1654 if (argv[arg][1] == 'p' && argv[arg][2] == '\0')
1658 if (*argv[arg] != '+')
1661 switch (*(argv[arg] + 1)) {
1665 case '1': case '2': case '3': case '4': case '5':
1666 case '6': case '7': case '8': case '9': case '0':
1667 startline = atoi(argv[arg] + 1);
1670 searchfor = argv[arg] + 2;
1671 if (*searchfor == '\0')
1673 p = searchfor + strlen(searchfor) - 1;
1674 if (*p == '/') *p = '\0';
1675 if (*searchfor == '\0')
1683 pgfile(stdin, "stdin");
1686 files.last = arg + argc - 1;
1687 for ( ; argv[arg]; arg += nextfile) {
1689 files.current = arg;
1691 static int firsttime;
1693 if (firsttime > 1) {
1694 mesg(_("(Next file: "));
1707 nextfile = cmd.count;
1720 nextfile = 0 - cmd.count;
1734 if (strcmp(argv[arg], "-") == 0)
1737 input = fopen(argv[arg], "r");
1738 if (input == NULL) {
1739 pgerror(errno, argv[arg]);
1744 if (ontty == 0 && argc > 2) {
1746 * Use the prefix as specified by SUSv2.
1748 write_all(1, "::::::::::::::\n", 15);
1749 write_all(1, argv[arg], strlen(argv[arg]));
1750 write_all(1, "\n::::::::::::::\n", 16);
1752 pgfile(input, argv[arg]);