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