Update to upstream util-linux 2.20.1
[framework/base/util-linux-ng.git] / text-utils / pg.c
1 /*
2  * pg  - A clone of the System V CRT paging utility.
3  *
4  *      Copyright (c) 2000-2001 Gunnar Ritter. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
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.
14  * 3. [deleted]
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.
18  *
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
29  * SUCH DAMAGE.
30  */
31
32 /* Sccsid @(#)pg.c 1.44 (gritter) 2/8/02 - modified for util-linux */
33
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <sys/stat.h>
37 #ifndef TIOCGWINSZ
38 #include <sys/ioctl.h>
39 #endif
40 #include <sys/termios.h>
41 #include <fcntl.h>
42 #include <regex.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <limits.h>
47 #include <ctype.h>
48 #include <errno.h>
49 #include <unistd.h>
50 #include <signal.h>
51 #include <setjmp.h>
52 #include <libgen.h>
53
54 #ifdef HAVE_NCURSES_H
55 #include <ncurses.h>
56 #elif defined(HAVE_NCURSES_NCURSES_H)
57 #include <ncurses/ncurses.h>
58 #endif
59
60 #include <term.h>
61
62 #include "nls.h"
63 #include "xalloc.h"
64 #include "widechar.h"
65 #include "writeall.h"
66
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 */
70
71 /*
72  * Avoid the message "`var' might be clobbered by `longjmp' or `vfork'"
73  */
74 #define CLOBBGRD(a)     (void)(&(a));
75
76 #define cuc(c)          ((c) & 0377)
77
78 enum { FORWARD = 1, BACKWARD = 2 };     /* search direction */
79 enum { TOP, MIDDLE, BOTTOM };           /* position of matching line */
80
81 /*
82  * States for syntax-aware command line editor.
83  */
84 enum {
85         COUNT,
86         SIGN,
87         CMD_FIN,
88         SEARCH,
89         SEARCH_FIN,
90         ADDON_FIN,
91         STRING,
92         INVALID
93 };
94
95 /*
96  * Current command
97  */
98 struct {
99         char cmdline[CMDBUF];
100         size_t cmdlen;
101         int count;
102         int key;
103         char pattern[CMDBUF];
104         char addon;
105 } cmd;
106
107 /*
108  * Position of file arguments on argv[] to main()
109  */
110 struct {
111         int first;
112         int current;
113         int last;
114 } files;
115
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 */
144
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\
149   h                       this screen\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\
154   l                       next line\n\
155   $                       last page\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\
163   n                       go to next file\n\
164 \n\
165 Many commands accept preceding numbers, for example:\n\
166 +1<newline> (next page); -1<newline> (previous page); 1<newline> (first page).\n\
167 \n\
168 See pg(1) for more information.\n\
169 -------------------------------------------------------\n");
170
171 #ifndef HAVE_FSEEKO
172   static int fseeko(FILE *f, off_t off, int whence) {
173         return fseek(f, (long) off, whence);
174   }
175   static off_t ftello(FILE *f) {
176         return (off_t) ftell(f);
177   }
178 #endif
179
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
184 #else
185 static int my_sigrelse(int sig) {
186         sigset_t sigs;
187
188         if (sigemptyset(&sigs) || sigaddset(&sigs, sig))
189                 return -1;
190         return sigprocmask(SIG_UNBLOCK, &sigs, NULL);
191 }
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;
195
196         act.sa_handler = disp;
197         if (sigemptyset(&act.sa_mask))
198                 return SIG_ERR;
199         act.sa_flags = 0;
200         if (sigaction(sig, &act, &oact))
201                 return SIG_ERR;
202         if (my_sigrelse(sig))
203                 return SIG_ERR;
204         return oact.sa_handler;
205 }
206 #endif
207
208 /*
209  * Quit pg.
210  */
211 static void
212 quit(int status)
213 {
214         exit(status < 0100 ? status : 077);
215 }
216
217 /*
218  * Usage message and similar routines.
219  */
220 static void
221 usage(void)
222 {
223         fprintf(stderr, _("%s: Usage: %s [-number] [-p string] [-cefnrs] "
224                           "[+line] [+/pattern/] [files]\n"),
225                         progname, progname);
226         quit(2);
227 }
228
229 static void
230 needarg(char *s)
231 {
232         fprintf(stderr, _("%s: option requires an argument -- %s\n"),
233                 progname, s);
234         usage();
235 }
236
237 static void
238 invopt(char *s)
239 {
240         fprintf(stderr, _("%s: illegal option -- %s\n"), progname, s);
241         usage();
242 }
243
244 #ifdef HAVE_WIDECHAR
245 /*
246  * A mbstowcs()-alike function that transparently handles invalid sequences.
247  */
248 static size_t
249 xmbstowcs(wchar_t *pwcs, const char *s, size_t nwcs)
250 {
251         size_t n = nwcs;
252         int c;
253
254         ignore_result( mbtowc(pwcs, NULL, MB_CUR_MAX) );        /* reset shift state */
255         while (*s && n) {
256                 if ((c = mbtowc(pwcs, s, MB_CUR_MAX)) < 0) {
257                         s++;
258                         *pwcs = L'?';
259                 } else
260                         s += c;
261                 pwcs++;
262                 n--;
263         }
264         if (n)
265                 *pwcs = L'\0';
266         ignore_result( mbtowc(pwcs, NULL, MB_CUR_MAX) );
267         return nwcs - n;
268 }
269 #endif
270
271 /*
272  * Helper function for tputs().
273  */
274 static int
275 outcap(int i)
276 {
277         char c = i;
278         return write_all(1, &c, 1) == 0 ? 1 : -1;
279 }
280
281 /*
282  * Write messages to terminal.
283  */
284 static void
285 mesg(char *message)
286 {
287         if (ontty == 0)
288                 return;
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);
294 }
295
296 /*
297  * Get the window size.
298  */
299 static void
300 getwinsize(void)
301 {
302         static int initialized, envlines, envcols, deflines, defcols;
303 #ifdef  TIOCGWINSZ
304         struct winsize winsz;
305         int badioctl;
306 #endif
307         char *p;
308
309         if (initialized == 0) {
310                 if ((p = getenv("LINES")) != NULL && *p != '\0')
311                         if ((envlines = atoi(p)) < 0)
312                                 envlines = 0;
313                 if ((p = getenv("COLUMNS")) != NULL && *p != '\0')
314                         if ((envcols = atoi(p)) < 0)
315                                 envcols = 0;
316                 /* terminfo values. */
317                 if (tinfostat != 1 || columns == 0)
318                         defcols = 24;
319                 else
320                         defcols = columns;
321                 if (tinfostat != 1 || lines == 0)
322                         deflines = 80;
323                 else
324                         deflines = lines;
325                 initialized = 1;
326         }
327 #ifdef  TIOCGWINSZ
328         badioctl = ioctl(1, TIOCGWINSZ, &winsz);
329 #endif
330         if (envcols)
331                 ttycols = envcols - 1;
332 #ifdef  TIOCGWINSZ
333         else if (!badioctl)
334                 ttycols = winsz.ws_col - 1;
335 #endif
336         else
337                 ttycols = defcols - 1;
338         if (havepagelen == 0) {
339                 if (envlines)
340                         pagelen = envlines - 1;
341 #ifdef  TIOCGWINSZ
342                 else if (!badioctl)
343                         pagelen = winsz.ws_row - 1;
344 #endif
345                 else
346                         pagelen = deflines - 1;
347         }
348 }
349
350 /*
351  * Message if skipping parts of files.
352  */
353 static void
354 skip(int direction)
355 {
356         if (direction > 0)
357                 mesg(_("...skipping forward\n"));
358         else
359                 mesg(_("...skipping backward\n"));
360 }
361
362 /*
363  * Signal handler while reading from input file.
364  */
365 static void
366 sighandler(int signum)
367 {
368         if (canjump && (signum == SIGINT || signum == SIGQUIT))
369                 longjmp(jmpenv, signum);
370         tcsetattr(1, TCSADRAIN, &otio);
371         quit(exitstatus);
372 }
373
374 /*
375  * Check whether the requested file was specified on the command line.
376  */
377 static int
378 checkf(void)
379 {
380         if (files.current + nextfile >= files.last) {
381                 mesg(_("No next file"));
382                 return 1;
383         }
384         if (files.current + nextfile < files.first) {
385                 mesg(_("No previous file"));
386                 return 1;
387         }
388         return 0;
389 }
390
391 #ifdef HAVE_WIDECHAR
392 /*
393  * Return the last character that will fit on the line at col columns
394  * in case MB_CUR_MAX > 1.
395  */
396 static char *
397 endline_for_mb(unsigned col, char *s)
398 {
399         size_t pos = 0;
400         wchar_t *p = wbuf;
401         wchar_t *end;
402         size_t wl;
403         char *t = s;
404
405         if ((wl = xmbstowcs(wbuf, t, sizeof wbuf - 1)) == (size_t)-1)
406                 return s + 1;
407         wbuf[wl] = L'\0';
408         while (*p != L'\0') {
409                 switch (*p) {
410                         /*
411                          * Cursor left.
412                          */
413                 case L'\b':
414                         if (pos > 0)
415                                 pos--;
416                         break;
417                         /*
418                          * No cursor movement.
419                          */
420                 case L'\a':
421                         break;
422                         /*
423                          * Special.
424                          */
425                 case L'\r':
426                         pos = 0;
427                         break;
428                 case L'\n':
429                         end = p + 1;
430                         goto ended;
431                         /*
432                          * Cursor right.
433                          */
434                 case L'\t':
435                         pos += TABSIZE - (pos % TABSIZE);
436                         break;
437                 default:
438                         if (iswprint(*p))
439                                 pos += wcwidth(*p);
440                         else
441                                 pos += wcwidth(L'?');
442                 }
443                 if (pos > col) {
444                         if (*p == L'\t')
445                                 p++;
446                         else if (pos > col + 1)
447                                 /*
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
452                                  * row.
453                                  */
454                                 p--;
455                         if (*++p == L'\n')
456                                 p++;
457                         end = p;
458                         goto ended;
459                 }
460                 p++;
461         }
462         end = p;
463  ended:
464         *end = L'\0';
465         p = wbuf;
466         if ((pos = wcstombs(NULL, p, READBUF)) == (size_t) -1)
467                 return s + 1;
468         return s + pos;
469 }
470 #endif
471
472 /*
473  * Return the last character that will fit on the line at col columns.
474  */
475 static char *
476 endline(unsigned col, char *s)
477 {
478         unsigned pos = 0;
479         char *t = s;
480
481 #ifdef HAVE_WIDECHAR
482         if (MB_CUR_MAX > 1)
483                 return endline_for_mb(col, s);
484 #endif
485
486         while (*s != '\0') {
487                 switch (*s) {
488                         /*
489                          * Cursor left.
490                          */
491                 case '\b':
492                         if (pos > 0)
493                                 pos--;
494                         break;
495                         /*
496                          * No cursor movement.
497                          */
498                 case '\a':
499                         break;
500                         /*
501                          * Special.
502                          */
503                 case '\r':
504                         pos = 0;
505                         break;
506                 case '\n':
507                         t = s + 1;
508                         goto cend;
509                         /*
510                          * Cursor right.
511                          */
512                 case '\t':
513                         pos += TABSIZE - (pos % TABSIZE);
514                         break;
515                 default:
516                         pos++;
517                 }
518                 if (pos > col) {
519                         if (*s == '\t')
520                                 s++;
521                         if (*++s == '\n')
522                                 s++;
523                         t = s;
524                         goto cend;
525                 }
526                 s++;
527         }
528         t = s;
529  cend:
530         return t;
531 }
532
533 /*
534  * Clear the current line on the terminal's screen.
535  */
536 static void
537 cline(void)
538 {
539         char *buf = xmalloc(ttycols + 2);
540         memset(buf, ' ', ttycols + 2);
541         buf[0] = '\r';
542         buf[ttycols + 1] = '\r';
543         write_all(1, buf, ttycols + 2);
544         free(buf);
545 }
546
547 /*
548  * Evaluate a command character's semantics.
549  */
550 static int
551 getstate(int c)
552 {
553         switch (c) {
554         case '1': case '2': case '3': case '4': case '5':
555         case '6': case '7': case '8': case '9': case '0':
556         case '\0':
557                 return COUNT;
558         case '-': case '+':
559                 return SIGN;
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':
563                 return CMD_FIN;
564         case '/': case '?': case '^':
565                 return SEARCH;
566         case 's': case '!':
567                 return STRING;
568         case 'm': case 'b': case 't':
569                 return ADDON_FIN;
570         default:
571 #ifdef PG_BELL
572                 if (bell)
573                         tputs(bell, 1, outcap);
574 #endif  /*  PG_BELL  */
575                 return INVALID;
576         }
577 }
578
579 /*
580  * Get the count and ignore last character of string.
581  */
582 static int
583 getcount(char *cmdstr)
584 {
585         char *buf;
586         char *p;
587         int i;
588
589         if (*cmdstr == '\0')
590                 return 1;
591         buf = xmalloc(strlen(cmdstr) + 1);
592         strcpy(buf, cmdstr);
593         if (cmd.key != '\0') {
594                 if (cmd.key == '/' || cmd.key == '?' || cmd.key == '^') {
595                         if ((p = strchr(buf, cmd.key)) != NULL)
596                                 *p = '\0';
597                 } else
598                         *(buf + strlen(buf) - 1) = '\0';
599         }
600         if (*buf == '\0')
601                 return 1;
602         if (buf[0] == '-' && buf[1] == '\0') {
603                 i = -1;
604         } else {
605                 if (*buf == '+')
606                         i = atoi(buf + 1);
607                 else
608                         i = atoi(buf);
609         }
610         free(buf);
611         return i;
612 }
613
614 /*
615  * Read what the user writes at the prompt. This is tricky because
616  * we check for valid input.
617  */
618 static void
619 prompt(long long pageno)
620 {
621         struct termios tio;
622         char key;
623         int state = COUNT;
624         int escape = 0;
625         char b[LINE_MAX], *p;
626
627         if (pageno != -1) {
628                 if ((p = strstr(pstring, "%d")) == NULL) {
629                         mesg(pstring);
630                 } else {
631                         strcpy(b, pstring);
632                         sprintf(b + (p - pstring), "%lld", pageno);
633                         strcat(b, p + 2);
634                         mesg(b);
635                 }
636         }
637         cmd.key = cmd.addon = cmd.cmdline[0] = '\0';
638         cmd.cmdlen = 0;
639         tcgetattr(1, &tio);
640         tio.c_lflag &= ~(ICANON | ECHO);
641         tio.c_cc[VMIN] = 1;
642         tio.c_cc[VTIME] = 0;
643         tcsetattr(1, TCSADRAIN, &tio);
644         tcflush(1, TCIFLUSH);
645         for (;;) {
646                 switch (read(1, &key, 1)) {
647                 case 0: quit(0);
648                         /*NOTREACHED*/
649                 case -1: quit(1);
650                 }
651                 if (key == tio.c_cc[VERASE]) {
652                         if (cmd.cmdlen) {
653                                 write_all(1, "\b \b", 3);
654                                 cmd.cmdline[--cmd.cmdlen] = '\0';
655                                 switch (state) {
656                                 case ADDON_FIN:
657                                         state = SEARCH_FIN;
658                                         cmd.addon = '\0';
659                                         break;
660                                 case CMD_FIN:
661                                         cmd.key = '\0';
662                                         state = COUNT;
663                                         break;
664                                 case SEARCH_FIN:
665                                         state = SEARCH;
666                                         /*FALLTHRU*/
667                                 case SEARCH:
668                                         if (cmd.cmdline[cmd.cmdlen - 1]
669                                                         == '\\') {
670                                                 escape = 1;
671                                                 while(cmd.cmdline[cmd.cmdlen
672                                                                 - escape - 1]
673                                                         == '\\') escape++;
674                                                 escape %= 2;
675                                         }
676                                         else {
677                                                 escape = 0;
678                                                 if (strchr(cmd.cmdline, cmd.key)
679                                                         == NULL) {
680                                                         cmd.key = '\0';
681                                                         state = COUNT;
682                                                 }
683                                         }
684                                         break;
685                                 }
686                         }
687                         if (cmd.cmdlen == 0) {
688                                 state = COUNT;
689                                 cmd.key = '\0';
690                         }
691                         continue;
692                 }
693                 if (key == tio.c_cc[VKILL]) {
694                         cline();
695                         cmd.cmdlen = 0;
696                         cmd.cmdline[0] = '\0';
697                         state = COUNT;
698                         cmd.key = '\0';
699                         continue;
700                 }
701                 if (key == '\n' || (nflag && state == COUNT && key == ' '))
702                         break;
703                 if (cmd.cmdlen >= CMDBUF - 1)
704                         continue;
705                 switch (state) {
706                 case STRING:
707                         break;
708                 case SEARCH:
709                         if (!escape) {
710                                 if (key == cmd.key)
711                                         state = SEARCH_FIN;
712                                 if (key == '\\')
713                                         escape = 1;
714                         } else
715                                 escape = 0;
716                         break;
717                 case SEARCH_FIN:
718                         if (getstate(key) != ADDON_FIN)
719                                 continue;
720                         state = ADDON_FIN;
721                         cmd.addon = key;
722                         switch (key) {
723                         case 't':
724                                 searchdisplay = TOP;
725                                 break;
726                         case 'm':
727                                 searchdisplay = MIDDLE;
728                                 break;
729                         case 'b':
730                                 searchdisplay = BOTTOM;
731                                 break;
732                         }
733                         break;
734                 case CMD_FIN:
735                 case ADDON_FIN:
736                         continue;
737                 default:
738                         state = getstate(key);
739                         switch (state) {
740                         case SIGN:
741                                 if (cmd.cmdlen != 0) {
742                                         state = INVALID;
743                                         continue;
744                                 }
745                                 state = COUNT;
746                                 /*FALLTHRU*/
747                         case COUNT:
748                                 break;
749                         case ADDON_FIN:
750                         case INVALID:
751                                 continue;
752                         default:
753                                 cmd.key = key;
754                         }
755                 }
756                 write_all(1, &key, 1);
757                 cmd.cmdline[cmd.cmdlen++] = key;
758                 cmd.cmdline[cmd.cmdlen] = '\0';
759                 if (nflag && state == CMD_FIN)
760                         goto endprompt;
761         }
762 endprompt:
763         tcsetattr(1, TCSADRAIN, &otio);
764         cline();
765         cmd.count = getcount(cmd.cmdline);
766 }
767
768 #ifdef HAVE_WIDECHAR
769 /*
770  * Remove backspace formatting, for searches
771  * in case MB_CUR_MAX > 1.
772  */
773 static char *
774 colb_for_mb(char *s)
775 {
776         char *p = s;
777         wchar_t *wp, *wq;
778         size_t l = strlen(s), wl;
779         unsigned i;
780
781         if ((wl = xmbstowcs(wbuf, p, sizeof wbuf)) == (size_t)-1)
782                 return s;
783         for (wp = wbuf, wq = wbuf, i = 0; *wp != L'\0' && i < wl;
784              wp++, wq++) {
785                 if (*wp == L'\b') {
786                         if (wq != wbuf)
787                                 wq -= 2;
788                         else
789                                 wq--;
790                 } else
791                         *wq = *wp;
792         }
793         *wq = L'\0';
794         wp = wbuf;
795         wcstombs(s, wp, l + 1);
796
797         return s;
798 }
799 #endif
800
801 /*
802  * Remove backspace formatting, for searches.
803  */
804 static char *
805 colb(char *s)
806 {
807         char *p = s, *q;
808
809 #ifdef HAVE_WIDECHAR
810         if (MB_CUR_MAX > 1)
811                 return colb_for_mb(s);
812 #endif
813
814         for (q = s; *p != '\0'; p++, q++) {
815                 if (*p == '\b') {
816                         if (q != s)
817                                 q -= 2;
818                         else
819                                 q--;
820                 } else
821                         *q = *p;
822         }
823         *q = '\0';
824
825         return s;
826 }
827
828 #ifdef HAVE_WIDECHAR
829 /*
830  * Convert nonprintable characters to spaces
831  * in case MB_CUR_MAX > 1.
832  */
833 static void
834 makeprint_for_mb(char *s, size_t l)
835 {
836         char *t = s;
837         wchar_t *wp = wbuf;
838         size_t wl;
839
840         if ((wl = xmbstowcs(wbuf, t, sizeof wbuf)) == (size_t)-1)
841                 return;
842         while (wl--) {
843                 if (!iswprint(*wp) && *wp != L'\n' && *wp != L'\r'
844                     && *wp != L'\b' && *wp != L'\t')
845                         *wp = L'?';
846                 wp++;
847         }
848         wp = wbuf;
849         wcstombs(s, wp, l);
850 }
851 #endif
852
853 /*
854  * Convert nonprintable characters to spaces.
855  */
856 static void
857 makeprint(char *s, size_t l)
858 {
859 #ifdef HAVE_WIDECHAR
860         if (MB_CUR_MAX > 1) {
861                 makeprint_for_mb(s, l);
862                 return;
863         }
864 #endif
865
866         while (l--) {
867                 if (!isprint(cuc(*s)) && *s != '\n' && *s != '\r'
868                     && *s != '\b' && *s != '\t')
869                         *s = '?';
870                 s++;
871         }
872 }
873
874 /*
875  * Strip backslash characters from the given string.
876  */
877 static void
878 striprs(char *s)
879 {
880         char *p = s;
881
882         do {
883                 if (*s == '\\') {
884                         s++;
885                 }
886                 *p++ = *s;
887         } while (*s++ != '\0');
888 }
889
890 /*
891  * Extract the search pattern off the command line.
892  */
893 static char *
894 makepat(void)
895 {
896         char *p;
897
898         if (cmd.addon == '\0')
899                 p = cmd.cmdline + strlen(cmd.cmdline) - 1;
900         else
901                 p = cmd.cmdline + strlen(cmd.cmdline) - 2;
902         if (*p == cmd.key)
903                 *p = '\0';
904         else
905                 *(p + 1) = '\0';
906         if ((p = strchr(cmd.cmdline, cmd.key)) != NULL) {
907                 p++;
908                 striprs(p);
909         }
910         return p;
911 }
912
913 /*
914  * Process errors that occurred in temporary file operations.
915  */
916 static void
917 tmperr(FILE *f, char *ftype)
918 {
919         if (ferror(f))
920                 fprintf(stderr, _("%s: Read error from %s file\n"),
921                         progname, ftype);
922         else if (feof(f))
923                 /*
924                  * Most likely '\0' in input.
925                  */
926                 fprintf(stderr, _("%s: Unexpected EOF in %s file\n"),
927                         progname, ftype);
928         else
929                 fprintf(stderr, _("%s: Unknown error in %s file\n"),
930                         progname, ftype);
931         quit(++exitstatus);
932 }
933
934 /*
935  * perror()-like, but showing the program's name.
936  */
937 static void
938 pgerror(int eno, char *string)
939 {
940         fprintf(stderr, "%s: %s: %s\n", progname, string, strerror(eno));
941 }
942
943 /*
944  * Read the file and respond to user input.
945  * Beware: long and ugly.
946  */
947 static void
948 pgfile(FILE *f, char *name)
949 {
950         off_t pos, oldpos, fpos;
951         off_t line = 0, fline = 0, bline = 0, oldline = 0, eofline = 0;
952         int dline = 0;
953         /*
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
961          */
962         int search = 0;
963         unsigned searchcount = 0;
964         /*
965          * Advance to EOF immediately.
966          */
967         int seekeof = 0;
968         /*
969          * EOF has been reached by `line'.
970          */
971         int eof = 0;
972         /*
973          * f and fbuf refer to the same file.
974          */
975         int nobuf = 0;
976         int sig;
977         int rerror;
978         size_t sz;
979         char b[READBUF + 1];
980         char *p;
981         /*
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
985          */
986         FILE *fbuf, *find, *save;
987
988         /* silence compiler - it may warn about longjmp() */
989         CLOBBGRD(line);
990         CLOBBGRD(fline);
991         CLOBBGRD(bline);
992         CLOBBGRD(oldline);
993         CLOBBGRD(eofline);
994         CLOBBGRD(dline);
995         CLOBBGRD(ttycols);
996         CLOBBGRD(search);
997         CLOBBGRD(searchcount);
998         CLOBBGRD(seekeof);
999         CLOBBGRD(eof);
1000         CLOBBGRD(fpos);
1001         CLOBBGRD(nobuf);
1002         CLOBBGRD(fbuf);
1003
1004         if (ontty == 0) {
1005                 /*
1006                  * Just copy stdin to stdout.
1007                  */
1008                 while ((sz = fread(b, sizeof *b, READBUF, f)) != 0)
1009                         write_all(1, b, sz);
1010                 if (ferror(f)) {
1011                         pgerror(errno, name);
1012                         exitstatus++;
1013                 }
1014                 return;
1015         }
1016         if ((fpos = fseeko(f, (off_t)0, SEEK_SET)) == -1)
1017                 fbuf = tmpfile();
1018         else {
1019                 fbuf = f;
1020                 nobuf = 1;
1021         }
1022         find = tmpfile();
1023         if (fbuf == NULL || find == NULL) {
1024                 fprintf(stderr, _("%s: Cannot create tempfile\n"), progname);
1025                 quit(++exitstatus);
1026         }
1027         if (searchfor) {
1028                 search = FORWARD;
1029                 oldline = 0;
1030                 searchcount = 1;
1031                 rerror = regcomp(&re, searchfor, REG_NOSUB | REG_NEWLINE);
1032                 if (rerror != 0) {
1033                         mesg(_("RE error: "));
1034                         regerror(rerror, &re, b, READBUF);
1035                         mesg(b);
1036                         goto newcmd;
1037                 }
1038                 remembered = 1;
1039         }
1040
1041         for (line = startline; ; ) {
1042                 /*
1043                  * Get a line from input file or buffer.
1044                  */
1045                 if (line < bline) {
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);
1055                         do {
1056                                 if (!nobuf)
1057                                         fseeko(fbuf, (off_t)0, SEEK_END);
1058                                 pos = ftello(fbuf);
1059                                 if ((sig = setjmp(jmpenv)) != 0) {
1060                                         /*
1061                                          * We got a signal.
1062                                          */
1063                                         canjump = 0;
1064                                         my_sigrelse(sig);
1065                                         fseeko(fbuf, pos, SEEK_SET);
1066                                         *b = '\0';
1067                                         dline = pagelen;
1068                                         break;
1069                                 } else {
1070                                         if (nobuf)
1071                                                 fseeko(f, fpos, SEEK_SET);
1072                                         canjump = 1;
1073                                         p = fgets(b, READBUF, f);
1074                                         if (nobuf)
1075                                                 if ((fpos = ftello(f)) == -1)
1076                                                         pgerror(errno, name);
1077                                         canjump = 0;
1078                                 }
1079                                 if (p == NULL || *b == '\0') {
1080                                         if (ferror(f))
1081                                                 pgerror(errno, name);
1082                                         eofline = fline;
1083                                         eof = 1;
1084                                         break;
1085                                 } else {
1086                                         if (!nobuf)
1087                                                 fputs(b, fbuf);
1088                                         fwrite_all(&pos, sizeof pos, 1, find);
1089                                         if (!fflag) {
1090                                                 oldpos = pos;
1091                                                 p = b;
1092                                                 while (*(p = endline(ttycols,
1093                                                                         p))
1094                                                                 != '\0') {
1095                                                         pos = oldpos + (p - b);
1096                                                         fwrite_all(&pos,
1097                                                                 sizeof pos,
1098                                                                 1, find);
1099                                                         fline++;
1100                                                         bline++;
1101                                                 }
1102                                         }
1103                                         fline++;
1104                                 }
1105                         } while (line > bline++);
1106                 } else {
1107                         /*
1108                          * eofline != 0
1109                          */
1110                         eof = 1;
1111                 }
1112                 if (search == FORWARD && remembered == 1) {
1113                         if (eof) {
1114                                 line = oldline;
1115                                 search = searchcount = 0;
1116                                 mesg(_("Pattern not found"));
1117                                 eof = 0;
1118                                 goto newcmd;
1119                         }
1120                         line++;
1121                         colb(b);
1122                         if (regexec(&re, b, 0, NULL, 0) == 0) {
1123                                 searchcount--;
1124                         }
1125                         if (searchcount == 0) {
1126                                 search = dline = 0;
1127                                 switch (searchdisplay) {
1128                                 case TOP:
1129                                         line -= 1;
1130                                         break;
1131                                 case MIDDLE:
1132                                         line -= pagelen / 2 + 1;
1133                                         break;
1134                                 case BOTTOM:
1135                                         line -= pagelen;
1136                                         break;
1137                                 }
1138                                 skip(1);
1139                         }
1140                         continue;
1141                 } else if (eof) {       /*
1142                                          * We are not searching.
1143                                          */
1144                         line = bline;
1145                 } else if (*b != '\0') {
1146                         if (cflag && clear_screen) {
1147                                 switch (dline) {
1148                                 case 0:
1149                                         tputs(clear_screen, 1, outcap);
1150                                         dline = 0;
1151                                 }
1152                         }
1153                         line++;
1154                         if (eofline && line == eofline)
1155                                 eof = 1;
1156                         dline++;
1157                         if ((sig = setjmp(jmpenv)) != 0) {
1158                                 /*
1159                                  * We got a signal.
1160                                  */
1161                                 canjump = 0;
1162                                 my_sigrelse(sig);
1163                                 dline = pagelen;
1164                         } else {
1165                                 p = endline(ttycols, b);
1166                                 sz = p - b;
1167                                 makeprint(b, sz);
1168                                 canjump = 1;
1169                                 write_all(1, b, sz);
1170                                 canjump = 0;
1171                         }
1172                 }
1173                 if (dline >= pagelen || eof) {
1174                         /*
1175                          * Time for prompting!
1176                          */
1177                         if (eof && seekeof) {
1178                                 eof = seekeof = 0;
1179                                 if (line >= pagelen)
1180                                         line -= pagelen;
1181                                 else
1182                                         line = 0;
1183                                 dline = -1;
1184                                 continue;
1185                         }
1186 newcmd:
1187                         if (eof) {
1188                                 if (fline == 0 || eflag)
1189                                         break;
1190                                 mesg(_("(EOF)"));
1191                         }
1192                         prompt((line - 1) / pagelen + 1);
1193                         switch (cmd.key) {
1194                         case '/':
1195                                 /*
1196                                  * Search forward.
1197                                  */
1198                                 search = FORWARD;
1199                                 oldline = line;
1200                                 searchcount = cmd.count;
1201                                 p = makepat();
1202                                 if (p != NULL && *p) {
1203                                         if (remembered == 1)
1204                                                 regfree(&re);
1205                                         rerror = regcomp(&re, p,
1206                                                 REG_NOSUB | REG_NEWLINE);
1207                                         if (rerror != 0) {
1208                                                 mesg(_("RE error: "));
1209                                                 sz = regerror(rerror, &re,
1210                                                                 b, READBUF);
1211                                                 mesg(b);
1212                                                 goto newcmd;
1213                                         }
1214                                         remembered = 1;
1215                                 } else if (remembered == 0) {
1216                                         mesg(_("No remembered search string"));
1217                                         goto newcmd;
1218                                 }
1219                                 continue;
1220                         case '?':
1221                         case '^':
1222                                 /*
1223                                  * Search backward.
1224                                  */
1225                                 search = BACKWARD;
1226                                 oldline = line;
1227                                 searchcount = cmd.count;
1228                                 p = makepat();
1229                                 if (p != NULL && *p) {
1230                                         if (remembered == 1)
1231                                                 regfree(&re);
1232                                         rerror = regcomp(&re, p,
1233                                                 REG_NOSUB | REG_NEWLINE);
1234                                         if (rerror != 0) {
1235                                                 mesg(_("RE error: "));
1236                                                 regerror(rerror, &re,
1237                                                                 b, READBUF);
1238                                                 mesg(b);
1239                                                 goto newcmd;
1240                                         }
1241                                         remembered = 1;
1242                                 } else if (remembered == 0) {
1243                                         mesg(_("No remembered search string"));
1244                                         goto newcmd;
1245                                 }
1246                                 line -= pagelen;
1247                                 if (line <= 0)
1248                                         goto notfound_bw;
1249                                 while (line) {
1250                                         fseeko(find, --line * sizeof pos,
1251                                                         SEEK_SET);
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");
1258                                         colb(b);
1259                                         if (regexec(&re, b, 0, NULL, 0) == 0)
1260                                                 searchcount--;
1261                                         if (searchcount == 0)
1262                                                 goto found_bw;
1263                                 }
1264 notfound_bw:
1265                                 line = oldline;
1266                                 search = searchcount = 0;
1267                                 mesg(_("Pattern not found"));
1268                                 goto newcmd;
1269 found_bw:
1270                                 eof = search = dline = 0;
1271                                 skip(-1);
1272                                 switch (searchdisplay) {
1273                                 case TOP:
1274                                         /* line -= 1; */
1275                                         break;
1276                                 case MIDDLE:
1277                                         line -= pagelen / 2;
1278                                         break;
1279                                 case BOTTOM:
1280                                         if (line != 0)
1281                                                 dline = -1;
1282                                         line -= pagelen;
1283                                         break;
1284                                 }
1285                                 if (line < 0)
1286                                         line = 0;
1287                                 continue;
1288                         case 's':
1289                                 /*
1290                                  * Save to file.
1291                                  */
1292                                 p = cmd.cmdline;
1293                                 while (*++p == ' ');
1294                                 if (*p == '\0')
1295                                         goto newcmd;
1296                                 save = fopen(p, "wb");
1297                                 if (save == NULL) {
1298                                         cmd.count = errno;
1299                                         mesg(_("Cannot open "));
1300                                         mesg(p);
1301                                         mesg(": ");
1302                                         mesg(strerror(cmd.count));
1303                                         goto newcmd;
1304                                 }
1305                                 /*
1306                                  * Advance to EOF.
1307                                  */
1308                                 fseeko(find, (off_t)0, SEEK_END);
1309                                 for (;;) {
1310                                         if (!nobuf)
1311                                                 fseeko(fbuf,(off_t)0,SEEK_END);
1312                                         pos = ftello(fbuf);
1313                                         if (fgets(b, READBUF, f) == NULL) {
1314                                                 eofline = fline;
1315                                                 break;
1316                                         }
1317                                         if (!nobuf)
1318                                                 fputs(b, fbuf);
1319                                         fwrite_all(&pos, sizeof pos, 1, find);
1320                                         if (!fflag) {
1321                                                 oldpos = pos;
1322                                                 p = b;
1323                                                 while (*(p = endline(ttycols,
1324                                                                         p))
1325                                                                 != '\0') {
1326                                                         pos = oldpos + (p - b);
1327                                                         fwrite_all(&pos,
1328                                                                 sizeof pos,
1329                                                                 1, find);
1330                                                         fline++;
1331                                                         bline++;
1332                                                 }
1333                                         }
1334                                         fline++;
1335                                         bline++;
1336                                 }
1337                                 fseeko(fbuf, (off_t)0, SEEK_SET);
1338                                 while ((sz = fread(b, sizeof *b, READBUF,
1339                                                         fbuf)) != 0) {
1340                                         /*
1341                                          * No error check for compat.
1342                                          */
1343                                         fwrite_all(b, sizeof *b, sz, save);
1344                                 }
1345                                 fclose(save);
1346                                 fseeko(fbuf, (off_t)0, SEEK_END);
1347                                 mesg(_("saved"));
1348                                 goto newcmd;
1349                         case 'l':
1350                                 /*
1351                                  * Next line.
1352                                  */
1353                                 if (*cmd.cmdline != 'l')
1354                                         eof = 0;
1355                                 if (cmd.count == 0)
1356                                         cmd.count = 1; /* compat */
1357                                 if (isdigit(cuc(*cmd.cmdline))) {
1358                                         line = cmd.count - 2;
1359                                         dline = 0;
1360                                 } else {
1361                                         if (cmd.count != 1) {
1362                                                 line += cmd.count - 1
1363                                                         - pagelen;
1364                                                 dline = -1;
1365                                                 skip(cmd.count);
1366                                         }
1367                                         /*
1368                                          * Nothing to do if count==1.
1369                                          */
1370                                 }
1371                                 break;
1372                         case 'd':
1373                                 /*
1374                                  * Half screen forward.
1375                                  */
1376                         case '\004':    /* ^D */
1377                                 if (*cmd.cmdline != cmd.key)
1378                                         eof = 0;
1379                                 if (cmd.count == 0)
1380                                         cmd.count = 1; /* compat */
1381                                 line += (cmd.count * pagelen / 2)
1382                                         - pagelen - 1;
1383                                 dline = -1;
1384                                 skip(cmd.count);
1385                                 break;
1386                         case 'f':
1387                                 /*
1388                                  * Skip forward.
1389                                  */
1390                                 if (cmd.count <= 0)
1391                                         cmd.count = 1; /* compat */
1392                                 line += cmd.count * pagelen - 2;
1393                                 if (eof)
1394                                         line += 2;
1395                                 if (*cmd.cmdline != 'f')
1396                                         eof = 0;
1397                                 else if (eof)
1398                                         break;
1399                                 if (eofline && line >= eofline)
1400                                         line -= pagelen;
1401                                 dline = -1;
1402                                 skip(cmd.count);
1403                                 break;
1404                         case '\0':
1405                                 /*
1406                                  * Just a number, or '-', or <newline>.
1407                                  */
1408                                 if (cmd.count == 0)
1409                                         cmd.count = 1; /* compat */
1410                                 if (isdigit(cuc(*cmd.cmdline)))
1411                                         line = (cmd.count - 1) * pagelen - 2;
1412                                 else
1413                                         line += (cmd.count - 1)
1414                                                 * (pagelen - 1) - 2;
1415                                 if (*cmd.cmdline != '\0')
1416                                         eof = 0;
1417                                 if (cmd.count != 1) {
1418                                         skip(cmd.count);
1419                                         dline = -1;
1420                                 } else {
1421                                         dline = 1;
1422                                         line += 2;
1423                                 }
1424                                 break;
1425                         case '$':
1426                                 /*
1427                                  * Advance to EOF.
1428                                  */
1429                                 if (!eof)
1430                                         skip(1);
1431                                 eof = 0;
1432                                 line = LONG_MAX;
1433                                 seekeof = 1;
1434                                 dline = -1;
1435                                 break;
1436                         case '.':
1437                         case '\014': /* ^L */
1438                                 /*
1439                                  * Repaint screen.
1440                                  */
1441                                 eof = 0;
1442                                 if (line >= pagelen)
1443                                         line -= pagelen;
1444                                 else
1445                                         line = 0;
1446                                 dline = 0;
1447                                 break;
1448                         case '!':
1449                                 /*
1450                                  * Shell escape.
1451                                  */
1452                                 if (rflag) {
1453                                         mesg(progname);
1454                                         mesg(_(": !command not allowed in "
1455                                                "rflag mode.\n"));
1456                                 } else {
1457                                         pid_t cpid;
1458
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()) {
1465                                         case 0:
1466                                                 p = getenv("SHELL");
1467                                                 if (p == NULL) p = "/bin/sh";
1468                                                 if (!nobuf)
1469                                                         fclose(fbuf);
1470                                                 fclose(find);
1471                                                 if (isatty(0) == 0) {
1472                                                         close(0);
1473                                                         open(tty, O_RDONLY);
1474                                                 } else {
1475                                                         fclose(f);
1476                                                 }
1477                                                 my_sigset(SIGINT, oldint);
1478                                                 my_sigset(SIGQUIT, oldquit);
1479                                                 my_sigset(SIGTERM, oldterm);
1480                                                 execl(p, p, "-c",
1481                                                         cmd.cmdline + 1, NULL);
1482                                                 pgerror(errno, p);
1483                                                 _exit(0177);
1484                                                 /*NOTREACHED*/
1485                                         case -1:
1486                                                 mesg(_("fork() failed, "
1487                                                        "try again later\n"));
1488                                                 break;
1489                                         default:
1490                                                 while (wait(NULL) != cpid);
1491                                         }
1492                                         my_sigset(SIGINT, sighandler);
1493                                         my_sigset(SIGQUIT, sighandler);
1494                                         mesg("!\n");
1495                                 }
1496                                 goto newcmd;
1497                         case 'h':
1498                         {
1499                                 /*
1500                                  * Help!
1501                                  */
1502                                 const char *help = _(helpscreen);
1503                                 write_all(1, copyright + 4, strlen(copyright + 4));
1504                                 write_all(1, help, strlen(help));
1505                                 goto newcmd;
1506                         }
1507                         case 'n':
1508                                 /*
1509                                  * Next file.
1510                                  */
1511                                 if (cmd.count == 0)
1512                                         cmd.count = 1;
1513                                 nextfile = cmd.count;
1514                                 if (checkf()) {
1515                                         nextfile = 1;
1516                                         goto newcmd;
1517                                 }
1518                                 eof = 1;
1519                                 break;
1520                         case 'p':
1521                                 /*
1522                                  * Previous file.
1523                                  */
1524                                 if (cmd.count == 0)
1525                                         cmd.count = 1;
1526                                 nextfile = 0 - cmd.count;
1527                                 if (checkf()) {
1528                                         nextfile = 1;
1529                                         goto newcmd;
1530                                 }
1531                                 eof = 1;
1532                                 break;
1533                         case 'q':
1534                         case 'Q':
1535                                 /*
1536                                  * Exit pg.
1537                                  */
1538                                 quit(exitstatus);
1539                                 /*NOTREACHED*/
1540                         case 'w':
1541                         case 'z':
1542                                 /*
1543                                  * Set window size.
1544                                  */
1545                                 if (cmd.count < 0)
1546                                         cmd.count = 0;
1547                                 if (*cmd.cmdline != cmd.key)
1548                                         pagelen = ++cmd.count;
1549                                 dline = 1;
1550                                 break;
1551                         }
1552                         if (line <= 0) {
1553                                 line = 0;
1554                                 dline = 0;
1555                         }
1556                         if (cflag && dline == 1) {
1557                                 dline = 0;
1558                                 line--;
1559                         }
1560                 }
1561                 if (eof)
1562                         break;
1563         }
1564         fclose(find);
1565         if (!nobuf)
1566                 fclose(fbuf);
1567 }
1568
1569 int
1570 main(int argc, char **argv)
1571 {
1572         int arg, i;
1573         char *p;
1574         FILE *input;
1575
1576         progname = basename(argv[0]);
1577
1578         setlocale(LC_MESSAGES, "");
1579         bindtextdomain(PACKAGE, LOCALEDIR);
1580         textdomain(PACKAGE);
1581
1582         if (tcgetattr(1, &otio) == 0) {
1583                 ontty = 1;
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, "");
1589                 tty = ttyname(1);
1590                 setupterm(NULL, 1, &tinfostat);
1591                 getwinsize();
1592                 helpscreen = _(helpscreen);
1593         }
1594         for (arg = 1; argv[arg]; arg++) {
1595                 if (*argv[arg] == '+')
1596                         continue;
1597                 if (*argv[arg] != '-' || argv[arg][1] == '\0')
1598                         break;
1599                 argc--;
1600                 for (i = 1; argv[arg][i]; i++) {
1601                         switch (argv[arg][i]) {
1602                         case '-':
1603                                 if (i != 1 || argv[arg][i + 1])
1604                                         invopt(&argv[arg][i]);
1605                                 goto endargs;
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);
1609                                 havepagelen = 1;
1610                                 goto nextarg;
1611                         case 'c':
1612                                 cflag = 1;
1613                                 break;
1614                         case 'e':
1615                                 eflag = 1;
1616                                 break;
1617                         case 'f':
1618                                 fflag = 1;
1619                                 break;
1620                         case 'n':
1621                                 nflag = 1;
1622                                 break;
1623                         case 'p':
1624                                 if (argv[arg][i + 1]) {
1625                                         pstring = &argv[arg][i + 1];
1626                                 } else if (argv[++arg]) {
1627                                         --argc;
1628                                         pstring = argv[arg];
1629                                 } else
1630                                         needarg("-p");
1631                                 goto nextarg;
1632                         case 'r':
1633                                 rflag = 1;
1634                                 break;
1635                         case 's':
1636                                 sflag = 1;
1637                                 break;
1638                         default:
1639                                 invopt(&argv[arg][i]);
1640                         }
1641                 }
1642 nextarg:
1643                 ;
1644         }
1645 endargs:
1646         for (arg = 1; argv[arg]; arg++) {
1647                 if (*argv[arg] == '-') {
1648                         if (argv[arg][1] == '-') {
1649                                 arg++;
1650                                 break;
1651                         }
1652                         if (argv[arg][1] == '\0')
1653                                 break;
1654                         if (argv[arg][1] == 'p' && argv[arg][2] == '\0')
1655                                 arg++;
1656                         continue;
1657                 }
1658                 if (*argv[arg] != '+')
1659                         break;
1660                 argc--;
1661                 switch (*(argv[arg] + 1)) {
1662                 case '\0':
1663                         needarg("+");
1664                         /*NOTREACHED*/
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);
1668                         break;
1669                 case '/':
1670                         searchfor = argv[arg] + 2;
1671                         if (*searchfor == '\0')
1672                                 needarg("+/");
1673                         p = searchfor + strlen(searchfor) - 1;
1674                         if (*p == '/') *p = '\0';
1675                         if (*searchfor == '\0')
1676                                 needarg("+/");
1677                         break;
1678                 default:
1679                         invopt(argv[arg]);
1680                 }
1681         }
1682         if (argc == 1) {
1683                 pgfile(stdin, "stdin");
1684         } else {
1685                 files.first = arg;
1686                 files.last = arg + argc - 1;
1687                 for ( ; argv[arg]; arg += nextfile) {
1688                         nextfile = 1;
1689                         files.current = arg;
1690                         if (argc > 2) {
1691                                 static int firsttime;
1692                                 firsttime++;
1693                                 if (firsttime > 1) {
1694                                         mesg(_("(Next file: "));
1695                                         mesg(argv[arg]);
1696                                         mesg(")");
1697 newfile:
1698                                         if (ontty) {
1699                                         prompt(-1);
1700                                         switch(cmd.key) {
1701                                         case 'n':
1702                                                 /*
1703                                                 * Next file.
1704                                                 */
1705                                                 if (cmd.count == 0)
1706                                                         cmd.count = 1;
1707                                                 nextfile = cmd.count;
1708                                                 if (checkf()) {
1709                                                         nextfile = 1;
1710                                                         mesg(":");
1711                                                         goto newfile;
1712                                                 }
1713                                                 continue;
1714                                         case 'p':
1715                                                 /*
1716                                                 * Previous file.
1717                                                 */
1718                                                 if (cmd.count == 0)
1719                                                         cmd.count = 1;
1720                                                 nextfile = 0 - cmd.count;
1721                                                 if (checkf()) {
1722                                                         nextfile = 1;
1723                                                         mesg(":");
1724                                                         goto newfile;
1725                                                 }
1726                                                 continue;
1727                                         case 'q':
1728                                         case 'Q':
1729                                                 quit(exitstatus);
1730                                 }
1731                                 } else mesg("\n");
1732                                 }
1733                         }
1734                         if (strcmp(argv[arg], "-") == 0)
1735                                 input = stdin;
1736                         else {
1737                                 input = fopen(argv[arg], "r");
1738                                 if (input == NULL) {
1739                                         pgerror(errno, argv[arg]);
1740                                         exitstatus++;
1741                                         continue;
1742                                 }
1743                         }
1744                         if (ontty == 0 && argc > 2) {
1745                                 /*
1746                                  * Use the prefix as specified by SUSv2.
1747                                  */
1748                                 write_all(1, "::::::::::::::\n", 15);
1749                                 write_all(1, argv[arg], strlen(argv[arg]));
1750                                 write_all(1, "\n::::::::::::::\n", 16);
1751                         }
1752                         pgfile(input, argv[arg]);
1753                         if (input != stdin)
1754                                 fclose(input);
1755                 }
1756         }
1757         quit(exitstatus);
1758         /*NOTREACHED*/
1759         return 0;
1760 }