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