home *** CD-ROM | disk | FTP | other *** search
/ Chip 1995 March / CHIP3.mdf / slackwar / a / util / util-lin.10 / util-lin / util-linux-1.10 / more.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-28  |  35.4 KB  |  1,826 lines

  1. /*
  2.  * Copyright (C) 1980 Regents Of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that the above copyright notice and this paragraph are
  7.  * duplicated in all such forms and that any documentation,
  8.  * advertising materials, and other materials related to such
  9.  * distribution and use acknowledge that the software was developed
  10.  * by the University of California, Berkeley.  The name of the
  11.  * University may not be used to endorse or promote products derived
  12.  * from this software without specific prior written permission.
  13.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  14.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  15.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  16.  */
  17.  
  18. #ifndef lint
  19. char copyright[] =
  20. "@(#) Copyright (c) 1980 Regents of the University of California.\n\
  21.  All rights reserved.\n";
  22. #endif /* not lint */
  23.  
  24. #ifndef lint
  25. static char sccsid[] = "@(#)more.c    5.19 (Berkeley) 6/29/88";
  26. #endif /* not lint */
  27.  
  28. /*
  29. ** more.c - General purpose tty output filter and file perusal program
  30. **
  31. **    by Eric Shienbrood, UC Berkeley
  32. **
  33. **    modified by Geoff Peck, UCB to add underlining, single spacing
  34. **    modified by John Foderaro, UCB to add -c and MORE environment variable
  35. */
  36.  
  37. #include <stdio.h>
  38. #include <unistd.h>
  39. #include <sys/param.h>
  40. #include <ctype.h>
  41. #include <signal.h>
  42. #include <errno.h>
  43. #include <termios.h>
  44. #include <setjmp.h>
  45. #include <sys/stat.h>
  46. #include <sys/file.h>
  47. #include <a.out.h>
  48. #include <varargs.h>
  49. #include <termcap.h>
  50.  
  51. #define HELPFILE    "/usr/lib/more.help"
  52. #define VI        "/usr/bin/vi"
  53.  
  54. #define Fopen(s,m)    (Currline = 0,file_pos=0,fopen(s,m))
  55. #define Ftell(f)    file_pos
  56. #define Fseek(f,off)    (file_pos=off,fseek(f,off,0))
  57. #define Getc(f)        (++file_pos, getc(f))
  58. #define Ungetc(c,f)    (--file_pos, ungetc(c,f))
  59.  
  60. #define stty(fd,argp)    ioctl(fd,TCSETAF,argp)
  61.  
  62. /* some function declarations */
  63. void initterm();
  64. void argscan(char *s);
  65. void copy_file(register FILE *f);
  66. void doclear();
  67. void home();
  68. void search(char buf[], FILE *file, register int n);
  69. void skiplns(register int n, register FILE *f);
  70. void screen (register FILE *f, register int num_lines);
  71. int  command (char *filename, register FILE *f);
  72. void erase (register int col);
  73. void cleareol();
  74. void clreos();
  75. int  pr(char *s1);
  76. void reset_tty();
  77. int  getline(register FILE *f, int *length);
  78. void prbuf (register char *s, register int n);
  79.  
  80. #define TBUFSIZ    1024
  81. #define LINSIZ    256
  82. #define ctrl(letter)    (letter & 077)
  83. #define RUBOUT    '\177'
  84. #define ESC    '\033'
  85. #define QUIT    '\034'
  86.  
  87. struct termio    otty, savetty;
  88. long        file_pos, file_size;
  89. int        fnum, no_intty, no_tty, slow_tty;
  90. int        dum_opt, dlines;
  91. void        onquit(), onsusp(), chgwinsz(), end_it();
  92. int        nscroll = 11;    /* Number of lines scrolled by 'd' */
  93. int        fold_opt = 1;    /* Fold long lines */
  94. int        stop_opt = 1;    /* Stop after form feeds */
  95. int        ssp_opt = 0;    /* Suppress white space */
  96. int        ul_opt = 1;    /* Underline as best we can */
  97. int        promptlen;
  98. int        Currline;    /* Line we are currently at */
  99. int        startup = 1;
  100. int        firstf = 1;
  101. int        notell = 1;
  102. int        docrterase = 0;
  103. int        docrtkill = 0;
  104. int        bad_so;    /* True if overwriting does not turn off standout */
  105. int        inwait, Pause, errors;
  106. int        within;    /* true if we are within a file,
  107.             false if we are between files */
  108. int        hard, dumb, noscroll, hardtabs, clreol, eatnl;
  109. int        catch_susp;    /* We should catch the SIGTSTP signal */
  110. char        **fnames;    /* The list of file names */
  111. int        nfiles;        /* Number of files left to process */
  112. char        *shell;        /* The name of the shell to use */
  113. int        shellp;        /* A previous shell command exists */
  114. char        ch;
  115. jmp_buf        restore;
  116. char        Line[LINSIZ];    /* Line buffer */
  117. int        Lpp = 24;    /* lines per page */
  118. char        *Clear;        /* clear screen */
  119. char        *eraseln;    /* erase line */
  120. char        *Senter, *Sexit;/* enter and exit standout mode */
  121. char        *ULenter, *ULexit;    /* enter and exit underline mode */
  122. char        *chUL;        /* underline character */
  123. char        *chBS;        /* backspace character */
  124. char        *Home;        /* go to home */
  125. char        *cursorm;    /* cursor movement */
  126. char        cursorhome[40];    /* contains cursor movement to home */
  127. char        *EodClr;    /* clear rest of screen */
  128. char        *tgetstr();
  129. int        Mcol = 80;    /* number of columns */
  130. int        Wrap = 1;    /* set if automargins */
  131. int        soglitch;    /* terminal has standout mode glitch */
  132. int        ulglitch;    /* terminal has underline mode glitch */
  133. int        pstate = 0;    /* current UL state */
  134. char        *getenv();
  135. static        magic();
  136. struct {
  137.     long chrctr, line;
  138. } context, screen_start;
  139. /* extern */ char    PC;        /* pad character */
  140.  
  141.  
  142. int main(int argc, char **argv) {
  143.     register FILE    *f;
  144.     register char    *s;
  145.     register char    *p;
  146.     register char    ch;
  147.     register int    left;
  148.     int            prnames = 0;
  149.     int            initopt = 0;
  150.     int            srchopt = 0;
  151.     int            clearit = 0;
  152.     int            initline;
  153.     char        initbuf[80];
  154.     FILE        *checkf();
  155.  
  156.     nfiles = argc;
  157.     fnames = argv;
  158.     initterm ();
  159.     nscroll = Lpp/2 - 1;
  160.     if (nscroll <= 0)
  161.     nscroll = 1;
  162.     if(s = getenv("MORE")) argscan(s);
  163.     while (--nfiles > 0) {
  164.     if ((ch = (*++fnames)[0]) == '-') {
  165.         argscan(*fnames+1);
  166.     }
  167.     else if (ch == '+') {
  168.         s = *fnames;
  169.         if (*++s == '/') {
  170.         srchopt++;
  171.         for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
  172.             *p++ = *s++;
  173.         *p = '\0';
  174.         }
  175.         else {
  176.         initopt++;
  177.         for (initline = 0; *s != '\0'; s++)
  178.             if (isdigit (*s))
  179.             initline = initline*10 + *s -'0';
  180.         --initline;
  181.         }
  182.     }
  183.     else break;
  184.     }
  185.     /* allow clreol only if Home and eraseln and EodClr strings are
  186.      *  defined, and in that case, make sure we are in noscroll mode
  187.      */
  188.     if(clreol)
  189.     {
  190.         if((Home == NULL) || (*Home == '\0') ||
  191.        (eraseln == NULL) || (*eraseln == '\0') ||
  192.            (EodClr == NULL) || (*EodClr == '\0') )
  193.           clreol = 0;
  194.     else noscroll = 1;
  195.     }
  196.     if (dlines == 0)
  197.     dlines = Lpp - (noscroll ? 1 : 2);
  198.     left = dlines;
  199.     if (nfiles > 1)
  200.     prnames++;
  201.     if (!no_intty && nfiles == 0) {
  202.     char *rindex();
  203.  
  204.     p = rindex(argv[0], '/');
  205.     fputs("usage: ",stderr);
  206.     fputs(p ? p + 1 : argv[0],stderr);
  207.     fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr);
  208.     exit(1);
  209.     }
  210.     else
  211.     f = stdin;
  212.     if (!no_tty) {
  213.     signal(SIGQUIT, onquit);
  214.     signal(SIGINT, end_it);
  215. #ifdef SIGWINCH
  216.     signal(SIGWINCH, chgwinsz);
  217. #endif
  218.     if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
  219.         signal(SIGTSTP, onsusp);
  220.         catch_susp++;
  221.     }
  222.     stty (fileno(stderr), &otty);
  223.     }
  224.     if (no_intty) {
  225.     if (no_tty)
  226.         copy_file (stdin);
  227.     else {
  228.         if ((ch = Getc (f)) == '\f')
  229.         doclear();
  230.         else {
  231.         Ungetc (ch, f);
  232.         if (noscroll && (ch != EOF)) {
  233.             if (clreol)
  234.             home ();
  235.             else
  236.             doclear ();
  237.         }
  238.         }
  239.         if (srchopt)
  240.         {
  241.         search (initbuf, stdin, 1);
  242.         if (noscroll)
  243.             left--;
  244.         }
  245.         else if (initopt)
  246.         skiplns (initline, stdin);
  247.         screen (stdin, left);
  248.     }
  249.     no_intty = 0;
  250.     prnames++;
  251.     firstf = 0;
  252.     }
  253.  
  254.     while (fnum < nfiles) {
  255.     if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
  256.         context.line = context.chrctr = 0;
  257.         Currline = 0;
  258.         if (firstf) setjmp (restore);
  259.         if (firstf) {
  260.         firstf = 0;
  261.         if (srchopt)
  262.         {
  263.             search (initbuf, f, 1);
  264.             if (noscroll)
  265.             left--;
  266.         }
  267.         else if (initopt)
  268.             skiplns (initline, f);
  269.         }
  270.         else if (fnum < nfiles && !no_tty) {
  271.         setjmp (restore);
  272.         left = command (fnames[fnum], f);
  273.         }
  274.         if (left != 0) {
  275.         if ((noscroll || clearit) && (file_size != LONG_MAX))
  276.             if (clreol)
  277.             home ();
  278.             else
  279.             doclear ();
  280.         if (prnames) {
  281.             if (bad_so)
  282.             erase (0);
  283.             if (clreol)
  284.             cleareol ();
  285.             pr("::::::::::::::");
  286.             if (promptlen > 14)
  287.             erase (14);
  288.             xprintf ("\n");
  289.             if(clreol) cleareol();
  290.             xprintf("%s\n", fnames[fnum]);
  291.             if(clreol) cleareol();
  292.             xprintf("::::::::::::::\n");
  293.             if (left > Lpp - 4)
  294.             left = Lpp - 4;
  295.         }
  296.         if (no_tty)
  297.             copy_file (f);
  298.         else {
  299.             within++;
  300.             screen(f, left);
  301.             within = 0;
  302.         }
  303.         }
  304.         setjmp (restore);
  305.         fflush(stdout);
  306.         fclose(f);
  307.         screen_start.line = screen_start.chrctr = 0L;
  308.         context.line = context.chrctr = 0L;
  309.     }
  310.     fnum++;
  311.     firstf = 0;
  312.     }
  313.     reset_tty ();
  314.     exit(0);
  315. }
  316.  
  317. void argscan(char *s) {
  318.     int seen_num = 0;
  319.  
  320.     while (*s != '\0') {
  321.         switch (*s) {
  322.           case '0': case '1': case '2':
  323.           case '3': case '4': case '5':
  324.           case '6': case '7': case '8':
  325.           case '9':
  326.             if (!seen_num) {
  327.                 dlines = 0;
  328.                 seen_num = 1;
  329.             }
  330.             dlines = dlines*10 + *s - '0';
  331.             break;
  332.           case 'd':
  333.             dum_opt = 1;
  334.             break;
  335.           case 'l':
  336.             stop_opt = 0;
  337.             break;
  338.           case 'f':
  339.             fold_opt = 0;
  340.             break;
  341.           case 'p':
  342.             noscroll++;
  343.             break;
  344.           case 'c':
  345.             clreol++;
  346.             break;
  347.           case 's':
  348.             ssp_opt = 1;
  349.             break;
  350.           case 'u':
  351.             ul_opt = 0;
  352.             break;
  353.         }
  354.         s++;
  355.     }
  356. }
  357.  
  358.  
  359. /*
  360. ** Check whether the file named by fs is an ASCII file which the user may
  361. ** access.  If it is, return the opened file. Otherwise return NULL.
  362. */
  363.  
  364. FILE *
  365. checkf (fs, clearfirst)
  366.     register char *fs;
  367.     int *clearfirst;
  368. {
  369.     struct stat stbuf;
  370.     register FILE *f;
  371.     char c;
  372.  
  373.     if (stat (fs, &stbuf) == -1) {
  374.         (void)fflush(stdout);
  375.         if (clreol)
  376.             cleareol ();
  377.         perror(fs);
  378.         return((FILE *)NULL);
  379.     }
  380.     if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
  381.         xprintf("\n*** %s: directory ***\n\n", fs);
  382.         return((FILE *)NULL);
  383.     }
  384.     if ((f = Fopen(fs, "r")) == NULL) {
  385.         (void)fflush(stdout);
  386.         perror(fs);
  387.         return((FILE *)NULL);
  388.     }
  389.     if (magic(f, fs))
  390.         return((FILE *)NULL);
  391.     c = Getc(f);
  392.     *clearfirst = c == '\f';
  393.     Ungetc (c, f);
  394.     if ((file_size = stbuf.st_size) == 0)
  395.         file_size = LONG_MAX;
  396.     return(f);
  397. }
  398.  
  399. /*
  400.  * magic --
  401.  *    check for file magic numbers.  This code would best be shared with
  402.  *    the file(1) program or, perhaps, more should not try and be so smart?
  403.  */
  404. static int
  405. magic(f, fs)
  406.     FILE *f;
  407.     char *fs;
  408. {
  409.     struct exec ex;
  410.  
  411.     if (fread(&ex, sizeof(ex), 1, f) == 1)
  412.         switch(ex.a_info) {
  413.         case OMAGIC:
  414.         case NMAGIC:
  415.         case ZMAGIC:
  416.         case 0405:
  417.         case 0411:
  418.         case 0177545:
  419.             xprintf("\n******** %s: Not a text file ********\n\n", fs);
  420.             (void)fclose(f);
  421.             return(1);
  422.         }
  423.     (void)fseek(f, 0L, L_SET);        /* rewind() not necessary */
  424.     return(0);
  425. }
  426.  
  427. /*
  428. ** A real function, for the tputs routine in termlib
  429. */
  430. void
  431. putch (ch)
  432. char ch;
  433. {
  434.     putchar (ch);
  435. }
  436.  
  437. /*
  438. ** Print out the contents of the file f, one screenful at a time.
  439. */
  440.  
  441. #define STOP -10
  442.  
  443. void screen (register FILE *f, register int num_lines)
  444. {
  445.     register int c;
  446.     register int nchars;
  447.     int length;            /* length of current line */
  448.     static int prev_len = 1;    /* length of previous line */
  449.  
  450.     for (;;) {
  451.     while (num_lines > 0 && !Pause) {
  452.         if ((nchars = getline (f, &length)) == EOF)
  453.         {
  454.         if (clreol)
  455.             clreos();
  456.         return;
  457.         }
  458.         if (ssp_opt && length == 0 && prev_len == 0)
  459.         continue;
  460.         prev_len = length;
  461.         if (bad_so || ((Senter && *Senter == ' ') && (promptlen > 0)))
  462.         erase (0);
  463.         /* must clear before drawing line since tabs on some terminals
  464.          * do not erase what they tab over.
  465.          */
  466.         if (clreol)
  467.         cleareol ();
  468.         prbuf (Line, length);
  469.         if (nchars < promptlen)
  470.         erase (nchars);    /* erase () sets promptlen to 0 */
  471.         else promptlen = 0;
  472.         /* is this needed?
  473.          * if (clreol)
  474.          *    cleareol();    * must clear again in case we wrapped *
  475.          */
  476.         if (nchars < Mcol || !fold_opt)
  477.         prbuf("\n", 1);    /* will turn off UL if necessary */
  478.         if (nchars == STOP)
  479.         break;
  480.         num_lines--;
  481.     }
  482.     if (pstate) {
  483.         tputs(ULexit, 1, putch);
  484.         pstate = 0;
  485.     }
  486.     fflush(stdout);
  487.     if ((c = Getc(f)) == EOF)
  488.     {
  489.         if (clreol)
  490.         clreos ();
  491.         return;
  492.     }
  493.  
  494.     if (Pause && clreol)
  495.         clreos ();
  496.     Ungetc (c, f);
  497.     setjmp (restore);
  498.     Pause = 0; startup = 0;
  499.     if ((num_lines = command (NULL, f)) == 0)
  500.         return;
  501.     if (hard && promptlen > 0)
  502.         erase (0);
  503.     if (noscroll && num_lines >= dlines)
  504.     {
  505.         if (clreol)
  506.         home();
  507.         else
  508.         doclear ();
  509.     }
  510.     screen_start.line = Currline;
  511.     screen_start.chrctr = Ftell (f);
  512.     }
  513. }
  514.  
  515. /*
  516. ** Come here if a quit signal is received
  517. */
  518.  
  519. void onquit()
  520. {
  521.     signal(SIGQUIT, SIG_IGN);
  522.     if (!inwait) {
  523.     putchar ('\n');
  524.     if (!startup) {
  525.         signal(SIGQUIT, onquit);
  526.         longjmp (restore, 1);
  527.     }
  528.     else
  529.         Pause++;
  530.     }
  531.     else if (!dum_opt && notell) {
  532.     write (2, "[Use q or Q to quit]", 20);
  533.     promptlen += 20;
  534.     notell = 0;
  535.     }
  536.     signal(SIGQUIT, onquit);
  537. }
  538.  
  539. /*
  540. ** Come here if a signal for a window size change is received
  541. */
  542.  
  543. #ifdef SIGWINCH
  544. void chgwinsz()
  545. {
  546.     struct winsize win;
  547.  
  548.     (void) signal(SIGWINCH, SIG_IGN);
  549.     if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
  550.     if (win.ws_row != 0) {
  551.         Lpp = win.ws_row;
  552.         nscroll = Lpp/2 - 1;
  553.         if (nscroll <= 0)
  554.         nscroll = 1;
  555.         dlines = Lpp - (noscroll ? 1 : 2);
  556.     }
  557.     if (win.ws_col != 0)
  558.         Mcol = win.ws_col;
  559.     }
  560.     (void) signal(SIGWINCH, chgwinsz);
  561. }
  562. #endif
  563.  
  564. /*
  565. ** Clean up terminal state and exit. Also come here if interrupt signal received
  566. */
  567.  
  568. void end_it ()
  569. {
  570.  
  571.     reset_tty ();
  572.     if (clreol) {
  573.     putchar ('\r');
  574.     clreos ();
  575.     fflush (stdout);
  576.     }
  577.     else if (!clreol && (promptlen > 0)) {
  578.     kill_line ();
  579.     fflush (stdout);
  580.     }
  581.     else
  582.     write (2, "\n", 1);
  583.     _exit(0);
  584. }
  585.  
  586. void copy_file(register FILE *f) {
  587.     register int c;
  588.  
  589.     while ((c = getc(f)) != EOF)
  590.     putchar(c);
  591. }
  592.  
  593. /* Simplified printf function */
  594.  
  595. int xprintf (fmt, va_alist)
  596. register char *fmt;
  597. va_dcl
  598. {
  599.     va_list ap;
  600.     register char ch;
  601.     register int ccount;
  602.  
  603.     ccount = 0;
  604.     va_start(ap);
  605.     while (*fmt) {
  606.         while ((ch = *fmt++) != '%') {
  607.             if (ch == '\0')
  608.                 return (ccount);
  609.             ccount++;
  610.             putchar (ch);
  611.         }
  612.         switch (*fmt++) {
  613.         case 'd':
  614.             ccount += printd (va_arg(ap, int));
  615.             break;
  616.         case 's':
  617.             ccount += pr (va_arg(ap, char *));
  618.             break;
  619.         case '%':
  620.             ccount++;
  621.             putchar ('%');
  622.             break;
  623.         case '0':
  624.             return (ccount);
  625.         default:
  626.             break;
  627.         }
  628.     }
  629.     va_end(ap);
  630.     return (ccount);
  631.  
  632. }
  633.  
  634. /*
  635. ** Print an integer as a string of decimal digits,
  636. ** returning the length of the print representation.
  637. */
  638.  
  639. printd (n)
  640. int n;
  641. {
  642.     int a, nchars;
  643.  
  644.     if (a = n/10)
  645.     nchars = 1 + printd(a);
  646.     else
  647.     nchars = 1;
  648.     putchar (n % 10 + '0');
  649.     return (nchars);
  650. }
  651.  
  652. /* Put the print representation of an integer into a string */
  653. static char *sptr;
  654.  
  655. scanstr (n, str)
  656. int n;
  657. char *str;
  658. {
  659.     sptr = str;
  660.     Sprintf (n);
  661.     *sptr = '\0';
  662. }
  663.  
  664. Sprintf (n)
  665. {
  666.     int a;
  667.  
  668.     if (a = n/10)
  669.     Sprintf (a);
  670.     *sptr++ = n % 10 + '0';
  671. }
  672.  
  673. static char bell = ctrl('G');
  674.  
  675. #ifdef undef
  676. strlen (s)
  677. char *s;
  678. {
  679.     register char *p;
  680.  
  681.     p = s;
  682.     while (*p++)
  683.     ;
  684.     return (p - s - 1);
  685. }
  686. #endif
  687.  
  688. /* See whether the last component of the path name "path" is equal to the
  689. ** string "string"
  690. */
  691.  
  692. tailequ (path, string)
  693. char *path;
  694. register char *string;
  695. {
  696.     register char *tail;
  697.  
  698.     tail = path + strlen(path);
  699.     while (tail >= path)
  700.         if (*(--tail) == '/')
  701.             break;
  702.     ++tail;
  703.     while (*tail++ == *string++)
  704.         if (*tail == '\0')
  705.             return(1);
  706.     return(0);
  707. }
  708.  
  709. prompt (filename)
  710. char *filename;
  711. {
  712.     if (clreol)
  713.     cleareol ();
  714.     else if (promptlen > 0)
  715.     kill_line ();
  716.     if (!hard) {
  717.     promptlen = 8;
  718.     if (Senter && Sexit) {
  719.         tputs (Senter, 1, putch);
  720.         promptlen += (2 * soglitch);
  721.     }
  722.     if (clreol)
  723.         cleareol ();
  724.     pr("--More--");
  725.     if (filename != NULL) {
  726.         promptlen += xprintf ("(Next file: %s)", filename);
  727.     }
  728.     else if (!no_intty) {
  729.         promptlen += xprintf ("(%d%%)", (int)((file_pos * 100) / file_size));
  730.     }
  731.     if (dum_opt) {
  732.         promptlen += pr("[Press space to continue, 'q' to quit.]");
  733.     }
  734.     if (Senter && Sexit)
  735.         tputs (Sexit, 1, putch);
  736.     if (clreol)
  737.         clreos ();
  738.     fflush(stdout);
  739.     }
  740.     else
  741.     write (2, &bell, 1);
  742.     inwait++;
  743. }
  744.  
  745. /*
  746. ** Get a logical line
  747. */
  748.  
  749. int getline(register FILE *f, int *length)
  750. {
  751.     register int    c;
  752.     register char    *p;
  753.     register int    column;
  754.     static int        colflg;
  755.  
  756.     p = Line;
  757.     column = 0;
  758.     c = Getc (f);
  759.     if (colflg && c == '\n') {
  760.     Currline++;
  761.     c = Getc (f);
  762.     }
  763.     while (p < &Line[LINSIZ - 1]) {
  764.     if (c == EOF) {
  765.         if (p > Line) {
  766.         *p = '\0';
  767.         *length = p - Line;
  768.         return (column);
  769.         }
  770.         *length = p - Line;
  771.         return (EOF);
  772.     }
  773.     if (c == '\n') {
  774.         Currline++;
  775.         break;
  776.     }
  777.     *p++ = c;
  778.     if (c == '\t')
  779.         if (!hardtabs || column < promptlen && !hard) {
  780.         if (hardtabs && eraseln && !dumb) {
  781.             column = 1 + (column | 7);
  782.             tputs (eraseln, 1, putch);
  783.             promptlen = 0;
  784.         }
  785.         else {
  786.             for (--p; p < &Line[LINSIZ - 1];) {
  787.             *p++ = ' ';
  788.             if ((++column & 7) == 0)
  789.                 break;
  790.             }
  791.             if (column >= promptlen) promptlen = 0;
  792.         }
  793.         }
  794.         else
  795.         column = 1 + (column | 7);
  796.     else if (c == '\b' && column > 0)
  797.         column--;
  798.     else if (c == '\r')
  799.         column = 0;
  800.     else if (c == '\f' && stop_opt) {
  801.         p[-1] = '^';
  802.         *p++ = 'L';
  803.         column += 2;
  804.         Pause++;
  805.     }
  806.     else if (c == EOF) {
  807.         *length = p - Line;
  808.         return (column);
  809.     }
  810.     else if (c >= ' ' && c != RUBOUT)
  811.         column++;
  812.     if (column >= Mcol && fold_opt) break;
  813.     c = Getc (f);
  814.     }
  815.     if (column >= Mcol && Mcol > 0) {
  816.     if (!Wrap) {
  817.         *p++ = '\n';
  818.     }
  819.     }
  820.     colflg = column == Mcol && fold_opt;
  821.     if (colflg && eatnl && Wrap) {
  822.     *p++ = '\n'; /* simulate normal wrap */
  823.     }
  824.     *length = p - Line;
  825.     *p = 0;
  826.     return (column);
  827. }
  828.  
  829. /*
  830. ** Erase the rest of the prompt, assuming we are starting at column col.
  831. */
  832.  
  833. void erase (register int col)
  834. {
  835.  
  836.     if (promptlen == 0)
  837.     return;
  838.     if (hard) {
  839.     putchar ('\n');
  840.     }
  841.     else {
  842.     if (col == 0)
  843.         putchar ('\r');
  844.     if (!dumb && eraseln)
  845.         tputs (eraseln, 1, putch);
  846.     else
  847.         for (col = promptlen - col; col > 0; col--)
  848.         putchar (' ');
  849.     }
  850.     promptlen = 0;
  851. }
  852.  
  853. /*
  854. ** Erase the current line entirely
  855. */
  856.  
  857. kill_line ()
  858. {
  859.     erase (0);
  860.     if (!eraseln || dumb) putchar ('\r');
  861. }
  862.  
  863. /*
  864.  * force clear to end of line
  865.  */
  866. void cleareol()
  867. {
  868.     tputs(eraseln, 1, putch);
  869. }
  870.  
  871. void clreos()
  872. {
  873.     tputs(EodClr, 1, putch);
  874. }
  875.  
  876. /*
  877. **  Print string and return number of characters
  878. */
  879.  
  880. int pr(char *s1)
  881. {
  882.     register char    *s;
  883.     register char    c;
  884.  
  885.     for (s = s1; c = *s++; )
  886.     putchar(c);
  887.     return (int) (s - s1 - 1);
  888. }
  889.  
  890.  
  891. /* Print a buffer of n characters */
  892.  
  893. void prbuf (register char *s, register int n)
  894. {
  895.     register char c;            /* next output character */
  896.     register int state;            /* next output char's UL state */
  897. #define wouldul(s,n)    ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
  898.  
  899.     while (--n >= 0)
  900.     if (!ul_opt)
  901.         putchar (*s++);
  902.     else {
  903.         if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
  904.         s++;
  905.         continue;
  906.         }
  907.         if (state = wouldul(s, n)) {
  908.         c = (*s == '_')? s[2] : *s ;
  909.         n -= 2;
  910.         s += 3;
  911.         } else
  912.         c = *s++;
  913.         if (state != pstate) {
  914.         if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
  915.             state = 1;
  916.         else
  917.             tputs(state ? ULenter : ULexit, 1, putch);
  918.         }
  919.         if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
  920.             putchar(c);
  921.         if (state && *chUL) {
  922.         pr(chBS);
  923.         tputs(chUL, 1, putch);
  924.         }
  925.         pstate = state;
  926.     }
  927. }
  928.  
  929. /*
  930. **  Clear the screen
  931. */
  932. void
  933. doclear()
  934. {
  935.     if (Clear && !hard) {
  936.     tputs(Clear, 1, putch);
  937.  
  938.     /* Put out carriage return so that system doesn't
  939.     ** get confused by escape sequences when expanding tabs
  940.     */
  941.     putchar ('\r');
  942.     promptlen = 0;
  943.     }
  944. }
  945.  
  946. /*
  947.  * Go to home position
  948.  */
  949. void
  950. home()
  951. {
  952.     tputs(Home,1,putch);
  953. }
  954.  
  955. static int lastcmd, lastarg, lastp;
  956. static int lastcolon;
  957. char shell_line[132];
  958.  
  959. /*
  960. ** Read a command and do it. A command consists of an optional integer
  961. ** argument followed by the command character.  Return the number of lines
  962. ** to display in the next screenful.  If there is nothing more to display
  963. ** in the current file, zero is returned.
  964. */
  965.  
  966. int command (char *filename, register FILE *f)
  967. {
  968.     register int nlines;
  969.     register int retval;
  970.     register char c;
  971.     char colonch;
  972.     FILE *helpf;
  973.     int done;
  974.     char comchar, cmdbuf[80], *p;
  975.  
  976. #define ret(val) retval=val;done++;break
  977.  
  978.     done = 0;
  979.     if (!errors)
  980.     prompt (filename);
  981.     else
  982.     errors = 0;
  983.     for (;;) {
  984.     nlines = number (&comchar);
  985.     lastp = colonch = 0;
  986.     if (comchar == '.') {    /* Repeat last command */
  987.         lastp++;
  988.         comchar = lastcmd;
  989.         nlines = lastarg;
  990.         if (lastcmd == ':')
  991.             colonch = lastcolon;
  992.     }
  993.     lastcmd = comchar;
  994.     lastarg = nlines;
  995.     if (comchar == otty.c_cc[VERASE]) {
  996.         kill_line ();
  997.         prompt (filename);
  998.         continue;
  999.     }
  1000.     switch (comchar) {
  1001.     case ':':
  1002.         retval = colon (filename, colonch, nlines);
  1003.         if (retval >= 0)
  1004.         done++;
  1005.         break;
  1006.     case 'b':
  1007.     case ctrl('B'):
  1008.         {
  1009.         register int initline;
  1010.  
  1011.         if (no_intty) {
  1012.             write(2, &bell, 1);
  1013.             return (-1);
  1014.         }
  1015.  
  1016.         if (nlines == 0) nlines++;
  1017.  
  1018.         putchar ('\r');
  1019.         erase (0);
  1020.         xprintf ("\n");
  1021.         if (clreol)
  1022.             cleareol ();
  1023.         xprintf ("...back %d page", nlines);
  1024.         if (nlines > 1)
  1025.             pr ("s\n");
  1026.         else
  1027.             pr ("\n");
  1028.  
  1029.         if (clreol)
  1030.             cleareol ();
  1031.         pr ("\n");
  1032.  
  1033.         initline = Currline - dlines * (nlines + 1);
  1034.         if (! noscroll)
  1035.             --initline;
  1036.         if (initline < 0) initline = 0;
  1037.         Fseek(f, 0L);
  1038.         Currline = 0;    /* skiplns() will make Currline correct */
  1039.         skiplns(initline, f);
  1040.         if (! noscroll) {
  1041.             ret(dlines + 1);
  1042.         }
  1043.         else {
  1044.             ret(dlines);
  1045.         }
  1046.         }
  1047.     case ' ':
  1048.     case 'z':
  1049.         if (nlines == 0) nlines = dlines;
  1050.         else if (comchar == 'z') dlines = nlines;
  1051.         ret (nlines);
  1052.     case 'd':
  1053.     case ctrl('D'):
  1054.         if (nlines != 0) nscroll = nlines;
  1055.         ret (nscroll);
  1056.     case 'q':
  1057.     case 'Q':
  1058.         end_it ();
  1059.     case 's':
  1060.     case 'f':
  1061.         if (nlines == 0) nlines++;
  1062.         if (comchar == 'f')
  1063.         nlines *= dlines;
  1064.         putchar ('\r');
  1065.         erase (0);
  1066.         xprintf ("\n");
  1067.         if (clreol)
  1068.         cleareol ();
  1069.         xprintf ("...skipping %d line", nlines);
  1070.         if (nlines > 1)
  1071.         pr ("s\n");
  1072.         else
  1073.         pr ("\n");
  1074.  
  1075.         if (clreol)
  1076.         cleareol ();
  1077.         pr ("\n");
  1078.  
  1079.         while (nlines > 0) {
  1080.         while ((c = Getc (f)) != '\n')
  1081.             if (c == EOF) {
  1082.             retval = 0;
  1083.             done++;
  1084.             goto endsw;
  1085.             }
  1086.             Currline++;
  1087.             nlines--;
  1088.         }
  1089.         ret (dlines);
  1090.     case '\n':
  1091.         if (nlines != 0)
  1092.         dlines = nlines;
  1093.         else
  1094.         nlines = 1;
  1095.         ret (nlines);
  1096.     case '\f':
  1097.         if (!no_intty) {
  1098.         doclear ();
  1099.         Fseek (f, screen_start.chrctr);
  1100.         Currline = screen_start.line;
  1101.         ret (dlines);
  1102.         }
  1103.         else {
  1104.         write (2, &bell, 1);
  1105.         break;
  1106.         }
  1107.     case '\'':
  1108.         if (!no_intty) {
  1109.         kill_line ();
  1110.         pr ("\n***Back***\n\n");
  1111.         Fseek (f, context.chrctr);
  1112.         Currline = context.line;
  1113.         ret (dlines);
  1114.         }
  1115.         else {
  1116.         write (2, &bell, 1);
  1117.         break;
  1118.         }
  1119.     case '=':
  1120.         kill_line ();
  1121.         promptlen = printd (Currline);
  1122.         fflush (stdout);
  1123.         break;
  1124.     case 'n':
  1125.         lastp++;
  1126.     case '/':
  1127.         if (nlines == 0) nlines++;
  1128.         kill_line ();
  1129.         pr ("/");
  1130.         promptlen = 1;
  1131.         fflush (stdout);
  1132.         if (lastp) {
  1133.         write (2,"\r", 1);
  1134.         search (NULL, f, nlines);    /* Use previous r.e. */
  1135.         }
  1136.         else {
  1137.         ttyin (cmdbuf, 78, '/');
  1138.         write (2, "\r", 1);
  1139.         search (cmdbuf, f, nlines);
  1140.         }
  1141.         ret (dlines-1);
  1142.     case '!':
  1143.         do_shell (filename);
  1144.         break;
  1145.     case '?':
  1146.     case 'h':
  1147.         if ((helpf = fopen (HELPFILE, "r")) == NULL)
  1148.         error ("Can't open help file");
  1149.         if (noscroll) doclear ();
  1150.         copy_file (helpf);
  1151.         fclose (helpf);
  1152.         prompt (filename);
  1153.         break;
  1154.     case 'v':    /* This case should go right before default */
  1155.         if (!no_intty) {
  1156.         kill_line ();
  1157.         cmdbuf[0] = '+';
  1158.         scanstr (Currline - dlines < 0 ? 0
  1159.                 : Currline - (dlines + 1) / 2, &cmdbuf[1]);
  1160.         pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
  1161.         execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
  1162.         break;
  1163.         }
  1164.     default:
  1165.         if (dum_opt) {
  1166.            kill_line ();
  1167.         if (Senter && Sexit) {
  1168.             tputs (Senter, 1, putch);
  1169.             promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch);
  1170.             tputs (Sexit, 1, putch);
  1171.         }
  1172.         else
  1173.             promptlen = pr ("[Press 'h' for instructions.]");
  1174.         fflush (stdout);
  1175.         }
  1176.         else
  1177.         write (2, &bell, 1);
  1178.         break;
  1179.     }
  1180.     if (done) break;
  1181.     }
  1182.     putchar ('\r');
  1183. endsw:
  1184.     inwait = 0;
  1185.     notell++;
  1186.     return (retval);
  1187. }
  1188.  
  1189. char ch;
  1190.  
  1191. /*
  1192.  * Execute a colon-prefixed command.
  1193.  * Returns <0 if not a command that should cause
  1194.  * more of the file to be printed.
  1195.  */
  1196.  
  1197. colon (filename, cmd, nlines)
  1198. char *filename;
  1199. int cmd;
  1200. int nlines;
  1201. {
  1202.     if (cmd == 0)
  1203.         ch = readch ();
  1204.     else
  1205.         ch = cmd;
  1206.     lastcolon = ch;
  1207.     switch (ch) {
  1208.     case 'f':
  1209.         kill_line ();
  1210.         if (!no_intty)
  1211.             promptlen = xprintf ("\"%s\" line %d", fnames[fnum], Currline);
  1212.         else
  1213.             promptlen = xprintf ("[Not a file] line %d", Currline);
  1214.         fflush (stdout);
  1215.         return (-1);
  1216.     case 'n':
  1217.         if (nlines == 0) {
  1218.             if (fnum >= nfiles - 1)
  1219.                 end_it ();
  1220.             nlines++;
  1221.         }
  1222.         putchar ('\r');
  1223.         erase (0);
  1224.         skipf (nlines);
  1225.         return (0);
  1226.     case 'p':
  1227.         if (no_intty) {
  1228.             write (2, &bell, 1);
  1229.             return (-1);
  1230.         }
  1231.         putchar ('\r');
  1232.         erase (0);
  1233.         if (nlines == 0)
  1234.             nlines++;
  1235.         skipf (-nlines);
  1236.         return (0);
  1237.     case '!':
  1238.         do_shell (filename);
  1239.         return (-1);
  1240.     case 'q':
  1241.     case 'Q':
  1242.         end_it ();
  1243.     default:
  1244.         write (2, &bell, 1);
  1245.         return (-1);
  1246.     }
  1247. }
  1248.  
  1249. /*
  1250. ** Read a decimal number from the terminal. Set cmd to the non-digit which
  1251. ** terminates the number.
  1252. */
  1253.  
  1254. number(cmd)
  1255. char *cmd;
  1256. {
  1257.     register int i;
  1258.  
  1259.     i = 0; ch = otty.c_cc[VKILL];
  1260.     for (;;) {
  1261.         ch = readch ();
  1262.         if (ch >= '0' && ch <= '9')
  1263.             i = i*10 + ch - '0';
  1264.         else if (ch == otty.c_cc[VKILL])
  1265.             i = 0;
  1266.         else {
  1267.             *cmd = ch;
  1268.             break;
  1269.         }
  1270.     }
  1271.     return (i);
  1272. }
  1273.  
  1274. do_shell (filename)
  1275. char *filename;
  1276. {
  1277.     char cmdbuf[80];
  1278.  
  1279.     kill_line ();
  1280.     pr ("!");
  1281.     fflush (stdout);
  1282.     promptlen = 1;
  1283.     if (lastp)
  1284.         pr (shell_line);
  1285.     else {
  1286.         ttyin (cmdbuf, 78, '!');
  1287.         if (expand (shell_line, cmdbuf)) {
  1288.             kill_line ();
  1289.             promptlen = xprintf ("!%s", shell_line);
  1290.         }
  1291.     }
  1292.     fflush (stdout);
  1293.     write (2, "\n", 1);
  1294.     promptlen = 0;
  1295.     shellp = 1;
  1296.     execute (filename, shell, shell, "-c", shell_line, 0);
  1297. }
  1298.  
  1299. /*
  1300. ** Search for nth ocurrence of regular expression contained in buf in the file
  1301. */
  1302.  
  1303. void search(char buf[], FILE *file, register int n)
  1304. {
  1305.     long startline = Ftell (file);
  1306.     register long line1 = startline;
  1307.     register long line2 = startline;
  1308.     register long line3 = startline;
  1309.     register int lncount;
  1310.     int saveln, rv, re_exec();
  1311.     char *s, *re_comp();
  1312.  
  1313.     context.line = saveln = Currline;
  1314.     context.chrctr = startline;
  1315.     lncount = 0;
  1316.     if ((s = re_comp (buf)) != 0)
  1317.     error (s);
  1318.     while (!feof (file)) {
  1319.     line3 = line2;
  1320.     line2 = line1;
  1321.     line1 = Ftell (file);
  1322.     rdline (file);
  1323.     lncount++;
  1324.     if ((rv = re_exec (Line)) == 1)
  1325.         if (--n == 0) {
  1326.             if (lncount > 3 || (lncount > 1 && no_intty))
  1327.             {
  1328.             pr ("\n");
  1329.             if (clreol)
  1330.                 cleareol ();
  1331.             pr("...skipping\n");
  1332.             }
  1333.             if (!no_intty) {
  1334.             Currline -= (lncount >= 3 ? 3 : lncount);
  1335.             Fseek (file, line3);
  1336.             if (noscroll)
  1337.                 if (clreol) {
  1338.                 home ();
  1339.                 cleareol ();
  1340.                 }
  1341.                 else
  1342.                 doclear ();
  1343.             }
  1344.             else {
  1345.             kill_line ();
  1346.             if (noscroll)
  1347.                 if (clreol) {
  1348.                     home ();
  1349.                     cleareol ();
  1350.                 }
  1351.                 else
  1352.                 doclear ();
  1353.             pr (Line);
  1354.             putchar ('\n');
  1355.             }
  1356.             break;
  1357.         }
  1358.     else if (rv == -1)
  1359.         error ("Regular expression botch");
  1360.     }
  1361.     if (feof (file)) {
  1362.     if (!no_intty) {
  1363. #ifndef __linux__
  1364.                 /* No longer in libc 4.5.8. . . */
  1365.         file->_flags &= ~STDIO_S_EOF_SEEN; /* why doesn't fseek do this ??!!??! */
  1366. #endif
  1367.         Currline = saveln;
  1368.         Fseek (file, startline);
  1369.     }
  1370.     else {
  1371.         pr ("\nPattern not found\n");
  1372.         end_it ();
  1373.     }
  1374.     error ("Pattern not found");
  1375.     }
  1376. }
  1377.  
  1378. /*VARARGS2*/
  1379. execute (filename, cmd, va_alist)
  1380. char *filename;
  1381. char *cmd;
  1382. va_dcl
  1383. {
  1384.     int id;
  1385.     int n;
  1386.     va_list argp;
  1387.  
  1388.     fflush (stdout);
  1389.     reset_tty ();
  1390.     for (n = 10; (id = fork ()) < 0 && n > 0; n--)
  1391.         sleep (5);
  1392.     if (id == 0) {
  1393.         if (!isatty(0)) {
  1394.         close(0);
  1395.         open("/dev/tty", 0);
  1396.         }
  1397.         va_start(argp);
  1398.         execv (cmd, argp);
  1399.         write (2, "exec failed\n", 12);
  1400.         exit (1);
  1401.         va_end(argp);    /* balance {}'s for some UNIX's */
  1402.     }
  1403.     if (id > 0) {
  1404.         signal (SIGINT, SIG_IGN);
  1405.         signal (SIGQUIT, SIG_IGN);
  1406.         if (catch_susp)
  1407.         signal(SIGTSTP, SIG_DFL);
  1408.         while (wait(0) > 0);
  1409.         signal (SIGINT, end_it);
  1410.         signal (SIGQUIT, onquit);
  1411.         if (catch_susp)
  1412.         signal(SIGTSTP, onsusp);
  1413.     } else
  1414.         write(2, "can't fork\n", 11);
  1415.     set_tty ();
  1416.     pr ("------------------------\n");
  1417.     prompt (filename);
  1418. }
  1419. /*
  1420. ** Skip n lines in the file f
  1421. */
  1422.  
  1423. void skiplns (register int n, register FILE *f)
  1424. {
  1425.     register char c;
  1426.  
  1427.     while (n > 0) {
  1428.     while ((c = Getc (f)) != '\n')
  1429.         if (c == EOF)
  1430.         return;
  1431.         n--;
  1432.         Currline++;
  1433.     }
  1434. }
  1435.  
  1436. /*
  1437. ** Skip nskip files in the file list (from the command line). Nskip may be
  1438. ** negative.
  1439. */
  1440.  
  1441. skipf (nskip)
  1442. register int nskip;
  1443. {
  1444.     if (nskip == 0) return;
  1445.     if (nskip > 0) {
  1446.     if (fnum + nskip > nfiles - 1)
  1447.         nskip = nfiles - fnum - 1;
  1448.     }
  1449.     else if (within)
  1450.     ++fnum;
  1451.     fnum += nskip;
  1452.     if (fnum < 0)
  1453.     fnum = 0;
  1454.     pr ("\n...Skipping ");
  1455.     pr ("\n");
  1456.     if (clreol)
  1457.     cleareol ();
  1458.     pr ("...Skipping ");
  1459.     pr (nskip > 0 ? "to file " : "back to file ");
  1460.     pr (fnames[fnum]);
  1461.     pr ("\n");
  1462.     if (clreol)
  1463.     cleareol ();
  1464.     pr ("\n");
  1465.     --fnum;
  1466. }
  1467.  
  1468. /*----------------------------- Terminal I/O -------------------------------*/
  1469.  
  1470. void initterm()
  1471. {
  1472.     char    buf[TBUFSIZ];
  1473.     static char    clearbuf[TBUFSIZ];
  1474.     char    *clearptr, *padstr;
  1475.     int        ldisc;
  1476.     int        lmode;
  1477.     char    *term;
  1478.     int        tgrp;
  1479.     struct winsize win;
  1480.  
  1481. retry:
  1482.     no_tty = ioctl(fileno(stdout), TCGETA, &otty);
  1483.     if (!no_tty) {    
  1484.     docrterase = (otty.c_cc[VERASE] != 255);
  1485.     docrtkill =  (otty.c_cc[VKILL] != 255);
  1486. #ifdef undef
  1487.     /*
  1488.      * Wait until we're in the foreground before we save the
  1489.      * the terminal modes.
  1490.      */
  1491.     if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) {
  1492.         perror("TIOCGPGRP");
  1493.         exit(1);
  1494.     }
  1495.     if (tgrp != getpgrp(0)) {
  1496.         kill(0, SIGTTOU);
  1497.         goto retry;
  1498.     }
  1499. #endif
  1500.     if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) {
  1501.         dumb++; ul_opt = 0;
  1502.     }
  1503.     else {
  1504. #ifdef TIOCGWINSZ
  1505.         if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
  1506. #endif
  1507.         Lpp = tgetnum("li");
  1508.         Mcol = tgetnum("co");
  1509. #ifdef TIOCGWINSZ
  1510.         } else {
  1511.         if ((Lpp = win.ws_row) == 0)
  1512.             Lpp = tgetnum("li");
  1513.         if ((Mcol = win.ws_col) == 0)
  1514.             Mcol = tgetnum("co");
  1515.         }
  1516. #endif
  1517.         if ((Lpp <= 0) || tgetflag("hc")) {
  1518.         hard++;    /* Hard copy terminal */
  1519.         Lpp = 24;
  1520.         }
  1521.         if (tgetflag("xn"))
  1522.         eatnl++; /* Eat newline at last column + 1; dec, concept */
  1523.         if (Mcol <= 0)
  1524.         Mcol = 80;
  1525.  
  1526.         if (tailequ (fnames[0], "page") || !hard && tgetflag("ns"))
  1527.         noscroll++;
  1528.         Wrap = tgetflag("am");
  1529.         bad_so = tgetflag ("xs");
  1530.         clearptr = clearbuf;
  1531.         eraseln = tgetstr("ce",&clearptr);
  1532.         Clear = tgetstr("cl", &clearptr);
  1533.         Senter = tgetstr("so", &clearptr);
  1534.         Sexit = tgetstr("se", &clearptr);
  1535.         if ((soglitch = tgetnum("sg")) < 0)
  1536.         soglitch = 0;
  1537.  
  1538.         /*
  1539.          *  Set up for underlining:  some terminals don't need it;
  1540.          *  others have start/stop sequences, still others have an
  1541.          *  underline char sequence which is assumed to move the
  1542.          *  cursor forward one character.  If underline sequence
  1543.          *  isn't available, settle for standout sequence.
  1544.          */
  1545.  
  1546.         if (tgetflag("ul") || tgetflag("os"))
  1547.         ul_opt = 0;
  1548.         if ((chUL = tgetstr("uc", &clearptr)) == NULL )
  1549.         chUL = "";
  1550.         if (((ULenter = tgetstr("us", &clearptr)) == NULL ||
  1551.              (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) {
  1552.             if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
  1553.             ULenter = "";
  1554.             ULexit = "";
  1555.         } else
  1556.             ulglitch = soglitch;
  1557.         } else {
  1558.         if ((ulglitch = tgetnum("ug")) < 0)
  1559.             ulglitch = 0;
  1560.         }
  1561.  
  1562.         if (padstr = tgetstr("pc", &clearptr))
  1563.         PC = *padstr;
  1564.         Home = tgetstr("ho",&clearptr);
  1565.         if (Home == 0 || *Home == '\0')
  1566.         {
  1567.         if ((cursorm = tgetstr("cm", &clearptr)) != NULL) {
  1568.             strcpy(cursorhome, tgoto(cursorm, 0, 0));
  1569.             Home = cursorhome;
  1570.            }
  1571.         }
  1572.         EodClr = tgetstr("cd", &clearptr);
  1573.         if ((chBS = tgetstr("bc", &clearptr)) == NULL)
  1574.         chBS = "\b";
  1575.  
  1576.     }
  1577.     if ((shell = getenv("SHELL")) == NULL)
  1578.         shell = "/bin/sh";
  1579.     }
  1580.     no_intty = ioctl(fileno(stdin), TCGETA, &otty);
  1581.     ioctl(fileno(stderr), TCGETA, &otty);
  1582.     savetty = otty;
  1583.     slow_tty = (otty.c_cflag & CBAUD) < B1200;
  1584.     hardtabs = (otty.c_oflag & TABDLY) != XTABS;
  1585.     if (!no_tty) 
  1586.     otty.c_lflag &= ~(ICANON|ECHO);
  1587. }
  1588.  
  1589. readch ()
  1590. {
  1591.     char ch;
  1592.     extern int errno;
  1593.  
  1594.     errno = 0;
  1595.     if (read (2, &ch, 1) <= 0)
  1596.         if (errno != EINTR)
  1597.             end_it();
  1598.         else
  1599.             ch = otty.c_cc[VKILL];
  1600.     return (ch);
  1601. }
  1602.  
  1603. static char BS = '\b';
  1604. static char *BSB = "\b \b";
  1605. static char CARAT = '^';
  1606. #define ERASEONECHAR \
  1607.     if (docrterase) \
  1608.     write (2, BSB, sizeof(BSB)); \
  1609.     else \
  1610.     write (2, &BS, sizeof(BS));
  1611.  
  1612. ttyin (buf, nmax, pchar)
  1613. char buf[];
  1614. register int nmax;
  1615. char pchar;
  1616. {
  1617.     register char *sptr;
  1618.     register char ch;
  1619.     register int slash = 0;
  1620.     int    maxlen;
  1621.     char cbuf;
  1622.  
  1623.     sptr = buf;
  1624.     maxlen = 0;
  1625.     while (sptr - buf < nmax) {
  1626.     if (promptlen > maxlen) maxlen = promptlen;
  1627.     ch = readch ();
  1628.     if (ch == '\\') {
  1629.         slash++;
  1630.     }
  1631.     else if ((ch == otty.c_cc[VERASE]) && !slash) {
  1632.         if (sptr > buf) {
  1633.         --promptlen;
  1634.         ERASEONECHAR
  1635.         --sptr;
  1636.         if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
  1637.             --promptlen;
  1638.             ERASEONECHAR
  1639.         }
  1640.         continue;
  1641.         }
  1642.         else {
  1643.         if (!eraseln) promptlen = maxlen;
  1644.         longjmp (restore, 1);
  1645.         }
  1646.     }
  1647.     else if ((ch == otty.c_cc[VKILL]) && !slash) {
  1648.         if (hard) {
  1649.         show (ch);
  1650.         putchar ('\n');
  1651.         putchar (pchar);
  1652.         }
  1653.         else {
  1654.         putchar ('\r');
  1655.         putchar (pchar);
  1656.         if (eraseln)
  1657.             erase (1);
  1658.         else if (docrtkill)
  1659.             while (promptlen-- > 1)
  1660.             write (2, BSB, sizeof(BSB));
  1661.         promptlen = 1;
  1662.         }
  1663.         sptr = buf;
  1664.         fflush (stdout);
  1665.         continue;
  1666.     }
  1667.     if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) {
  1668.         ERASEONECHAR
  1669.         --sptr;
  1670.     }
  1671.     if (ch != '\\')
  1672.         slash = 0;
  1673.     *sptr++ = ch;
  1674.     if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
  1675.         ch += ch == RUBOUT ? -0100 : 0100;
  1676.         write (2, &CARAT, 1);
  1677.         promptlen++;
  1678.     }
  1679.     cbuf = ch;
  1680.     if (ch != '\n' && ch != ESC) {
  1681.         write (2, &cbuf, 1);
  1682.         promptlen++;
  1683.     }
  1684.     else
  1685.         break;
  1686.     }
  1687.     *--sptr = '\0';
  1688.     if (!eraseln) promptlen = maxlen;
  1689.     if (sptr - buf >= nmax - 1)
  1690.     error ("Line too long");
  1691. }
  1692.  
  1693. expand (outbuf, inbuf)
  1694. char *outbuf;
  1695. char *inbuf;
  1696. {
  1697.     register char *instr;
  1698.     register char *outstr;
  1699.     register char ch;
  1700.     char temp[200];
  1701.     int changed = 0;
  1702.  
  1703.     instr = inbuf;
  1704.     outstr = temp;
  1705.     while ((ch = *instr++) != '\0')
  1706.     switch (ch) {
  1707.     case '%':
  1708.         if (!no_intty) {
  1709.         strcpy (outstr, fnames[fnum]);
  1710.         outstr += strlen (fnames[fnum]);
  1711.         changed++;
  1712.         }
  1713.         else
  1714.         *outstr++ = ch;
  1715.         break;
  1716.     case '!':
  1717.         if (!shellp)
  1718.         error ("No previous command to substitute for");
  1719.         strcpy (outstr, shell_line);
  1720.         outstr += strlen (shell_line);
  1721.         changed++;
  1722.         break;
  1723.     case '\\':
  1724.         if (*instr == '%' || *instr == '!') {
  1725.         *outstr++ = *instr++;
  1726.         break;
  1727.         }
  1728.     default:
  1729.         *outstr++ = ch;
  1730.     }
  1731.     *outstr++ = '\0';
  1732.     strcpy (outbuf, temp);
  1733.     return (changed);
  1734. }
  1735.  
  1736. show (ch)
  1737. register char ch;
  1738. {
  1739.     char cbuf;
  1740.  
  1741.     if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
  1742.     ch += ch == RUBOUT ? -0100 : 0100;
  1743.     write (2, &CARAT, 1);
  1744.     promptlen++;
  1745.     }
  1746.     cbuf = ch;
  1747.     write (2, &cbuf, 1);
  1748.     promptlen++;
  1749. }
  1750.  
  1751. error (mess)
  1752. char *mess;
  1753. {
  1754.     if (clreol)
  1755.     cleareol ();
  1756.     else
  1757.     kill_line ();
  1758.     promptlen += strlen (mess);
  1759.     if (Senter && Sexit) {
  1760.     tputs (Senter, 1, putch);
  1761.     pr(mess);
  1762.     tputs (Sexit, 1, putch);
  1763.     }
  1764.     else
  1765.     pr (mess);
  1766.     fflush(stdout);
  1767.     errors++;
  1768.     longjmp (restore, 1);
  1769. }
  1770.  
  1771.  
  1772. set_tty ()
  1773. {
  1774.     otty.c_lflag &= ~(ICANON|ECHO);
  1775.     stty(fileno(stderr), &otty);
  1776. }
  1777.  
  1778. void reset_tty ()
  1779. {
  1780.     if (no_tty)
  1781.     return;
  1782.     if (pstate) {
  1783.     tputs(ULexit, 1, putch);
  1784.     fflush(stdout);
  1785.     pstate = 0;
  1786.     }
  1787.     otty.c_lflag |= ICANON|ECHO;
  1788.     stty(fileno(stderr), &savetty);
  1789. }
  1790.  
  1791. rdline (f)
  1792. register FILE *f;
  1793. {
  1794.     register char c;
  1795.     register char *p;
  1796.  
  1797.     p = Line;
  1798.     while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
  1799.     *p++ = c;
  1800.     if (c == '\n')
  1801.     Currline++;
  1802.     *p = '\0';
  1803. }
  1804.  
  1805. /* Come here when we get a suspend signal from the terminal */
  1806.  
  1807. void onsusp ()
  1808. {
  1809.     /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
  1810.     signal(SIGTTOU, SIG_IGN);
  1811.     reset_tty ();
  1812.     fflush (stdout);
  1813.     signal(SIGTTOU, SIG_DFL);
  1814.     /* Send the TSTP signal to suspend our process group */
  1815.     signal(SIGTSTP, SIG_DFL);
  1816. /*    sigsetmask(0);*/
  1817.     kill (0, SIGTSTP);
  1818.     /* Pause for station break */
  1819.  
  1820.     /* We're back */
  1821.     signal (SIGTSTP, onsusp);
  1822.     set_tty ();
  1823.     if (inwait)
  1824.         longjmp (restore, 1);
  1825. }
  1826.