1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2016 by Delphix. All rights reserved.
  25  */
  26 
  27 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T     */
  28 /*        All Rights Reserved   */
  29 
  30 #include <signal.h>
  31 #include <setjmp.h>
  32 #include <sys/types.h>
  33 #include <sys/dirent.h>
  34 #include <sys/stat.h>
  35 #include <fcntl.h>
  36 #include <ctype.h>
  37 #include <stdio.h>
  38 #include <wchar.h>
  39 #include <curses.h>
  40 #include <term.h>
  41 #include <errno.h>
  42 #include <stdlib.h>
  43 #include <regexpr.h>
  44 #include <limits.h>
  45 #include <locale.h>
  46 #include <wctype.h> /* iswprint() */
  47 #include <string.h>
  48 #include <unistd.h>
  49 #include <wait.h>
  50 #include <libw.h>
  51 #include <regexpr.h>
  52 
  53 
  54 /*
  55  *      pg -- paginator for crt terminals
  56  *
  57  *      Includes the ability to display pages that have
  58  *      already passed by. Also gives the user the ability
  59  *      to search forward and backwards for regular expressions.
  60  *      This works for piped input by copying to a temporary file,
  61  *      and resolving backreferences from there.
  62  *
  63  *      Note:   The reason that there are so many commands to do
  64  *              the same types of things is to try to accommodate
  65  *              users of other paginators.
  66  */
  67 
  68 #define LINSIZ  1024
  69 #define QUIT    '\034'
  70 #define BOF     (EOF - 1)       /* Begining of File */
  71 #define STOP    (EOF - 2)
  72 #define PROMPTSIZE      256
  73 
  74 /*
  75  * Function definitions
  76  */
  77 static  void    lineset(int);
  78 static  char    *setprompt();
  79 static  int     set_state(int *, wchar_t, char *);
  80 static  void    help();
  81 static  void    copy_file(FILE *, FILE *);
  82 static  void    re_error(int);
  83 static  void    save_input(FILE *);
  84 static  void    save_pipe();
  85 static  void    newdol(FILE *);
  86 static  void    erase_line(int);
  87 static  void    kill_line();
  88 static  void    doclear();
  89 static  void    sopr(char *, int);
  90 static  void    prompt(char *);
  91 static  void    error(char *);
  92 static  void    terminit();
  93 static  void    compact();
  94 static  off_t   getaline(FILE *);
  95 static  int     mrdchar();
  96 static  off_t   find(int, off_t);
  97 static  int     search(char *, off_t);
  98 static  FILE    *checkf(char *);
  99 static  int     skipf(int);
 100 static  int     readch();
 101 static  int     ttyin();
 102 static  int     number();
 103 static  int     command(char *);
 104 static  int     screen(char *);
 105 static  int     fgetputc();
 106 static  char    *pg_strchr();
 107 
 108 
 109 struct line {                   /* how line addresses are stored */
 110         off_t   l_addr;         /* file offset */
 111         off_t   l_no;           /* line number in file */
 112 };
 113 
 114 typedef struct line     LINE;
 115 
 116 static  LINE    *zero = NULL,   /* first line */
 117                 *dot,           /* current line */
 118                 *dol,           /* last line */
 119                 *contig;        /* where contiguous (non-aged) lines start */
 120 static  long    nlall;          /* room for how many LINEs in memory */
 121 
 122 static  FILE    *in_file,       /* current input stream */
 123                 *tmp_fin,       /* pipe temporary file in */
 124                 *tmp_fou;       /* pipe temporary file out */
 125 static  char    tmp_name[] = "/tmp/pgXXXXXX";
 126 
 127 static  short   sign;           /* sign of command input */
 128 
 129 static  int     fnum,           /* which file argument we're in */
 130                 pipe_in,        /* set when stdin is a pipe */
 131                 out_is_tty;     /* set if stdout is a tty */
 132 static  pid_t   my_pgid;
 133 
 134 static  void    on_brk(),
 135                 end_it();
 136 static  short   brk_hit;        /* interrupt handling is pending flag */
 137 
 138 static  int     window = 0;     /* window size in lines */
 139 static  short   eof_pause = 1;  /* pause w/ prompt at end of files */
 140 static  short   rmode = 0;      /* deny shell escape in restricted mode */
 141 static  short   soflag = 0;     /* output all messages in standout mode */
 142 static  short   promptlen;      /* length of the current prompt */
 143 static  short   firstf = 1;     /* set before first file has been processed */
 144 static  short   inwait,         /* set while waiting for user input */
 145                 errors;         /* set if error message has been printed. */
 146                                 /* if so, need to erase it and prompt */
 147 
 148 static  char    **fnames;
 149 static  short   status = 0;     /* set > 0 if error detected */
 150 static  short   fflag = 0;      /* set if the f option is used */
 151 static  short   nflag = 0;      /* set for "no newline" input option */
 152 static  short   clropt = 0;     /* set if the clear option is used */
 153 static  int     initopt = 0;    /* set if the line option is used */
 154 static  int     srchopt = 0;    /* set if the search option is used */
 155 static  int     initline;
 156 static  char    initbuf[BUFSIZ];
 157 static  wchar_t leave_search = L't';
 158                                 /* where on the page to leave a found string */
 159 static  short   nfiles;
 160 static  char    *shell;
 161 static  char    *promptstr = ":";
 162 static  off_t   nchars;                 /* return from getaline in find() */
 163 static  jmp_buf restore;
 164 static  char    Line[LINSIZ+2];
 165 
 166 static  int     catch_susp;
 167 
 168 static  void    onsusp();
 169 
 170 struct screen_stat {
 171         off_t   first_line;
 172         off_t   last_line;
 173         short   is_eof;
 174         };
 175 
 176 static  struct screen_stat old_ss = { 0, 0, 0 };
 177 static  struct screen_stat new_ss;
 178 static  struct termio otty;     /* to save old terminal settings */
 179 
 180 static  short   termflg = 0;    /* set once terminal is initialized */
 181 static  short   eoflag;         /* set whenever at end of current file */
 182 static  short   doliseof;       /* set when last line of file is known */
 183 static  off_t   eofl_no;        /* what the last line of the file is */
 184 static  void    usage(void);
 185 static FILE     *pg_stdin;
 186 
 187 int
 188 main(int argc, char **argv)
 189 {
 190         char    *s;
 191         char    *p;
 192         int             prnames = 0;
 193         int             opt;
 194         int             i;
 195 
 196         (void) setlocale(LC_ALL, "");
 197 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 198 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 199 #endif
 200         (void) textdomain(TEXT_DOMAIN);
 201 
 202         /* check for non-standard "-#" option */
 203         for (i = 1; i < argc; i++) {
 204                 if (strcmp(argv[i], "--") == 0)
 205                         break;
 206 
 207                 if ((argv[i][0] == '-') && isdigit(argv[i][1])) {
 208                         if (strlen(&argv[i][1]) !=
 209                             strspn(&argv[i][1], "0123456789")) {
 210                                 (void) fprintf(stderr, gettext(
 211                                     "pg: Badly formed number\n"));
 212                                 usage();
 213                         }
 214 
 215                         window = (int)strtol(&argv[i][1], (char **)NULL, 10);
 216 
 217                         while (i < argc) {
 218                                 argv[i] = argv[i + 1];
 219                                 i++;
 220                         }
 221                         i--;
 222                         argc--;
 223                 }
 224         }
 225 
 226         /* check for non-standard + option */
 227         for (i = 1; i < argc; i++) {
 228                 if (strcmp(argv[i], "--") == 0)
 229                 break;
 230 
 231                 if (argv[i][0] == '+') {
 232                         if (argv[i][1] == '/') {
 233                                 srchopt++;
 234                                 initopt = 0;
 235                                 for (s = &argv[i][2], p = initbuf; *s != '\0'; )
 236                                         if (p < initbuf + sizeof (initbuf))
 237                                                 *p++ = *s++;
 238                                         else {
 239                                                 (void) fprintf(stderr, gettext(
 240                                                     "pg: pattern too long\n"));
 241                                                 return (1);
 242                                         }
 243                                 *p = '\0';
 244                         } else {
 245                                 initopt++;
 246                                 srchopt = 0;
 247                                 s = &argv[i][2];
 248                                 for (; isdigit(*s); s++)
 249                                         initline = initline*10 + *s -'0';
 250                                 if (*s != '\0')
 251                                         usage();
 252                         }
 253 
 254                         while (i < argc) {
 255                                 argv[i] = argv[i + 1];
 256                                 i++;
 257                         }
 258                         i--;
 259                         argc--;
 260                 }
 261         }
 262 
 263         while ((opt = getopt(argc, argv, "cefnrsp:")) != EOF) {
 264                 switch (opt) {
 265                 case 'c':
 266                         clropt = 1;
 267                         break;
 268 
 269                 case 'e':
 270                         eof_pause = 0;
 271                         break;
 272 
 273                 case 'f':
 274                         fflag = 1;
 275                         break;
 276 
 277                 case 'n':
 278                         nflag = 1;
 279                         break;
 280 
 281                 case 'r':
 282                         rmode = 1;      /* restricted mode */
 283                         break;
 284 
 285                 case 's':
 286                         soflag = 1;     /* standout mode */
 287                         break;
 288 
 289                 case 'p':
 290                         promptstr = setprompt(optarg);
 291                         break;
 292 
 293                 default:
 294                         usage();
 295                 }
 296         }
 297 
 298         nfiles = argc - optind;
 299         fnames = &argv[optind];
 300 
 301         (void) signal(SIGQUIT, end_it);
 302         (void) signal(SIGINT, end_it);
 303         out_is_tty = isatty(1);
 304         my_pgid = getpgrp();
 305         if (out_is_tty) {
 306                 terminit();
 307                 (void) signal(SIGQUIT, on_brk);
 308                 (void) signal(SIGINT, on_brk);
 309                 if (signal(SIGTSTP, SIG_IGN) == SIG_DFL) {
 310                         (void) signal(SIGTSTP, onsusp);
 311                         catch_susp++;
 312                 }
 313         }
 314         if (window == 0)
 315                 window = lines - 1;
 316         if (window <= 1)
 317                 window = 2;
 318         if (initline <= 0)
 319                 initline = 1;
 320         if (nfiles > 1)
 321                 prnames++;
 322 
 323         if (nfiles == 0) {
 324                 fnames[0] = "-";
 325                 nfiles++;
 326         }
 327         while (fnum < nfiles) {
 328                 if (strcmp(fnames[fnum], "") == 0)
 329                         fnames[fnum] = "-";
 330                 if ((in_file = checkf(fnames[fnum])) == NULL) {
 331                         status = 2;
 332                         fnum++;
 333                 } else {
 334                         status = 0;
 335                         if (out_is_tty)
 336                                 fnum += screen(fnames[fnum]);
 337                         else {
 338                                 if (prnames) {
 339                                         (void) fputs("::::::::::::::\n",
 340                                             stdout);
 341                                         (void) fputs(fnames[fnum], stdout);
 342                                         (void) fputs("\n::::::::::::::\n",
 343                                             stdout);
 344                                 }
 345                                 copy_file(in_file, stdout);
 346                                 fnum++;
 347                         }
 348                         (void) fflush(stdout);
 349                         if (pipe_in)
 350                                 save_pipe();
 351                         else
 352                         if (in_file != tmp_fin)
 353                                 (void) fclose(in_file);
 354                 }
 355         }
 356         end_it();
 357 
 358         /*NOTREACHED*/
 359         return (0);
 360 }
 361 
 362 static  char *
 363 setprompt(char *s)
 364 {
 365         int i = 0;
 366         int pct_d = 0;
 367         static char pstr[PROMPTSIZE];
 368 
 369         while (i < PROMPTSIZE - 2)
 370                 switch (pstr[i++] = *s++) {
 371                 case '\0':
 372                         return (pstr);
 373                 case '%':
 374                         if (*s == 'd' && !pct_d) {
 375                                 pct_d++;
 376                         } else if (*s != '%')
 377                                 pstr[i++] = '%';
 378                         if ((pstr[i++] = *s++) == '\0')
 379                                 return (pstr);
 380                         break;
 381                 default:
 382                         break;
 383                 }
 384         (void) fprintf(stderr, gettext("pg: prompt too long\n"));
 385         exit(1);
 386         /*NOTREACHED*/
 387 }
 388 
 389 
 390 /*
 391  * Print out the contents of the file f, one screenful at a time.
 392  */
 393 
 394 static int
 395 screen(char *file_name)
 396 {
 397         int cmd_ret = 0;
 398         off_t start;
 399         short hadchance = 0;
 400 
 401         old_ss.is_eof = 0;
 402         old_ss.first_line = 0;
 403         old_ss.last_line = 0;
 404         new_ss = old_ss;
 405         if (!firstf)
 406                 cmd_ret = command(file_name);
 407         else {
 408                 firstf = 0;
 409                 if (initopt) {
 410                         initopt = 0;
 411                         new_ss.first_line = initline;
 412                         new_ss.last_line = initline + (off_t)window - 1;
 413                 } else if (srchopt) {
 414                         srchopt = 0;
 415                         if (!search(initbuf, (off_t)1))
 416                                 cmd_ret = command(file_name);
 417                 } else {
 418                         new_ss.first_line = 1;
 419                         new_ss.last_line = (off_t)window;
 420                 }
 421         }
 422 
 423         for (;;) {
 424                 if (cmd_ret)
 425                         return (cmd_ret);
 426                 if (hadchance && new_ss.last_line >= eofl_no)
 427                         return (1);
 428                 hadchance = 0;
 429 
 430                 if (new_ss.last_line < (off_t)window)
 431                         new_ss.last_line = (off_t)window;
 432                 if (find(0, new_ss.last_line + 1) != EOF)
 433                         new_ss.is_eof = 0;
 434                 else {
 435                         new_ss.is_eof = 1;
 436                         new_ss.last_line = eofl_no - 1;
 437                         new_ss.first_line = new_ss.last_line -
 438                             (off_t)window + 1;
 439                 }
 440 
 441                 if (new_ss.first_line < 1)
 442                         new_ss.first_line = 1;
 443                 if (clropt) {
 444                         doclear();
 445                         start = new_ss.first_line;
 446                 } else {
 447                         if (new_ss.first_line == old_ss.last_line)
 448                                 start = new_ss.first_line + 1;
 449                         else
 450                         if (new_ss.first_line > old_ss.last_line)
 451                                 start = new_ss.first_line;
 452                         else
 453                         if (old_ss.first_line < new_ss.first_line)
 454                                 start = old_ss.last_line + 1;
 455                         else
 456                                 start = new_ss.first_line;
 457 
 458                         if (start < old_ss.first_line)
 459                                 sopr(gettext("...skipping backward\n"), 0);
 460                         else
 461                         if (start > old_ss.last_line + 1)
 462                                 sopr(gettext("...skipping forward\n"), 0);
 463                 }
 464 
 465                 for (; start <= new_ss.last_line; start++) {
 466                         (void) find(0, start);
 467                         (void) fputs(Line, stdout);
 468                         if (brk_hit) {
 469                                 new_ss.last_line = find(1, 0);
 470                                 new_ss.is_eof = 0;
 471                                 break;
 472                         }
 473                 }
 474 
 475                 brk_hit = 0;
 476                 (void) fflush(stdout);
 477                 if (new_ss.is_eof) {
 478                         if (!eof_pause || eofl_no == 1)
 479                                 return (1);
 480                         hadchance++;
 481                         error("(EOF)");
 482                 }
 483                 old_ss = new_ss;
 484                 cmd_ret = command((char *)NULL);
 485         }
 486 }
 487 
 488 static  char    cmdbuf[LINSIZ], *cmdptr;
 489 #define BEEP()          if (bell) { (void) putp(bell); (void) fflush(stdout); }
 490 #define BLANKS(p)       while (*p == ' ' || *p == '\t') p++
 491 #define CHECKEND()      BLANKS(cmdptr); if (*cmdptr) { BEEP(); break; }
 492 
 493 /*
 494  * Read a command and do it. A command consists of an optional integer
 495  * argument followed by the command character.  Return the number of files
 496  * to skip, 0 if we're still talking about the same file.
 497  */
 498 
 499 static int
 500 command(char *filename)
 501 {
 502         off_t nlines;
 503         FILE *sf;
 504         char *cmdend;
 505         pid_t id;
 506         int skip;
 507         int     len;
 508         wchar_t wc;
 509         wchar_t wc_e;
 510         wchar_t wc_e1;
 511         char    *p;
 512 
 513         for (;;) {
 514                 /*
 515                  * Wait for output to drain before going on.
 516                  * This is done so that the user will not hit
 517                  * break and quit before they have seen the prompt.
 518                  */
 519                 (void) ioctl(1, TCSBRK, 1);
 520                 if (setjmp(restore) > 0)
 521                         end_it();
 522                 inwait = 1;
 523                 brk_hit = 0;
 524                 if (errors)
 525                         errors = 0;
 526                 else {
 527                         kill_line();
 528                         prompt(filename);
 529                 }
 530                 (void) fflush(stdout);
 531                 if (ttyin())
 532                         continue;
 533                 cmdptr = cmdbuf;
 534                 nlines = number();
 535                 BLANKS(cmdptr);
 536 
 537                 if ((len = mbtowc(&wc, cmdptr, MB_CUR_MAX)) <= 0) {
 538                         wc = *cmdptr;
 539                         len = 1;
 540                 }
 541                 cmdptr += len;
 542                 switch (wc) {
 543                 case 'h':
 544                         CHECKEND();
 545                         help();
 546                         break;
 547                 case '\014': /* ^L */
 548                 case '.':       /* redisplay current window */
 549                         CHECKEND();
 550                         new_ss.first_line = old_ss.first_line;
 551                         new_ss.last_line = old_ss.last_line;
 552                         inwait = 0;
 553                         return (0);
 554                 case 'w':       /* set window size */
 555                 case 'z':
 556                         if (sign == -1) {
 557                                 BEEP();
 558                                 break;
 559                         }
 560                         CHECKEND();
 561                         if (nlines == 0)
 562                                 nlines = (off_t)window;
 563                         else
 564                         if (nlines > 1)
 565                                 window = (int)nlines;
 566                         else {
 567                                 BEEP();
 568                                 break;
 569                         }
 570                         new_ss.first_line = old_ss.last_line;
 571                         new_ss.last_line = new_ss.first_line +
 572                             (off_t)window - 1;
 573                         inwait = 0;
 574                         return (0);
 575                 case '\004': /* ^D */
 576                 case 'd':
 577                         CHECKEND();
 578                         if (sign == 0)
 579                                 sign = 1;
 580                         new_ss.last_line = old_ss.last_line +
 581                             (off_t)sign*window/2;
 582                         new_ss.first_line = new_ss.last_line -
 583                             (off_t)window + 1;
 584                         inwait = 0;
 585                         return (0);
 586                 case 's':
 587                         /*
 588                          * save input in filename.
 589                          * Check for filename, access, etc.
 590                          */
 591                         BLANKS(cmdptr);
 592                         if (!*cmdptr) {
 593                                 BEEP();
 594                                 break;
 595                         }
 596                         if (setjmp(restore) > 0) {
 597                                 BEEP();
 598                         } else {
 599                                 char outstr[PROMPTSIZE];
 600                                 if ((sf = fopen(cmdptr, "w")) == NULL) {
 601                                         error("cannot open save file");
 602                                         break;
 603                                 }
 604                                 kill_line();
 605                                 (void) sprintf(outstr, gettext(
 606                                     "saving file %s"), cmdptr);
 607                                 sopr(outstr, 1);
 608                                 (void) fflush(stdout);
 609                                 save_input(sf);
 610                                 error("saved");
 611                         }
 612                         (void) fclose(sf);
 613                         break;
 614                 case 'q':
 615                 case 'Q':
 616                         CHECKEND();
 617                         inwait = 0;
 618                         end_it();
 619                         /*FALLTHROUGH*/
 620 
 621                 case 'f':       /* skip forward screenfuls */
 622                         CHECKEND();
 623                         if (sign == 0)
 624                                 sign++; /* skips are always relative */
 625                         if (nlines == 0)
 626                                 nlines++;
 627                         nlines = nlines * (window - 1);
 628                         if (sign == 1)
 629                                 new_ss.first_line = old_ss.last_line + nlines;
 630                         else
 631                                 new_ss.first_line = old_ss.first_line - nlines;
 632                         new_ss.last_line = new_ss.first_line +
 633                             (off_t)window - 1;
 634                         inwait = 0;
 635                         return (0);
 636                 case 'l':       /* get a line */
 637                         CHECKEND();
 638                         if (nlines == 0) {
 639                                 nlines++;
 640                                 if (sign == 0)
 641                                         sign = 1;
 642                         }
 643                         switch (sign) {
 644                         case 1:
 645                                 new_ss.last_line = old_ss.last_line + nlines;
 646                                 new_ss.first_line =
 647                                     new_ss.last_line - (off_t)window + 1;
 648                                 break;
 649                         case 0:  /* leave addressed line at top */
 650                                 new_ss.first_line = nlines;
 651                                 new_ss.last_line = nlines + (off_t)window - 1;
 652                                 break;
 653                         case -1:
 654                                 new_ss.first_line = old_ss.first_line - nlines;
 655                                 new_ss.last_line =
 656                                     new_ss.first_line + (off_t)window - 1;
 657                                 break;
 658                         }
 659                         inwait = 0;
 660                         return (0);
 661                 case '\0': /* \n or blank */
 662                         if (nlines == 0) {
 663                                 nlines++;
 664                                 if (sign == 0)
 665                                         sign = 1;
 666                         }
 667                         nlines = (nlines - 1) * (window - 1);
 668                         switch (sign) {
 669                         case 1:
 670                                 new_ss.first_line = old_ss.last_line + nlines;
 671                                 new_ss.last_line =
 672                                     new_ss.first_line + (off_t)window - 1;
 673                                 break;
 674                         case 0:
 675                                 new_ss.first_line = nlines + 1;
 676                                 new_ss.last_line = nlines + (off_t)window;
 677                                 /*
 678                                  * This if statement is to fix the obscure bug
 679                                  * where you have a file that has less lines
 680                                  * than a screen holds, and the user types '1',
 681                                  * expecting to have the 1st page (re)displayed.
 682                                  * If we didn't set the new last_line to
 683                                  * eofl_no-1, the screen() routine
 684                                  * would cause pg to exit.
 685                                  */
 686                                 if (new_ss.first_line == 1 &&
 687                                     new_ss.last_line >= eofl_no)
 688                                         new_ss.last_line = eofl_no - 1;
 689                                 break;
 690                         case -1:
 691                                 new_ss.last_line = old_ss.first_line - nlines;
 692                                 new_ss.first_line =
 693                                     new_ss.last_line - (off_t)window + 1;
 694                                 break;
 695                         }
 696                         inwait = 0;
 697                         return (0);
 698                 case 'n':       /* switch to next file in arglist */
 699                         CHECKEND();
 700                         if (sign == 0)
 701                                 sign = 1;
 702                         if (nlines == 0)
 703                                 nlines++;
 704                         if ((skip = skipf(sign *nlines)) == 0) {
 705                                 BEEP();
 706                                 break;
 707                         }
 708                         inwait = 0;
 709                         return (skip);
 710                 case 'p':       /* switch to previous file in arglist */
 711                         CHECKEND();
 712                         if (sign == 0)
 713                                 sign = 1;
 714                         if (nlines == 0)
 715                                 nlines++;
 716                         if ((skip = skipf(-sign * nlines)) == 0) {
 717                                 BEEP();
 718                                 break;
 719                         }
 720                         inwait = 0;
 721                         return (skip);
 722                 case '$':       /* go to end of file */
 723                         CHECKEND();
 724                         sign = 1;
 725                         while (find(1, (off_t)10000) != EOF)
 726                                 /* any large number will do */;
 727                         new_ss.last_line = eofl_no - 1;
 728                         new_ss.first_line = eofl_no - (off_t)window;
 729                         inwait = 0;
 730                         return (0);
 731                 case '/':       /* search forward for r.e. */
 732                 case '?':       /*   "  backwards */
 733                 case '^':       /* this ones a ? for regent100s */
 734                         if (sign < 0) {
 735                                 BEEP();
 736                                 break;
 737                         }
 738                         if (nlines == 0)
 739                                 nlines++;
 740                         cmdptr--;
 741                         cmdend = cmdptr + (strlen(cmdptr) - 1);
 742                         wc_e1 = -1;
 743                         wc_e = -1;
 744                         for (p = cmdptr; p <= cmdend; p += len) {
 745                                 wc_e1 = wc_e;
 746                                 if ((len = mbtowc(&wc_e, p, MB_CUR_MAX)) <= 0) {
 747                                         wc_e = *p;
 748                                         len = 1;
 749                                 }
 750                         }
 751 
 752                         if (cmdend > cmdptr + 1) {
 753                                 if ((wc_e1 == *cmdptr) &&
 754                                     ((wc_e == L't') ||
 755                                     (wc_e == L'm') || (wc_e == L'b'))) {
 756                                         leave_search = wc_e;
 757                                         wc_e = wc_e1;
 758                                         cmdend--;
 759                                 }
 760                         }
 761                         if ((cmdptr < cmdend) && (wc_e == *cmdptr))
 762                                 *cmdend = '\0';
 763                         if (*cmdptr != '/')  /* signify back search by - */
 764                                 nlines = -nlines;
 765                         if (!search(++cmdptr, (off_t)nlines))
 766                                 break;
 767                         else {
 768                                 inwait = 0;
 769                                 return (0);
 770                         }
 771                 case '!':       /* shell escape */
 772                         if (rmode) {    /* restricted mode */
 773                                 (void) fprintf(stderr, gettext(
 774                                 "!command not allowed in restricted mode.\n"));
 775                                 break;
 776                         }
 777                         if (!hard_copy) { /* redisplay the command */
 778                                 (void) fputs(cmdbuf, stdout);
 779                                 (void) fputs("\n", stdout);
 780                         }
 781                         if ((id = fork()) < 0) {
 782                                 error("cannot fork, try again later");
 783                                 break;
 784                         }
 785                         if (id == (pid_t)0) {
 786                                 /*
 787                                  * if stdin is a pipe, need to close it so
 788                                  * that the terminal is really stdin for
 789                                  * the command
 790                                  */
 791                                 (void) fclose(stdin);
 792                                 (void) fclose(pg_stdin);
 793                                 (void) dup(fileno(stdout));
 794                                 (void) execl(shell, shell, "-c", cmdptr, 0);
 795                                 (void) perror("exec");
 796                                 exit(1);
 797                         }
 798                         (void) signal(SIGINT, SIG_IGN);
 799                         (void) signal(SIGQUIT, SIG_IGN);
 800                         if (catch_susp)
 801                                 (void) signal(SIGTSTP, SIG_DFL);
 802                         while (wait(NULL) != id) {
 803                                 if (errno == ECHILD)
 804                                         break;
 805                                 else
 806                                         errno = 0;
 807                         }
 808                         (void) fputs("!\n", stdout);
 809                         (void) fflush(stdout);
 810                         (void) signal(SIGINT, on_brk);
 811                         (void) signal(SIGQUIT, on_brk);
 812                         if (catch_susp)
 813                                 (void) signal(SIGTSTP, onsusp);
 814                         break;
 815                 default:
 816                         BEEP();
 817                         break;
 818                 }
 819         }
 820 }
 821 
 822 static int
 823 number()
 824 {
 825         int i;
 826         char *p;
 827 
 828         i = 0;
 829         sign = 0;
 830         p = cmdptr;
 831         BLANKS(p);
 832         if (*p == '+') {
 833                 p++;
 834                 sign = 1;
 835         }
 836         else
 837         if (*p == '-') {
 838                 p++;
 839                 sign = -1;
 840         }
 841         while (isdigit(*p))
 842                 i = i * 10 + *p++ - '0';
 843         cmdptr = p;
 844         return (i);
 845 }
 846 
 847 static int
 848 ttyin(void)
 849 {
 850         char *sptr, *p;
 851         wchar_t ch;
 852         int slash = 0;
 853         int state = 0;
 854         int width, length;
 855         char multic[MB_LEN_MAX];
 856         int     len;
 857 
 858         (void) fixterm();
 859         /* initialize state processing */
 860         (void) set_state(&state, ' ', (char *)0);
 861         sptr = cmdbuf;
 862         while (state != 10) {
 863                 if ((ch = readch()) < 0 || !iswascii(ch) && !iswprint(ch)) {
 864                         BEEP();
 865                         continue;
 866                 }
 867 
 868                 if ((length = wctomb(multic, ch)) < 0)
 869                         length = 0;
 870                 multic[length] = 0;
 871 
 872                 if (ch == '\n' && !slash)
 873                         break;
 874                 if (ch == erasechar() && !slash) {
 875                         if (sptr > cmdbuf) {
 876                                 char *oldp = cmdbuf;
 877                                 wchar_t wchar;
 878                                 p = cmdbuf;
 879                                 while (p  < sptr) {
 880                                         oldp = p;
 881                                         len = mbtowc(&wchar, p, MB_CUR_MAX);
 882                                         if (len <= 0) {
 883                                                 wchar = (unsigned char)*p;
 884                                                 len = 1;
 885                                         }
 886                                         p += len;
 887                                 }
 888                                 if ((width = wcwidth(wchar)) <= 0)
 889                                         /* ascii control character */
 890                                         width = 2;
 891                                 promptlen -= width;
 892                                 while (width--)
 893                                         (void) fputs("\b \b", stdout);
 894                                 sptr = oldp;
 895                         }
 896                         (void) set_state(&state, ch, sptr);
 897                         (void) fflush(stdout);
 898                         continue;
 899                 }
 900                 else
 901                 if (ch == killchar() && !slash) {
 902                         if (hard_copy)
 903                                 (void) putwchar(ch);
 904                         (void) resetterm();
 905                         return (1);
 906                 }
 907                 if (ch < ' ')
 908                         width = 2;
 909                 else
 910                         if ((width = wcwidth(ch)) <= 0)
 911                                 width = 0;
 912                 if (slash) {
 913                         slash = 0;
 914                         (void) fputs("\b \b", stdout);
 915                         sptr--;
 916                         promptlen--;
 917                 } else /* is there room to keep this character? */
 918                 if (sptr >= cmdbuf + sizeof (cmdbuf) ||
 919                     promptlen + width >= columns) {
 920                         BEEP();
 921                         continue;
 922                 }
 923                 else
 924                 if (ch == '\\')
 925                         slash++;
 926                 if (set_state(&state, ch, sptr) == 0) {
 927                         BEEP();
 928                         continue;
 929                 }
 930                 (void) strncpy(sptr, multic, (size_t)length);
 931                 sptr += length;
 932                 if (ch < ' ') {
 933                         ch += 0100;
 934                         multic[0] = '^';
 935                         multic[1] = ch;
 936                         length = 2;
 937                 }
 938                 p = multic;
 939                 while (length--)
 940                         (void) putchar(*p++);
 941                 promptlen += width;
 942                 (void) fflush(stdout);
 943         }
 944 
 945         *sptr = '\0';
 946         kill_line();
 947         (void) fflush(stdout);
 948         (void) resetterm();
 949         return (0);
 950 }
 951 
 952 static  int
 953 set_state(int *pstate, wchar_t c, char *pc)
 954 {
 955         static char *psign;
 956         static char *pnumber;
 957         static char *pcommand;
 958         static int slash;
 959 
 960         if (*pstate == 0) {
 961                 psign = (char *)NULL;
 962                 pnumber = (char *)NULL;
 963                 pcommand = (char *)NULL;
 964                 *pstate = 1;
 965                 slash = 0;
 966                 return (1);
 967         }
 968         if (c == '\\' && !slash) {
 969                 slash++;
 970                 return (1);
 971         }
 972         if (c == erasechar() && !slash)
 973                 switch (*pstate) {
 974                 case 4:
 975                         if (pc > pcommand)
 976                                 return (1);
 977                         pcommand = (char *)NULL;
 978                         /*FALLTHROUGH*/
 979 
 980                 case 3:
 981                         if (pnumber && pc > pnumber) {
 982                                 *pstate = 3;
 983                                 return (1);
 984                         }
 985                         pnumber = (char *)NULL;
 986                         /*FALLTHROUGH*/
 987 
 988                 case 2:
 989                         if (psign && pc > psign) {
 990                                 *pstate = 2;
 991                                 return (1);
 992                         }
 993                         psign = (char *)NULL;
 994                         /*FALLTHROUGH*/
 995 
 996                 case 1:
 997                         *pstate = 1;
 998                         return (1);
 999                 }
1000 
1001         slash = 0;
1002         switch (*pstate) {
1003         case 1: /* before recieving anything interesting */
1004                 if (c == '\t' || (!nflag && c == ' '))
1005                         return (1);
1006                 if (c == '+' || c == '-') {
1007                         psign = pc;
1008                         *pstate = 2;
1009                         return (1);
1010                 }
1011                 /*FALLTHROUGH*/
1012 
1013         case 2: /* recieved sign, waiting for digit */
1014                 if (iswascii(c) && isdigit(c)) {
1015                         pnumber = pc;
1016                         *pstate = 3;
1017                         return (1);
1018                 }
1019                 /*FALLTHROUGH*/
1020 
1021         case 3: /* recieved digit, waiting for the rest of the number */
1022                 if (iswascii(c) && isdigit(c))
1023                         return (1);
1024                 if (iswascii(c) && pg_strchr("h\014.wz\004dqQfl np$", c)) {
1025                         pcommand = pc;
1026                         if (nflag)
1027                                 *pstate = 10;
1028                         else
1029                                 *pstate = 4;
1030                         return (1);
1031                 }
1032                 if (iswascii(c) && pg_strchr("s/^?!", c)) {
1033                         pcommand = pc;
1034                         *pstate = 4;
1035                         return (1);
1036                 }
1037                 return (0);
1038         case 4:
1039                 return (1);
1040         }
1041         return (0);
1042 }
1043 
1044 static int
1045 readch(void)
1046 {
1047         return (fgetwc(pg_stdin));
1048 }
1049 
1050 static void
1051 help(void)
1052 {
1053         if (clropt)
1054                 doclear();
1055 
1056         (void) fputs(gettext(
1057 "-------------------------------------------------------\n"
1058 "  h                     help\n"
1059 "  q or Q                quit\n"
1060 "  <blank> or <newline>  next page\n"
1061 "  l                     next line\n"
1062 "  d or <^D>             display half a page more\n"
1063 "  . or <^L>             redisplay current page\n"
1064 "  f                     skip the next page forward\n"
1065 "  n                     next file\n"
1066 "  p                     previous file\n"
1067 "  $                     last page\n"
1068 "  w or z                set window size and display next page\n"
1069 "  s savefile            save current file in savefile\n"
1070 "  /pattern/             search forward for pattern\n"
1071 "  ?pattern? or\n"
1072 "  ^pattern^             search backward for pattern\n"
1073 "  !command              execute command\n"
1074 "\n"
1075 "Most commands can be preceeded by a number, as in:\n"
1076 "+1<newline> (next page); -1<newline> (previous page); 1<newline> (page 1).\n"
1077 "\n"
1078 "See the manual page for more detail.\n"
1079 "-------------------------------------------------------\n"),
1080             stdout);
1081 }
1082 
1083 /*
1084  * Skip nskip files in the file list (from the command line). Nskip may be
1085  * negative.
1086  */
1087 
1088 static int
1089 skipf(int nskip)
1090 {
1091         if (fnum + nskip < 0) {
1092                 nskip = -fnum;
1093                 if (nskip == 0)
1094                         error("No previous file");
1095         }
1096         else
1097         if (fnum + nskip > nfiles - 1) {
1098                 nskip = (nfiles - 1) - fnum;
1099                 if (nskip == 0)
1100                         error("No next file");
1101         }
1102         return (nskip);
1103 }
1104 
1105 /*
1106  * Check whether the file named by fs is a file which the user may
1107  * access.  If it is, return the opened file. Otherwise return NULL.
1108  */
1109 
1110 static FILE *
1111 checkf(char *fs)
1112 {
1113         struct stat stbuf;
1114         FILE *f;
1115         int fd;
1116         int f_was_opened;
1117 
1118         pipe_in = 0;
1119         if (strcmp(fs, "-") == 0) {
1120                 if (tmp_fin == NULL)
1121                         f = stdin;
1122                 else {
1123                         rewind(tmp_fin);
1124                         f = tmp_fin;
1125                 }
1126                 f_was_opened = 0;
1127         } else {
1128                 if ((f = fopen(fs, "r")) == (FILE *)NULL) {
1129                         (void) fflush(stdout);
1130                         perror(fs);
1131                         return ((FILE *)NULL);
1132                 }
1133                 f_was_opened = 1;
1134         }
1135         if (fstat(fileno(f), &stbuf) == -1) {
1136                 if (f_was_opened)
1137                         (void) fclose(f);
1138                 (void) fflush(stdout);
1139                 perror(fs);
1140                 return ((FILE *)NULL);
1141         }
1142         if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
1143                 if (f_was_opened)
1144                         (void) fclose(f);
1145                 (void) fprintf(stderr, "pg: ");
1146                 (void) fprintf(stderr, gettext("%s is a directory\n"), fs);
1147                 return ((FILE *)NULL);
1148         }
1149         if ((stbuf.st_mode & S_IFMT) == S_IFREG) {
1150                 if (f == stdin)         /* It may have been read from */
1151                         rewind(f);      /* already, and not reopened  */
1152         } else {
1153                 if (f != stdin) {
1154                         if (f_was_opened)
1155                                 (void) fclose(f);
1156                         (void) fprintf(stderr, "pg: ");
1157                         (void) fprintf(stderr, gettext(
1158                         "special files only handled as standard input\n"));
1159                         return ((FILE *)NULL);
1160                 } else {
1161                         if ((fd = mkstemp(tmp_name)) < 0) {
1162                                 (void) perror(tmp_name);
1163                                 return ((FILE *)NULL);
1164                         }
1165                         (void) close(fd);
1166                         if ((tmp_fou = fopen(tmp_name, "w")) == NULL) {
1167                                 (void) perror(tmp_name);
1168                                 return ((FILE *)NULL);
1169                         }
1170                         if ((tmp_fin = fopen(tmp_name, "r")) == NULL) {
1171                                 (void) perror(tmp_name);
1172                                 return ((FILE *)NULL);
1173                         }
1174                         pipe_in = 1;
1175                 }
1176         }
1177         lineset(BOF);
1178         return (f);
1179 }
1180 
1181 static void
1182 copy_file(FILE *f, FILE *out)
1183 {
1184         int c;
1185 
1186         while ((c = getc(f)) != EOF)
1187                 (void) putc(c, out);
1188 
1189 }
1190 
1191 static void
1192 re_error(int i)
1193 {
1194         int j;
1195         static struct messages {
1196                 char *message;
1197                 int number;
1198                 } re_errmsg[] = {
1199                 "Pattern not found",                            1,
1200                 "Range endpoint too large",                     11,
1201                 "Bad number",                                   16,
1202                 "`\\digit' out of range",                       25,
1203                 "No remembered search string",                  41,
1204                 "\\( \\) imbalance",                            42,
1205                 "Too many \\(",                                 43,
1206                 "More than two numbers given in \\{ \\}",       44,
1207                 "} expected after \\",                          45,
1208                 "First number exceeds second in \\{ \\}",       46,
1209                 "[] imbalance",                                 49,
1210                 "Regular expression overflow",                  50,
1211                 "Illegal byte sequence",                        67,
1212                 "Bad regular expression",                       0
1213                 };
1214 
1215         for (j = 0; re_errmsg[j].number != 0; j++)
1216                 if (re_errmsg[j].number == i)
1217                         break;
1218         error(re_errmsg[j].message);
1219         longjmp(restore, 1);  /* restore to search() */
1220 }
1221 
1222 /*
1223  * Search for nth ocurrence of regular expression contained in buf in the file
1224  *      negative n implies backward search
1225  *      n 'guaranteed' non-zero
1226  */
1227 static int
1228 search(char *buf, off_t n)
1229 {
1230         int direction;
1231         static char *expbuf;
1232         char *nexpbuf;
1233         int END_COND;
1234 
1235         if (setjmp(restore) <= 0) {
1236                 nexpbuf = compile(buf, (char *)0, (char *)0);
1237                 if (regerrno) {
1238                         if (regerrno != 41 || expbuf == NULL)
1239                                 re_error(regerrno);
1240                 } else {
1241                         if (expbuf)
1242                                 free(expbuf);
1243                         expbuf = nexpbuf;
1244                 }
1245 
1246                 if (n < 0) { /* search back */
1247                         direction = -1;
1248                         (void) find(0, old_ss.first_line);
1249                         END_COND = BOF;
1250                 } else {
1251                         direction = 1;
1252                         (void) find(0, old_ss.last_line);
1253                         END_COND = EOF;
1254                 }
1255 
1256                 while (find(1, direction) != END_COND) {
1257                         if (brk_hit)
1258                                 break;
1259                         if (step(Line, expbuf))
1260                                 if ((n -= direction) == 0) {
1261                                         switch (leave_search) {
1262                                         case 't':
1263                                                 new_ss.first_line =
1264                                                     find(1, (off_t)0);
1265                                                 new_ss.last_line =
1266                                                     new_ss.first_line +
1267                                                     (off_t)window
1268                                                     - 1;
1269                                                 break;
1270                                         case 'b':
1271                                                 new_ss.last_line =
1272                                                     find(1, (off_t)0);
1273                                                 new_ss.first_line =
1274                                                     new_ss.last_line -
1275                                                     (off_t)window
1276                                                     + 1;
1277                                                 break;
1278                                         case 'm':
1279                                                 new_ss.first_line =
1280                                                     find(1, (off_t)0) -
1281                                                     ((off_t)window - 1)/2;
1282                                                 new_ss.last_line =
1283                                                     new_ss.first_line +
1284                                                     (off_t)window
1285                                                     - 1;
1286                                                 break;
1287                                         }
1288                                         return (1);
1289                                 }
1290                 }
1291                 re_error(1); /* Pattern not found */
1292         }
1293         BEEP();
1294         return (0);
1295 }
1296 
1297 /*
1298  *      find -- find line in file f, subject to certain constraints.
1299  *
1300  *      This is the reason for all the funny stuff with sign and nlines.
1301  *      We need to be able to differentiate between relative and abosolute
1302  *      address specifications.
1303  *
1304  *      So...there are basically three cases that this routine
1305  *      handles. Either line is zero, which  means there is to be
1306  *      no motion (because line numbers start at one), or
1307  *      how and line specify a number, or line itself is negative,
1308  *      which is the same as having how == -1 and line == abs(line).
1309  *
1310  *      Then, figure where exactly it is that we are going (an absolute
1311  *      line number). Find out if it is within what we have read,
1312  *      if so, go there without further ado. Otherwise, do some
1313  *      magic to get there, saving all the intervening lines,
1314  *      in case the user wants to see them some time later.
1315  *
1316  *      In any case, return the line number that we end up at.
1317  *      (This is used by search() and screen()). If we go past EOF,
1318  *      return EOF.
1319  *      This EOF will go away eventually, as pg is expanded to
1320  *      handle multiple files as one huge one. Then EOF will
1321  *      mean we have run off the file list.
1322  *      If the requested line number is too far back, return BOF.
1323  */
1324 
1325 static off_t
1326 find(int how, off_t line)
1327 {
1328         /* no compacted memory yet */
1329         FILE *f = in_file;
1330         off_t where;
1331 
1332         if (how == 0)
1333                 where = line;
1334         else
1335                 if (dot == zero - 1)
1336                         where = how * line;
1337                 else
1338                         where = how * line + dot->l_no;
1339 
1340         /* now, where is either at, before, or after dol */
1341         /* most likely case is after, so do it first */
1342 
1343         eoflag = 0;
1344         if (where >= dol->l_no) {
1345                 if (doliseof) {
1346                         dot = dol;
1347                         eoflag++;
1348                         return (EOF);
1349                 }
1350                 if (pipe_in)
1351                         in_file = f = stdin;
1352                 else
1353                         (void) fseeko(f, (off_t)dol->l_addr, SEEK_SET);
1354                 dot = dol - 1;
1355                 while ((nchars = getaline(f)) != EOF) {
1356                         dot++;
1357                         newdol(f);
1358                         if (where == dot->l_no || brk_hit)
1359                                 break;
1360                 }
1361                 if (nchars != EOF)
1362                         return (dot->l_no);
1363                 else { /* EOF */
1364                         dot = dol;
1365                         eoflag++;
1366                         doliseof++;
1367                         eofl_no = dol->l_no;
1368                         return (EOF);
1369                 }
1370         } else { /* where < dol->l_no */
1371                 if (pipe_in) {
1372                         (void) fflush(tmp_fou);
1373                         in_file = f = tmp_fin;
1374                 }
1375                 if (where < zero->l_no) {
1376                         (void) fseeko(f, (off_t)zero->l_addr, SEEK_SET);
1377                         dot = zero - 1;
1378                         return (BOF);
1379                 } else {
1380                         dot = zero + where - 1;
1381                         (void) fseeko(f, (off_t)dot->l_addr, SEEK_SET);
1382                         nchars = getaline(f);
1383                         return (dot->l_no);
1384                 }
1385         }
1386 }
1387 
1388 static FILE *fileptr;
1389 static int (*rdchar)();
1390 
1391 static int
1392 mrdchar()
1393 {
1394         return (rdchar(fileptr));
1395 }
1396 
1397 /*
1398  * Get a logical line
1399  */
1400 static off_t
1401 getaline(FILE *f)
1402 {
1403         char    *p;
1404         int     column;
1405         static char multic[MB_LEN_MAX];
1406         static int savlength;
1407         wchar_t c;
1408         int length, width;
1409 
1410         if (pipe_in && f == stdin)
1411                 rdchar = fgetputc;
1412         else
1413                 rdchar = (int (*)())fgetwc;
1414 
1415         fileptr = f;
1416         /* copy overlap from previous call to getaline */
1417         if (savlength)
1418                 (void) strncpy(Line, multic, (size_t)savlength);
1419         for (column = 0, p = Line + savlength; ; ) {
1420                 if ((c = mrdchar()) <= 0) {
1421                         clearerr(f);
1422                         if (p > Line) {      /* last line doesn't have '\n', */
1423                                 *p++ = '\n';
1424                                 *p = '\0';      /* print it any way */
1425                                 return (column);
1426                         }
1427                         return (EOF);
1428                 }
1429                 length = wctomb(multic, c);
1430                 if (length < 0) {
1431                         length = -length;
1432                         c = 0;
1433                 }
1434                 if ((width = wcwidth(c)) < 0)
1435                         width = 0;
1436                 if (column + width > columns && !fflag)
1437                         break;
1438 
1439                 if (p + length > &Line[LINSIZ - 2] && c != '\n')
1440                         break;
1441                 (void) strncpy(p, multic, (size_t)length);
1442                 p += length;
1443                 column += width;
1444                 /* don't have any overlap here */
1445                 length = 0;
1446                 switch (c) {
1447                 case '\t': /* just a guess */
1448                         column = 1 + (column | 7);
1449                         break;
1450                 case '\b':
1451                         if (column > 0)
1452                                 column--;
1453                         break;
1454                 case '\r':
1455                         column = 0;
1456                         break;
1457                 }
1458                 if (c == '\n')
1459                         break;
1460                 if (column >= columns && !fflag)
1461                         break;
1462         }
1463         if (c != '\n') { /* We're stopping in the middle of the line */
1464                 if (column != columns || !auto_right_margin)
1465                         *p++ = '\n';    /* for the display */
1466                 /* save overlap for next call to getaline */
1467                 savlength = length;
1468                 if (savlength == 0) {
1469                         /*
1470                          * check if following byte is newline and get
1471                          * it if it is
1472                          */
1473                         c = fgetwc(f);
1474                         if (c == '\n') {
1475                                 /* gobble and copy (if necessary) newline */
1476                                 (void) ungetwc(c, f);
1477                                 (void) (*rdchar)(f);
1478                         } else if (c == EOF)
1479                                 clearerr(f);
1480                         else
1481                                 (void) ungetwc(c, f);
1482                 }
1483         } else
1484                 savlength = 0;
1485         *p = 0;
1486         return (column);
1487 }
1488 
1489 static void
1490 save_input(FILE *f)
1491 {
1492         if (pipe_in) {
1493                 save_pipe();
1494                 in_file = tmp_fin;
1495                 pipe_in = 0;
1496         }
1497         (void) fseeko(in_file, (off_t)0, SEEK_SET);
1498         copy_file(in_file, f);
1499 }
1500 
1501 static void
1502 save_pipe(void)
1503 {
1504         if (!doliseof)
1505                 while (fgetputc(stdin) != EOF)
1506                         if (brk_hit) {
1507                                 brk_hit = 0;
1508                                 error("Piped input only partially saved");
1509                                 break;
1510                         }
1511         (void) fclose(tmp_fou);
1512 }
1513 
1514 /*
1515  * copy anything read from a pipe to tmp_fou
1516  */
1517 static int
1518 fgetputc(FILE *f)
1519 {
1520         int c;
1521 
1522         if ((c = fgetwc(f)) != EOF)
1523                 (void) fputwc(c, tmp_fou);
1524         return (c);
1525 }
1526 
1527 /*
1528  * initialize line memory
1529  */
1530 static void
1531 lineset(int how)
1532 {
1533         if (zero == NULL) {
1534                 nlall = 128;
1535                 zero = (LINE *) malloc(nlall * sizeof (LINE));
1536         }
1537         dol = contig = zero;
1538         zero->l_no = 1;
1539         zero->l_addr = 0l;
1540         if (how == BOF) {
1541                 dot = zero - 1;
1542                 eoflag = 0;
1543                 doliseof = 0;
1544                 eofl_no = -1;
1545         } else {
1546                 dot = dol;
1547                 eoflag = 1;
1548                 doliseof = 1;
1549                 eofl_no = 1;
1550         }
1551 }
1552 
1553 /*
1554  *  add address of new 'dol'
1555  * assumes that f is currently at beginning of said line
1556  * updates dol
1557  */
1558 static void
1559 newdol(FILE *f)
1560 {
1561         int diff;
1562 
1563         if ((dol - zero) + 1 >= nlall) {
1564                 LINE *ozero = zero;
1565 
1566                 nlall += 512;
1567                 if ((zero = (LINE *)realloc((char *)zero,
1568                     (unsigned)(nlall * sizeof (LINE)))) == NULL) {
1569                         zero = ozero;
1570                         compact();
1571                 }
1572                 diff = (int)((int)zero - (int)ozero);
1573                 dot = (LINE *)((int)dot + diff);
1574                 dol = (LINE *)((int)dol + diff);
1575                 contig = (LINE *)((int)contig + diff);
1576         }
1577         dol++;
1578         if (!pipe_in)
1579                 dol->l_addr = (off_t)ftello(f);
1580         else {
1581                 (void) fflush(tmp_fou);
1582                 dol->l_addr = (off_t)ftello(tmp_fou);
1583         }
1584         dol->l_no = (dol-1)->l_no + 1;
1585 }
1586 
1587 static void
1588 compact(void)
1589 {
1590         (void) perror("realloc");
1591         end_it();
1592 
1593 }
1594 
1595 static void
1596 terminit(void)  /* set up terminal dependencies from termlib */
1597 {
1598         int err_ret;
1599         struct termio ntty;
1600 
1601         for (;;) {
1602                 pid_t my_tgid;
1603                 my_tgid = tcgetpgrp(1);
1604                 if (my_tgid == -1 || my_tgid == my_pgid)
1605                         break;
1606                 (void) kill(-my_pgid, SIGTTOU);
1607         }
1608 
1609         if ((freopen("/dev/tty", "r+", stdout)) == NULL) {
1610                 (void) perror("open");
1611                 exit(1);
1612         }
1613         (void) ioctl(fileno(stdout), TCGETA, &otty);
1614         termflg = 1;
1615 
1616         (void) setupterm(0, fileno(stdout), &err_ret);
1617         (void) ioctl(fileno(stdout), TCGETA, &ntty);
1618         ntty.c_lflag &= ~(ECHONL | ECHO | ICANON);
1619         ntty.c_cc[VMIN] = 1;
1620         ntty.c_cc[VTIME] = 1;
1621         (void) ioctl(fileno(stdout), TCSETAW, &ntty);
1622         pg_stdin = fdopen(dup(fileno(stdout)), "r");
1623         (void) saveterm();
1624         (void) resetterm();
1625         if (lines <= 0 || hard_copy) {
1626                 hard_copy = 1;
1627                 lines = 24;
1628         }
1629         if (columns <= 0)
1630                 columns = 80;
1631         if (clropt && !clear_screen)
1632                 clropt = 0;
1633         if ((shell = getenv("SHELL")) == (char *)NULL)
1634                         shell = "/usr/bin/sh";
1635 }
1636 
1637 static void
1638 error(char *mess)
1639 {
1640         kill_line();
1641         sopr(gettext(mess), 1);
1642         prompt((char *)NULL);
1643         errors++;
1644 }
1645 
1646 static void
1647 prompt(char *filename)
1648 {
1649         char outstr[PROMPTSIZE+6];
1650         int pagenum;
1651         if (filename != NULL) {
1652                 /*
1653                  * TRANSLATION_NOTE
1654                  *      %s is a filename.
1655                  */
1656                 (void) sprintf(outstr, gettext("(Next file: %s)"), filename);
1657         } else {
1658                 if ((pagenum = (int)((new_ss.last_line-2)/(window-1)+1)) >
1659                     999999) {
1660                         pagenum = 999999;
1661                 }
1662                 (void) sprintf(outstr, promptstr, pagenum);
1663         }
1664         sopr(outstr, 1);
1665         (void) fflush(stdout);
1666 }
1667 
1668 /*
1669  *  sopr puts out the message (please no \n's) surrounded by standout
1670  *  begins and ends
1671  */
1672 
1673 static void
1674 sopr(char *m, int count)
1675 {
1676         wchar_t wc;
1677         int     len, n;
1678         char    *p;
1679 
1680         if (count) {
1681                 p = m;
1682                 for (; *p; p += len) {
1683                         if ((len = mbtowc(&wc, p, MB_CUR_MAX)) <= 0) {
1684                                 len = 1;
1685                                 continue;
1686                         }
1687                         if ((n = wcwidth(wc)) > 0)
1688                                 promptlen += n;
1689                 }
1690         }
1691         if (soflag && enter_standout_mode && exit_standout_mode) {
1692                 (void) putp(enter_standout_mode);
1693                 (void) fputs(m, stdout);
1694                 (void) putp(exit_standout_mode);
1695         }
1696         else
1697                 (void) fputs(m, stdout);
1698 }
1699 
1700 static void
1701 doclear(void)
1702 {
1703         if (clear_screen)
1704                 (void) putp(clear_screen);
1705         (void) putchar('\r');  /* this resets the terminal drivers character */
1706                         /* count in case it is trying to expand tabs  */
1707 
1708 }
1709 
1710 static void
1711 kill_line(void)
1712 {
1713         erase_line(0);
1714         if (!clr_eol) (void) putchar('\r');
1715 
1716 }
1717 
1718 /* erase from after col to end of prompt */
1719 static void
1720 erase_line(int col)
1721 {
1722 
1723         if (promptlen == 0)
1724                 return;
1725         if (hard_copy)
1726                 (void) putchar('\n');
1727         else {
1728                 if (col == 0)
1729                         (void) putchar('\r');
1730                 if (clr_eol) {
1731                         (void) putp(clr_eol);
1732                         /* for the terminal driver again */
1733                         (void) putchar('\r');
1734                 }
1735                 else
1736                         for (col = promptlen - col; col > 0; col--)
1737                                 (void) putchar(' ');
1738         }
1739         promptlen = 0;
1740 }
1741 
1742 /*
1743  * Come here if a quit or interrupt signal is received
1744  */
1745 
1746 static void
1747 on_brk(int sno)
1748 {
1749         (void) signal(sno, on_brk);
1750         if (!inwait) {
1751                 BEEP();
1752                 brk_hit = 1;
1753         } else {
1754                 brk_hit = 0;
1755                 longjmp(restore, 1);
1756         }
1757 }
1758 
1759 /*
1760  * Clean up terminal state and exit.
1761  */
1762 
1763 void
1764 end_it(void)
1765 {
1766 
1767         if (out_is_tty) {
1768                 kill_line();
1769                 (void) resetterm();
1770                 if (termflg)
1771                         (void) ioctl(fileno(stdout), TCSETAW, &otty);
1772         }
1773         if (tmp_fin)
1774                 (void) fclose(tmp_fin);
1775         if (tmp_fou)
1776                 (void) fclose(tmp_fou);
1777         if (tmp_fou || tmp_fin)
1778                 (void) unlink(tmp_name);
1779         exit(status);
1780 }
1781 
1782 void
1783 onsusp(void)
1784 {
1785         int ttou_is_dfl;
1786 
1787         /* ignore SIGTTOU so following resetterm and flush works */
1788         ttou_is_dfl = (signal(SIGTTOU, SIG_IGN) == SIG_DFL);
1789         (void) resetterm();
1790         (void) fflush(stdout);
1791         if (ttou_is_dfl)
1792                 (void) signal(SIGTTOU, SIG_DFL);
1793 
1794         /* send SIGTSTP to stop this process group */
1795         (void) signal(SIGTSTP, SIG_DFL);
1796         (void) kill(-my_pgid, SIGTSTP);
1797 
1798         /* continued - reset the terminal */
1799 #ifdef __STDC__
1800         (void) signal(SIGTSTP, (void (*)(int))onsusp);
1801 #else
1802         (void) signal(SIGTSTP, (void (*))onsusp);
1803 #endif
1804         (void) resetterm();
1805         if (inwait)
1806                 longjmp(restore, -1);
1807 
1808 }
1809 
1810 static char *
1811 pg_strchr(char *str, wchar_t c)
1812 {
1813         while (*str) {
1814                 if (c == *str)
1815                         return (str);
1816                 str++;
1817         }
1818         return (0);
1819 }
1820 
1821 void
1822 usage(void)
1823 {
1824         (void) fprintf(stderr, gettext(
1825 "Usage: pg [-number] [-p string] [-cefnrs] [+line] [+/pattern/] files\n"));
1826         exit(1);
1827 }