home *** CD-ROM | disk | FTP | other *** search
/ Chip 1995 March / CHIP3.mdf / slackwar / a / util / util-lin.2 / util-lin / util-linux-2.2 / text-utils / more.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-22  |  35.5 KB  |  1,833 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. #ifdef __linux__
  431. int putch( int ch )
  432. #else
  433. void
  434. putch (ch)
  435. char ch;
  436. #endif
  437. {
  438.     putchar (ch);
  439. #ifdef __linux__
  440.     return 0;
  441. #endif
  442. }
  443.  
  444. /*
  445. ** Print out the contents of the file f, one screenful at a time.
  446. */
  447.  
  448. #define STOP -10
  449.  
  450. void screen (register FILE *f, register int num_lines)
  451. {
  452.     register int c;
  453.     register int nchars;
  454.     int length;            /* length of current line */
  455.     static int prev_len = 1;    /* length of previous line */
  456.  
  457.     for (;;) {
  458.     while (num_lines > 0 && !Pause) {
  459.         if ((nchars = getline (f, &length)) == EOF)
  460.         {
  461.         if (clreol)
  462.             clreos();
  463.         return;
  464.         }
  465.         if (ssp_opt && length == 0 && prev_len == 0)
  466.         continue;
  467.         prev_len = length;
  468.         if (bad_so || ((Senter && *Senter == ' ') && (promptlen > 0)))
  469.         erase (0);
  470.         /* must clear before drawing line since tabs on some terminals
  471.          * do not erase what they tab over.
  472.          */
  473.         if (clreol)
  474.         cleareol ();
  475.         prbuf (Line, length);
  476.         if (nchars < promptlen)
  477.         erase (nchars);    /* erase () sets promptlen to 0 */
  478.         else promptlen = 0;
  479.         /* is this needed?
  480.          * if (clreol)
  481.          *    cleareol();    * must clear again in case we wrapped *
  482.          */
  483.         if (nchars < Mcol || !fold_opt)
  484.         prbuf("\n", 1);    /* will turn off UL if necessary */
  485.         if (nchars == STOP)
  486.         break;
  487.         num_lines--;
  488.     }
  489.     if (pstate) {
  490.         tputs(ULexit, 1, putch);
  491.         pstate = 0;
  492.     }
  493.     fflush(stdout);
  494.     if ((c = Getc(f)) == EOF)
  495.     {
  496.         if (clreol)
  497.         clreos ();
  498.         return;
  499.     }
  500.  
  501.     if (Pause && clreol)
  502.         clreos ();
  503.     Ungetc (c, f);
  504.     setjmp (restore);
  505.     Pause = 0; startup = 0;
  506.     if ((num_lines = command (NULL, f)) == 0)
  507.         return;
  508.     if (hard && promptlen > 0)
  509.         erase (0);
  510.     if (noscroll && num_lines >= dlines)
  511.     {
  512.         if (clreol)
  513.         home();
  514.         else
  515.         doclear ();
  516.     }
  517.     screen_start.line = Currline;
  518.     screen_start.chrctr = Ftell (f);
  519.     }
  520. }
  521.  
  522. /*
  523. ** Come here if a quit signal is received
  524. */
  525.  
  526. void onquit()
  527. {
  528.     signal(SIGQUIT, SIG_IGN);
  529.     if (!inwait) {
  530.     putchar ('\n');
  531.     if (!startup) {
  532.         signal(SIGQUIT, onquit);
  533.         longjmp (restore, 1);
  534.     }
  535.     else
  536.         Pause++;
  537.     }
  538.     else if (!dum_opt && notell) {
  539.     write (2, "[Use q or Q to quit]", 20);
  540.     promptlen += 20;
  541.     notell = 0;
  542.     }
  543.     signal(SIGQUIT, onquit);
  544. }
  545.  
  546. /*
  547. ** Come here if a signal for a window size change is received
  548. */
  549.  
  550. #ifdef SIGWINCH
  551. void chgwinsz()
  552. {
  553.     struct winsize win;
  554.  
  555.     (void) signal(SIGWINCH, SIG_IGN);
  556.     if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
  557.     if (win.ws_row != 0) {
  558.         Lpp = win.ws_row;
  559.         nscroll = Lpp/2 - 1;
  560.         if (nscroll <= 0)
  561.         nscroll = 1;
  562.         dlines = Lpp - (noscroll ? 1 : 2);
  563.     }
  564.     if (win.ws_col != 0)
  565.         Mcol = win.ws_col;
  566.     }
  567.     (void) signal(SIGWINCH, chgwinsz);
  568. }
  569. #endif
  570.  
  571. /*
  572. ** Clean up terminal state and exit. Also come here if interrupt signal received
  573. */
  574.  
  575. void end_it ()
  576. {
  577.  
  578.     reset_tty ();
  579.     if (clreol) {
  580.     putchar ('\r');
  581.     clreos ();
  582.     fflush (stdout);
  583.     }
  584.     else if (!clreol && (promptlen > 0)) {
  585.     kill_line ();
  586.     fflush (stdout);
  587.     }
  588.     else
  589.     write (2, "\n", 1);
  590.     _exit(0);
  591. }
  592.  
  593. void copy_file(register FILE *f) {
  594.     register int c;
  595.  
  596.     while ((c = getc(f)) != EOF)
  597.     putchar(c);
  598. }
  599.  
  600. /* Simplified printf function */
  601.  
  602. int xprintf (fmt, va_alist)
  603. register char *fmt;
  604. va_dcl
  605. {
  606.     va_list ap;
  607.     register char ch;
  608.     register int ccount;
  609.  
  610.     ccount = 0;
  611.     va_start(ap);
  612.     while (*fmt) {
  613.         while ((ch = *fmt++) != '%') {
  614.             if (ch == '\0')
  615.                 return (ccount);
  616.             ccount++;
  617.             putchar (ch);
  618.         }
  619.         switch (*fmt++) {
  620.         case 'd':
  621.             ccount += printd (va_arg(ap, int));
  622.             break;
  623.         case 's':
  624.             ccount += pr (va_arg(ap, char *));
  625.             break;
  626.         case '%':
  627.             ccount++;
  628.             putchar ('%');
  629.             break;
  630.         case '0':
  631.             return (ccount);
  632.         default:
  633.             break;
  634.         }
  635.     }
  636.     va_end(ap);
  637.     return (ccount);
  638.  
  639. }
  640.  
  641. /*
  642. ** Print an integer as a string of decimal digits,
  643. ** returning the length of the print representation.
  644. */
  645.  
  646. printd (n)
  647. int n;
  648. {
  649.     int a, nchars;
  650.  
  651.     if (a = n/10)
  652.     nchars = 1 + printd(a);
  653.     else
  654.     nchars = 1;
  655.     putchar (n % 10 + '0');
  656.     return (nchars);
  657. }
  658.  
  659. /* Put the print representation of an integer into a string */
  660. static char *sptr;
  661.  
  662. scanstr (n, str)
  663. int n;
  664. char *str;
  665. {
  666.     sptr = str;
  667.     Sprintf (n);
  668.     *sptr = '\0';
  669. }
  670.  
  671. Sprintf (n)
  672. {
  673.     int a;
  674.  
  675.     if (a = n/10)
  676.     Sprintf (a);
  677.     *sptr++ = n % 10 + '0';
  678. }
  679.  
  680. static char bell = ctrl('G');
  681.  
  682. #ifdef undef
  683. strlen (s)
  684. char *s;
  685. {
  686.     register char *p;
  687.  
  688.     p = s;
  689.     while (*p++)
  690.     ;
  691.     return (p - s - 1);
  692. }
  693. #endif
  694.  
  695. /* See whether the last component of the path name "path" is equal to the
  696. ** string "string"
  697. */
  698.  
  699. tailequ (path, string)
  700. char *path;
  701. register char *string;
  702. {
  703.     register char *tail;
  704.  
  705.     tail = path + strlen(path);
  706.     while (tail >= path)
  707.         if (*(--tail) == '/')
  708.             break;
  709.     ++tail;
  710.     while (*tail++ == *string++)
  711.         if (*tail == '\0')
  712.             return(1);
  713.     return(0);
  714. }
  715.  
  716. prompt (filename)
  717. char *filename;
  718. {
  719.     if (clreol)
  720.     cleareol ();
  721.     else if (promptlen > 0)
  722.     kill_line ();
  723.     if (!hard) {
  724.     promptlen = 8;
  725.     if (Senter && Sexit) {
  726.         tputs (Senter, 1, putch);
  727.         promptlen += (2 * soglitch);
  728.     }
  729.     if (clreol)
  730.         cleareol ();
  731.     pr("--More--");
  732.     if (filename != NULL) {
  733.         promptlen += xprintf ("(Next file: %s)", filename);
  734.     }
  735.     else if (!no_intty) {
  736.         promptlen += xprintf ("(%d%%)", (int)((file_pos * 100) / file_size));
  737.     }
  738.     if (dum_opt) {
  739.         promptlen += pr("[Press space to continue, 'q' to quit.]");
  740.     }
  741.     if (Senter && Sexit)
  742.         tputs (Sexit, 1, putch);
  743.     if (clreol)
  744.         clreos ();
  745.     fflush(stdout);
  746.     }
  747.     else
  748.     write (2, &bell, 1);
  749.     inwait++;
  750. }
  751.  
  752. /*
  753. ** Get a logical line
  754. */
  755.  
  756. int getline(register FILE *f, int *length)
  757. {
  758.     register int    c;
  759.     register char    *p;
  760.     register int    column;
  761.     static int        colflg;
  762.  
  763.     p = Line;
  764.     column = 0;
  765.     c = Getc (f);
  766.     if (colflg && c == '\n') {
  767.     Currline++;
  768.     c = Getc (f);
  769.     }
  770.     while (p < &Line[LINSIZ - 1]) {
  771.     if (c == EOF) {
  772.         if (p > Line) {
  773.         *p = '\0';
  774.         *length = p - Line;
  775.         return (column);
  776.         }
  777.         *length = p - Line;
  778.         return (EOF);
  779.     }
  780.     if (c == '\n') {
  781.         Currline++;
  782.         break;
  783.     }
  784.     *p++ = c;
  785.     if (c == '\t')
  786.         if (!hardtabs || column < promptlen && !hard) {
  787.         if (hardtabs && eraseln && !dumb) {
  788.             column = 1 + (column | 7);
  789.             tputs (eraseln, 1, putch);
  790.             promptlen = 0;
  791.         }
  792.         else {
  793.             for (--p; p < &Line[LINSIZ - 1];) {
  794.             *p++ = ' ';
  795.             if ((++column & 7) == 0)
  796.                 break;
  797.             }
  798.             if (column >= promptlen) promptlen = 0;
  799.         }
  800.         }
  801.         else
  802.         column = 1 + (column | 7);
  803.     else if (c == '\b' && column > 0)
  804.         column--;
  805.     else if (c == '\r')
  806.         column = 0;
  807.     else if (c == '\f' && stop_opt) {
  808.         p[-1] = '^';
  809.         *p++ = 'L';
  810.         column += 2;
  811.         Pause++;
  812.     }
  813.     else if (c == EOF) {
  814.         *length = p - Line;
  815.         return (column);
  816.     }
  817.     else if (c >= ' ' && c != RUBOUT)
  818.         column++;
  819.     if (column >= Mcol && fold_opt) break;
  820.     c = Getc (f);
  821.     }
  822.     if (column >= Mcol && Mcol > 0) {
  823.     if (!Wrap) {
  824.         *p++ = '\n';
  825.     }
  826.     }
  827.     colflg = column == Mcol && fold_opt;
  828.     if (colflg && eatnl && Wrap) {
  829.     *p++ = '\n'; /* simulate normal wrap */
  830.     }
  831.     *length = p - Line;
  832.     *p = 0;
  833.     return (column);
  834. }
  835.  
  836. /*
  837. ** Erase the rest of the prompt, assuming we are starting at column col.
  838. */
  839.  
  840. void erase (register int col)
  841. {
  842.  
  843.     if (promptlen == 0)
  844.     return;
  845.     if (hard) {
  846.     putchar ('\n');
  847.     }
  848.     else {
  849.     if (col == 0)
  850.         putchar ('\r');
  851.     if (!dumb && eraseln)
  852.         tputs (eraseln, 1, putch);
  853.     else
  854.         for (col = promptlen - col; col > 0; col--)
  855.         putchar (' ');
  856.     }
  857.     promptlen = 0;
  858. }
  859.  
  860. /*
  861. ** Erase the current line entirely
  862. */
  863.  
  864. kill_line ()
  865. {
  866.     erase (0);
  867.     if (!eraseln || dumb) putchar ('\r');
  868. }
  869.  
  870. /*
  871.  * force clear to end of line
  872.  */
  873. void cleareol()
  874. {
  875.     tputs(eraseln, 1, putch);
  876. }
  877.  
  878. void clreos()
  879. {
  880.     tputs(EodClr, 1, putch);
  881. }
  882.  
  883. /*
  884. **  Print string and return number of characters
  885. */
  886.  
  887. int pr(char *s1)
  888. {
  889.     register char    *s;
  890.     register char    c;
  891.  
  892.     for (s = s1; c = *s++; )
  893.     putchar(c);
  894.     return (int) (s - s1 - 1);
  895. }
  896.  
  897.  
  898. /* Print a buffer of n characters */
  899.  
  900. void prbuf (register char *s, register int n)
  901. {
  902.     register char c;            /* next output character */
  903.     register int state;            /* next output char's UL state */
  904. #define wouldul(s,n)    ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
  905.  
  906.     while (--n >= 0)
  907.     if (!ul_opt)
  908.         putchar (*s++);
  909.     else {
  910.         if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
  911.         s++;
  912.         continue;
  913.         }
  914.         if (state = wouldul(s, n)) {
  915.         c = (*s == '_')? s[2] : *s ;
  916.         n -= 2;
  917.         s += 3;
  918.         } else
  919.         c = *s++;
  920.         if (state != pstate) {
  921.         if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
  922.             state = 1;
  923.         else
  924.             tputs(state ? ULenter : ULexit, 1, putch);
  925.         }
  926.         if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
  927.             putchar(c);
  928.         if (state && *chUL) {
  929.         pr(chBS);
  930.         tputs(chUL, 1, putch);
  931.         }
  932.         pstate = state;
  933.     }
  934. }
  935.  
  936. /*
  937. **  Clear the screen
  938. */
  939. void
  940. doclear()
  941. {
  942.     if (Clear && !hard) {
  943.     tputs(Clear, 1, putch);
  944.  
  945.     /* Put out carriage return so that system doesn't
  946.     ** get confused by escape sequences when expanding tabs
  947.     */
  948.     putchar ('\r');
  949.     promptlen = 0;
  950.     }
  951. }
  952.  
  953. /*
  954.  * Go to home position
  955.  */
  956. void
  957. home()
  958. {
  959.     tputs(Home,1,putch);
  960. }
  961.  
  962. static int lastcmd, lastarg, lastp;
  963. static int lastcolon;
  964. char shell_line[132];
  965.  
  966. /*
  967. ** Read a command and do it. A command consists of an optional integer
  968. ** argument followed by the command character.  Return the number of lines
  969. ** to display in the next screenful.  If there is nothing more to display
  970. ** in the current file, zero is returned.
  971. */
  972.  
  973. int command (char *filename, register FILE *f)
  974. {
  975.     register int nlines;
  976.     register int retval;
  977.     register char c;
  978.     char colonch;
  979.     FILE *helpf;
  980.     int done;
  981.     char comchar, cmdbuf[80], *p;
  982.  
  983. #define ret(val) retval=val;done++;break
  984.  
  985.     done = 0;
  986.     if (!errors)
  987.     prompt (filename);
  988.     else
  989.     errors = 0;
  990.     for (;;) {
  991.     nlines = number (&comchar);
  992.     lastp = colonch = 0;
  993.     if (comchar == '.') {    /* Repeat last command */
  994.         lastp++;
  995.         comchar = lastcmd;
  996.         nlines = lastarg;
  997.         if (lastcmd == ':')
  998.             colonch = lastcolon;
  999.     }
  1000.     lastcmd = comchar;
  1001.     lastarg = nlines;
  1002.     if (comchar == otty.c_cc[VERASE]) {
  1003.         kill_line ();
  1004.         prompt (filename);
  1005.         continue;
  1006.     }
  1007.     switch (comchar) {
  1008.     case ':':
  1009.         retval = colon (filename, colonch, nlines);
  1010.         if (retval >= 0)
  1011.         done++;
  1012.         break;
  1013.     case 'b':
  1014.     case ctrl('B'):
  1015.         {
  1016.         register int initline;
  1017.  
  1018.         if (no_intty) {
  1019.             write(2, &bell, 1);
  1020.             return (-1);
  1021.         }
  1022.  
  1023.         if (nlines == 0) nlines++;
  1024.  
  1025.         putchar ('\r');
  1026.         erase (0);
  1027.         xprintf ("\n");
  1028.         if (clreol)
  1029.             cleareol ();
  1030.         xprintf ("...back %d page", nlines);
  1031.         if (nlines > 1)
  1032.             pr ("s\n");
  1033.         else
  1034.             pr ("\n");
  1035.  
  1036.         if (clreol)
  1037.             cleareol ();
  1038.         pr ("\n");
  1039.  
  1040.         initline = Currline - dlines * (nlines + 1);
  1041.         if (! noscroll)
  1042.             --initline;
  1043.         if (initline < 0) initline = 0;
  1044.         Fseek(f, 0L);
  1045.         Currline = 0;    /* skiplns() will make Currline correct */
  1046.         skiplns(initline, f);
  1047.         if (! noscroll) {
  1048.             ret(dlines + 1);
  1049.         }
  1050.         else {
  1051.             ret(dlines);
  1052.         }
  1053.         }
  1054.     case ' ':
  1055.     case 'z':
  1056.         if (nlines == 0) nlines = dlines;
  1057.         else if (comchar == 'z') dlines = nlines;
  1058.         ret (nlines);
  1059.     case 'd':
  1060.     case ctrl('D'):
  1061.         if (nlines != 0) nscroll = nlines;
  1062.         ret (nscroll);
  1063.     case 'q':
  1064.     case 'Q':
  1065.         end_it ();
  1066.     case 's':
  1067.     case 'f':
  1068.         if (nlines == 0) nlines++;
  1069.         if (comchar == 'f')
  1070.         nlines *= dlines;
  1071.         putchar ('\r');
  1072.         erase (0);
  1073.         xprintf ("\n");
  1074.         if (clreol)
  1075.         cleareol ();
  1076.         xprintf ("...skipping %d line", nlines);
  1077.         if (nlines > 1)
  1078.         pr ("s\n");
  1079.         else
  1080.         pr ("\n");
  1081.  
  1082.         if (clreol)
  1083.         cleareol ();
  1084.         pr ("\n");
  1085.  
  1086.         while (nlines > 0) {
  1087.         while ((c = Getc (f)) != '\n')
  1088.             if (c == EOF) {
  1089.             retval = 0;
  1090.             done++;
  1091.             goto endsw;
  1092.             }
  1093.             Currline++;
  1094.             nlines--;
  1095.         }
  1096.         ret (dlines);
  1097.     case '\n':
  1098.         if (nlines != 0)
  1099.         dlines = nlines;
  1100.         else
  1101.         nlines = 1;
  1102.         ret (nlines);
  1103.     case '\f':
  1104.         if (!no_intty) {
  1105.         doclear ();
  1106.         Fseek (f, screen_start.chrctr);
  1107.         Currline = screen_start.line;
  1108.         ret (dlines);
  1109.         }
  1110.         else {
  1111.         write (2, &bell, 1);
  1112.         break;
  1113.         }
  1114.     case '\'':
  1115.         if (!no_intty) {
  1116.         kill_line ();
  1117.         pr ("\n***Back***\n\n");
  1118.         Fseek (f, context.chrctr);
  1119.         Currline = context.line;
  1120.         ret (dlines);
  1121.         }
  1122.         else {
  1123.         write (2, &bell, 1);
  1124.         break;
  1125.         }
  1126.     case '=':
  1127.         kill_line ();
  1128.         promptlen = printd (Currline);
  1129.         fflush (stdout);
  1130.         break;
  1131.     case 'n':
  1132.         lastp++;
  1133.     case '/':
  1134.         if (nlines == 0) nlines++;
  1135.         kill_line ();
  1136.         pr ("/");
  1137.         promptlen = 1;
  1138.         fflush (stdout);
  1139.         if (lastp) {
  1140.         write (2,"\r", 1);
  1141.         search (NULL, f, nlines);    /* Use previous r.e. */
  1142.         }
  1143.         else {
  1144.         ttyin (cmdbuf, 78, '/');
  1145.         write (2, "\r", 1);
  1146.         search (cmdbuf, f, nlines);
  1147.         }
  1148.         ret (dlines-1);
  1149.     case '!':
  1150.         do_shell (filename);
  1151.         break;
  1152.     case '?':
  1153.     case 'h':
  1154.         if ((helpf = fopen (HELPFILE, "r")) == NULL)
  1155.         error ("Can't open help file");
  1156.         if (noscroll) doclear ();
  1157.         copy_file (helpf);
  1158.         fclose (helpf);
  1159.         prompt (filename);
  1160.         break;
  1161.     case 'v':    /* This case should go right before default */
  1162.         if (!no_intty) {
  1163.         kill_line ();
  1164.         cmdbuf[0] = '+';
  1165.         scanstr (Currline - dlines < 0 ? 0
  1166.                 : Currline - (dlines + 1) / 2, &cmdbuf[1]);
  1167.         pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
  1168.         execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
  1169.         break;
  1170.         }
  1171.     default:
  1172.         if (dum_opt) {
  1173.            kill_line ();
  1174.         if (Senter && Sexit) {
  1175.             tputs (Senter, 1, putch);
  1176.             promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch);
  1177.             tputs (Sexit, 1, putch);
  1178.         }
  1179.         else
  1180.             promptlen = pr ("[Press 'h' for instructions.]");
  1181.         fflush (stdout);
  1182.         }
  1183.         else
  1184.         write (2, &bell, 1);
  1185.         break;
  1186.     }
  1187.     if (done) break;
  1188.     }
  1189.     putchar ('\r');
  1190. endsw:
  1191.     inwait = 0;
  1192.     notell++;
  1193.     return (retval);
  1194. }
  1195.  
  1196. char ch;
  1197.  
  1198. /*
  1199.  * Execute a colon-prefixed command.
  1200.  * Returns <0 if not a command that should cause
  1201.  * more of the file to be printed.
  1202.  */
  1203.  
  1204. colon (filename, cmd, nlines)
  1205. char *filename;
  1206. int cmd;
  1207. int nlines;
  1208. {
  1209.     if (cmd == 0)
  1210.         ch = readch ();
  1211.     else
  1212.         ch = cmd;
  1213.     lastcolon = ch;
  1214.     switch (ch) {
  1215.     case 'f':
  1216.         kill_line ();
  1217.         if (!no_intty)
  1218.             promptlen = xprintf ("\"%s\" line %d", fnames[fnum], Currline);
  1219.         else
  1220.             promptlen = xprintf ("[Not a file] line %d", Currline);
  1221.         fflush (stdout);
  1222.         return (-1);
  1223.     case 'n':
  1224.         if (nlines == 0) {
  1225.             if (fnum >= nfiles - 1)
  1226.                 end_it ();
  1227.             nlines++;
  1228.         }
  1229.         putchar ('\r');
  1230.         erase (0);
  1231.         skipf (nlines);
  1232.         return (0);
  1233.     case 'p':
  1234.         if (no_intty) {
  1235.             write (2, &bell, 1);
  1236.             return (-1);
  1237.         }
  1238.         putchar ('\r');
  1239.         erase (0);
  1240.         if (nlines == 0)
  1241.             nlines++;
  1242.         skipf (-nlines);
  1243.         return (0);
  1244.     case '!':
  1245.         do_shell (filename);
  1246.         return (-1);
  1247.     case 'q':
  1248.     case 'Q':
  1249.         end_it ();
  1250.     default:
  1251.         write (2, &bell, 1);
  1252.         return (-1);
  1253.     }
  1254. }
  1255.  
  1256. /*
  1257. ** Read a decimal number from the terminal. Set cmd to the non-digit which
  1258. ** terminates the number.
  1259. */
  1260.  
  1261. number(cmd)
  1262. char *cmd;
  1263. {
  1264.     register int i;
  1265.  
  1266.     i = 0; ch = otty.c_cc[VKILL];
  1267.     for (;;) {
  1268.         ch = readch ();
  1269.         if (ch >= '0' && ch <= '9')
  1270.             i = i*10 + ch - '0';
  1271.         else if (ch == otty.c_cc[VKILL])
  1272.             i = 0;
  1273.         else {
  1274.             *cmd = ch;
  1275.             break;
  1276.         }
  1277.     }
  1278.     return (i);
  1279. }
  1280.  
  1281. do_shell (filename)
  1282. char *filename;
  1283. {
  1284.     char cmdbuf[80];
  1285.  
  1286.     kill_line ();
  1287.     pr ("!");
  1288.     fflush (stdout);
  1289.     promptlen = 1;
  1290.     if (lastp)
  1291.         pr (shell_line);
  1292.     else {
  1293.         ttyin (cmdbuf, 78, '!');
  1294.         if (expand (shell_line, cmdbuf)) {
  1295.             kill_line ();
  1296.             promptlen = xprintf ("!%s", shell_line);
  1297.         }
  1298.     }
  1299.     fflush (stdout);
  1300.     write (2, "\n", 1);
  1301.     promptlen = 0;
  1302.     shellp = 1;
  1303.     execute (filename, shell, shell, "-c", shell_line, 0);
  1304. }
  1305.  
  1306. /*
  1307. ** Search for nth ocurrence of regular expression contained in buf in the file
  1308. */
  1309.  
  1310. void search(char buf[], FILE *file, register int n)
  1311. {
  1312.     long startline = Ftell (file);
  1313.     register long line1 = startline;
  1314.     register long line2 = startline;
  1315.     register long line3 = startline;
  1316.     register int lncount;
  1317.     int saveln, rv, re_exec();
  1318.     char *s, *re_comp();
  1319.  
  1320.     context.line = saveln = Currline;
  1321.     context.chrctr = startline;
  1322.     lncount = 0;
  1323.     if ((s = re_comp (buf)) != 0)
  1324.     error (s);
  1325.     while (!feof (file)) {
  1326.     line3 = line2;
  1327.     line2 = line1;
  1328.     line1 = Ftell (file);
  1329.     rdline (file);
  1330.     lncount++;
  1331.     if ((rv = re_exec (Line)) == 1)
  1332.         if (--n == 0) {
  1333.             if (lncount > 3 || (lncount > 1 && no_intty))
  1334.             {
  1335.             pr ("\n");
  1336.             if (clreol)
  1337.                 cleareol ();
  1338.             pr("...skipping\n");
  1339.             }
  1340.             if (!no_intty) {
  1341.             Currline -= (lncount >= 3 ? 3 : lncount);
  1342.             Fseek (file, line3);
  1343.             if (noscroll)
  1344.                 if (clreol) {
  1345.                 home ();
  1346.                 cleareol ();
  1347.                 }
  1348.                 else
  1349.                 doclear ();
  1350.             }
  1351.             else {
  1352.             kill_line ();
  1353.             if (noscroll)
  1354.                 if (clreol) {
  1355.                     home ();
  1356.                     cleareol ();
  1357.                 }
  1358.                 else
  1359.                 doclear ();
  1360.             pr (Line);
  1361.             putchar ('\n');
  1362.             }
  1363.             break;
  1364.         }
  1365.     else if (rv == -1)
  1366.         error ("Regular expression botch");
  1367.     }
  1368.     if (feof (file)) {
  1369.     if (!no_intty) {
  1370. #ifndef __linux__
  1371.                 /* No longer in libc 4.5.8. . . */
  1372.         file->_flags &= ~STDIO_S_EOF_SEEN; /* why doesn't fseek do this ??!!??! */
  1373. #endif
  1374.         Currline = saveln;
  1375.         Fseek (file, startline);
  1376.     }
  1377.     else {
  1378.         pr ("\nPattern not found\n");
  1379.         end_it ();
  1380.     }
  1381.     error ("Pattern not found");
  1382.     }
  1383. }
  1384.  
  1385. /*VARARGS2*/
  1386. execute (filename, cmd, va_alist)
  1387. char *filename;
  1388. char *cmd;
  1389. va_dcl
  1390. {
  1391.     int id;
  1392.     int n;
  1393.     va_list argp;
  1394.  
  1395.     fflush (stdout);
  1396.     reset_tty ();
  1397.     for (n = 10; (id = fork ()) < 0 && n > 0; n--)
  1398.         sleep (5);
  1399.     if (id == 0) {
  1400.         if (!isatty(0)) {
  1401.         close(0);
  1402.         open("/dev/tty", 0);
  1403.         }
  1404.         va_start(argp);
  1405.         execv (cmd, argp);
  1406.         write (2, "exec failed\n", 12);
  1407.         exit (1);
  1408.         va_end(argp);    /* balance {}'s for some UNIX's */
  1409.     }
  1410.     if (id > 0) {
  1411.         signal (SIGINT, SIG_IGN);
  1412.         signal (SIGQUIT, SIG_IGN);
  1413.         if (catch_susp)
  1414.         signal(SIGTSTP, SIG_DFL);
  1415.         while (wait(0) > 0);
  1416.         signal (SIGINT, end_it);
  1417.         signal (SIGQUIT, onquit);
  1418.         if (catch_susp)
  1419.         signal(SIGTSTP, onsusp);
  1420.     } else
  1421.         write(2, "can't fork\n", 11);
  1422.     set_tty ();
  1423.     pr ("------------------------\n");
  1424.     prompt (filename);
  1425. }
  1426. /*
  1427. ** Skip n lines in the file f
  1428. */
  1429.  
  1430. void skiplns (register int n, register FILE *f)
  1431. {
  1432.     register char c;
  1433.  
  1434.     while (n > 0) {
  1435.     while ((c = Getc (f)) != '\n')
  1436.         if (c == EOF)
  1437.         return;
  1438.         n--;
  1439.         Currline++;
  1440.     }
  1441. }
  1442.  
  1443. /*
  1444. ** Skip nskip files in the file list (from the command line). Nskip may be
  1445. ** negative.
  1446. */
  1447.  
  1448. skipf (nskip)
  1449. register int nskip;
  1450. {
  1451.     if (nskip == 0) return;
  1452.     if (nskip > 0) {
  1453.     if (fnum + nskip > nfiles - 1)
  1454.         nskip = nfiles - fnum - 1;
  1455.     }
  1456.     else if (within)
  1457.     ++fnum;
  1458.     fnum += nskip;
  1459.     if (fnum < 0)
  1460.     fnum = 0;
  1461.     pr ("\n...Skipping ");
  1462.     pr ("\n");
  1463.     if (clreol)
  1464.     cleareol ();
  1465.     pr ("...Skipping ");
  1466.     pr (nskip > 0 ? "to file " : "back to file ");
  1467.     pr (fnames[fnum]);
  1468.     pr ("\n");
  1469.     if (clreol)
  1470.     cleareol ();
  1471.     pr ("\n");
  1472.     --fnum;
  1473. }
  1474.  
  1475. /*----------------------------- Terminal I/O -------------------------------*/
  1476.  
  1477. void initterm()
  1478. {
  1479.     char    buf[TBUFSIZ];
  1480.     static char    clearbuf[TBUFSIZ];
  1481.     char    *clearptr, *padstr;
  1482.     int        ldisc;
  1483.     int        lmode;
  1484.     char    *term;
  1485.     int        tgrp;
  1486.     struct winsize win;
  1487.  
  1488. retry:
  1489.     no_tty = ioctl(fileno(stdout), TCGETA, &otty);
  1490.     if (!no_tty) {    
  1491.     docrterase = (otty.c_cc[VERASE] != 255);
  1492.     docrtkill =  (otty.c_cc[VKILL] != 255);
  1493. #ifdef undef
  1494.     /*
  1495.      * Wait until we're in the foreground before we save the
  1496.      * the terminal modes.
  1497.      */
  1498.     if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) {
  1499.         perror("TIOCGPGRP");
  1500.         exit(1);
  1501.     }
  1502.     if (tgrp != getpgrp(0)) {
  1503.         kill(0, SIGTTOU);
  1504.         goto retry;
  1505.     }
  1506. #endif
  1507.     if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) {
  1508.         dumb++; ul_opt = 0;
  1509.     }
  1510.     else {
  1511. #ifdef TIOCGWINSZ
  1512.         if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
  1513. #endif
  1514.         Lpp = tgetnum("li");
  1515.         Mcol = tgetnum("co");
  1516. #ifdef TIOCGWINSZ
  1517.         } else {
  1518.         if ((Lpp = win.ws_row) == 0)
  1519.             Lpp = tgetnum("li");
  1520.         if ((Mcol = win.ws_col) == 0)
  1521.             Mcol = tgetnum("co");
  1522.         }
  1523. #endif
  1524.         if ((Lpp <= 0) || tgetflag("hc")) {
  1525.         hard++;    /* Hard copy terminal */
  1526.         Lpp = 24;
  1527.         }
  1528.         if (tgetflag("xn"))
  1529.         eatnl++; /* Eat newline at last column + 1; dec, concept */
  1530.         if (Mcol <= 0)
  1531.         Mcol = 80;
  1532.  
  1533.         if (tailequ (fnames[0], "page") || !hard && tgetflag("ns"))
  1534.         noscroll++;
  1535.         Wrap = tgetflag("am");
  1536.         bad_so = tgetflag ("xs");
  1537.         clearptr = clearbuf;
  1538.         eraseln = tgetstr("ce",&clearptr);
  1539.         Clear = tgetstr("cl", &clearptr);
  1540.         Senter = tgetstr("so", &clearptr);
  1541.         Sexit = tgetstr("se", &clearptr);
  1542.         if ((soglitch = tgetnum("sg")) < 0)
  1543.         soglitch = 0;
  1544.  
  1545.         /*
  1546.          *  Set up for underlining:  some terminals don't need it;
  1547.          *  others have start/stop sequences, still others have an
  1548.          *  underline char sequence which is assumed to move the
  1549.          *  cursor forward one character.  If underline sequence
  1550.          *  isn't available, settle for standout sequence.
  1551.          */
  1552.  
  1553.         if (tgetflag("ul") || tgetflag("os"))
  1554.         ul_opt = 0;
  1555.         if ((chUL = tgetstr("uc", &clearptr)) == NULL )
  1556.         chUL = "";
  1557.         if (((ULenter = tgetstr("us", &clearptr)) == NULL ||
  1558.              (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) {
  1559.             if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
  1560.             ULenter = "";
  1561.             ULexit = "";
  1562.         } else
  1563.             ulglitch = soglitch;
  1564.         } else {
  1565.         if ((ulglitch = tgetnum("ug")) < 0)
  1566.             ulglitch = 0;
  1567.         }
  1568.  
  1569.         if (padstr = tgetstr("pc", &clearptr))
  1570.         PC = *padstr;
  1571.         Home = tgetstr("ho",&clearptr);
  1572.         if (Home == 0 || *Home == '\0')
  1573.         {
  1574.         if ((cursorm = tgetstr("cm", &clearptr)) != NULL) {
  1575.             strcpy(cursorhome, tgoto(cursorm, 0, 0));
  1576.             Home = cursorhome;
  1577.            }
  1578.         }
  1579.         EodClr = tgetstr("cd", &clearptr);
  1580.         if ((chBS = tgetstr("bc", &clearptr)) == NULL)
  1581.         chBS = "\b";
  1582.  
  1583.     }
  1584.     if ((shell = getenv("SHELL")) == NULL)
  1585.         shell = "/bin/sh";
  1586.     }
  1587.     no_intty = ioctl(fileno(stdin), TCGETA, &otty);
  1588.     ioctl(fileno(stderr), TCGETA, &otty);
  1589.     savetty = otty;
  1590.     slow_tty = (otty.c_cflag & CBAUD) < B1200;
  1591.     hardtabs = (otty.c_oflag & TABDLY) != XTABS;
  1592.     if (!no_tty) 
  1593.     otty.c_lflag &= ~(ICANON|ECHO);
  1594. }
  1595.  
  1596. readch ()
  1597. {
  1598.     char ch;
  1599.     extern int errno;
  1600.  
  1601.     errno = 0;
  1602.     if (read (2, &ch, 1) <= 0)
  1603.         if (errno != EINTR)
  1604.             end_it();
  1605.         else
  1606.             ch = otty.c_cc[VKILL];
  1607.     return (ch);
  1608. }
  1609.  
  1610. static char BS = '\b';
  1611. static char *BSB = "\b \b";
  1612. static char CARAT = '^';
  1613. #define ERASEONECHAR \
  1614.     if (docrterase) \
  1615.     write (2, BSB, sizeof(BSB)); \
  1616.     else \
  1617.     write (2, &BS, sizeof(BS));
  1618.  
  1619. ttyin (buf, nmax, pchar)
  1620. char buf[];
  1621. register int nmax;
  1622. char pchar;
  1623. {
  1624.     register char *sptr;
  1625.     register char ch;
  1626.     register int slash = 0;
  1627.     int    maxlen;
  1628.     char cbuf;
  1629.  
  1630.     sptr = buf;
  1631.     maxlen = 0;
  1632.     while (sptr - buf < nmax) {
  1633.     if (promptlen > maxlen) maxlen = promptlen;
  1634.     ch = readch ();
  1635.     if (ch == '\\') {
  1636.         slash++;
  1637.     }
  1638.     else if ((ch == otty.c_cc[VERASE]) && !slash) {
  1639.         if (sptr > buf) {
  1640.         --promptlen;
  1641.         ERASEONECHAR
  1642.         --sptr;
  1643.         if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
  1644.             --promptlen;
  1645.             ERASEONECHAR
  1646.         }
  1647.         continue;
  1648.         }
  1649.         else {
  1650.         if (!eraseln) promptlen = maxlen;
  1651.         longjmp (restore, 1);
  1652.         }
  1653.     }
  1654.     else if ((ch == otty.c_cc[VKILL]) && !slash) {
  1655.         if (hard) {
  1656.         show (ch);
  1657.         putchar ('\n');
  1658.         putchar (pchar);
  1659.         }
  1660.         else {
  1661.         putchar ('\r');
  1662.         putchar (pchar);
  1663.         if (eraseln)
  1664.             erase (1);
  1665.         else if (docrtkill)
  1666.             while (promptlen-- > 1)
  1667.             write (2, BSB, sizeof(BSB));
  1668.         promptlen = 1;
  1669.         }
  1670.         sptr = buf;
  1671.         fflush (stdout);
  1672.         continue;
  1673.     }
  1674.     if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) {
  1675.         ERASEONECHAR
  1676.         --sptr;
  1677.     }
  1678.     if (ch != '\\')
  1679.         slash = 0;
  1680.     *sptr++ = ch;
  1681.     if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
  1682.         ch += ch == RUBOUT ? -0100 : 0100;
  1683.         write (2, &CARAT, 1);
  1684.         promptlen++;
  1685.     }
  1686.     cbuf = ch;
  1687.     if (ch != '\n' && ch != ESC) {
  1688.         write (2, &cbuf, 1);
  1689.         promptlen++;
  1690.     }
  1691.     else
  1692.         break;
  1693.     }
  1694.     *--sptr = '\0';
  1695.     if (!eraseln) promptlen = maxlen;
  1696.     if (sptr - buf >= nmax - 1)
  1697.     error ("Line too long");
  1698. }
  1699.  
  1700. expand (outbuf, inbuf)
  1701. char *outbuf;
  1702. char *inbuf;
  1703. {
  1704.     register char *instr;
  1705.     register char *outstr;
  1706.     register char ch;
  1707.     char temp[200];
  1708.     int changed = 0;
  1709.  
  1710.     instr = inbuf;
  1711.     outstr = temp;
  1712.     while ((ch = *instr++) != '\0')
  1713.     switch (ch) {
  1714.     case '%':
  1715.         if (!no_intty) {
  1716.         strcpy (outstr, fnames[fnum]);
  1717.         outstr += strlen (fnames[fnum]);
  1718.         changed++;
  1719.         }
  1720.         else
  1721.         *outstr++ = ch;
  1722.         break;
  1723.     case '!':
  1724.         if (!shellp)
  1725.         error ("No previous command to substitute for");
  1726.         strcpy (outstr, shell_line);
  1727.         outstr += strlen (shell_line);
  1728.         changed++;
  1729.         break;
  1730.     case '\\':
  1731.         if (*instr == '%' || *instr == '!') {
  1732.         *outstr++ = *instr++;
  1733.         break;
  1734.         }
  1735.     default:
  1736.         *outstr++ = ch;
  1737.     }
  1738.     *outstr++ = '\0';
  1739.     strcpy (outbuf, temp);
  1740.     return (changed);
  1741. }
  1742.  
  1743. show (ch)
  1744. register char ch;
  1745. {
  1746.     char cbuf;
  1747.  
  1748.     if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
  1749.     ch += ch == RUBOUT ? -0100 : 0100;
  1750.     write (2, &CARAT, 1);
  1751.     promptlen++;
  1752.     }
  1753.     cbuf = ch;
  1754.     write (2, &cbuf, 1);
  1755.     promptlen++;
  1756. }
  1757.  
  1758. error (mess)
  1759. char *mess;
  1760. {
  1761.     if (clreol)
  1762.     cleareol ();
  1763.     else
  1764.     kill_line ();
  1765.     promptlen += strlen (mess);
  1766.     if (Senter && Sexit) {
  1767.     tputs (Senter, 1, putch);
  1768.     pr(mess);
  1769.     tputs (Sexit, 1, putch);
  1770.     }
  1771.     else
  1772.     pr (mess);
  1773.     fflush(stdout);
  1774.     errors++;
  1775.     longjmp (restore, 1);
  1776. }
  1777.  
  1778.  
  1779. set_tty ()
  1780. {
  1781.     otty.c_lflag &= ~(ICANON|ECHO);
  1782.     stty(fileno(stderr), &otty);
  1783. }
  1784.  
  1785. void reset_tty ()
  1786. {
  1787.     if (no_tty)
  1788.     return;
  1789.     if (pstate) {
  1790.     tputs(ULexit, 1, putch);
  1791.     fflush(stdout);
  1792.     pstate = 0;
  1793.     }
  1794.     otty.c_lflag |= ICANON|ECHO;
  1795.     stty(fileno(stderr), &savetty);
  1796. }
  1797.  
  1798. rdline (f)
  1799. register FILE *f;
  1800. {
  1801.     register char c;
  1802.     register char *p;
  1803.  
  1804.     p = Line;
  1805.     while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
  1806.     *p++ = c;
  1807.     if (c == '\n')
  1808.     Currline++;
  1809.     *p = '\0';
  1810. }
  1811.  
  1812. /* Come here when we get a suspend signal from the terminal */
  1813.  
  1814. void onsusp ()
  1815. {
  1816.     /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
  1817.     signal(SIGTTOU, SIG_IGN);
  1818.     reset_tty ();
  1819.     fflush (stdout);
  1820.     signal(SIGTTOU, SIG_DFL);
  1821.     /* Send the TSTP signal to suspend our process group */
  1822.     signal(SIGTSTP, SIG_DFL);
  1823. /*    sigsetmask(0);*/
  1824.     kill (0, SIGTSTP);
  1825.     /* Pause for station break */
  1826.  
  1827.     /* We're back */
  1828.     signal (SIGTSTP, onsusp);
  1829.     set_tty ();
  1830.     if (inwait)
  1831.         longjmp (restore, 1);
  1832. }
  1833.