home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume3 / less / part2 < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  56.3 KB

  1. From: ihnp4!nsc!nsc-pdc!rgb (Robert Bond)
  2. Subject: less part 2 of 2
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 121
  7. Submitted by: ihnp4!nsc!nsc-pdc!rgb (Robert Bond)
  8.  
  9.  
  10. #!/bin/sh-----cut here-----cut here-----cut here-----cut here-----
  11. # shar:    Shell Archiver
  12. #    Run the following text with /bin/sh to create:
  13. #    ch.c
  14. #    position.c
  15. #    input.c
  16. #    output.c
  17. #    screen.c
  18. #    prompt.c
  19. #    line.c
  20. #    signal.c
  21. #    help.c
  22. #    ttyin.c
  23. #    command.c
  24. #    version.c
  25. echo shar: extracting ch.c
  26. cat - << \SHAR_EOF > ch.c
  27. /*
  28.  * Low level character input from the input file.
  29.  * We use these special purpose routines which optimize moving
  30.  * both forward and backward from the current read pointer.
  31.  */
  32.  
  33. #include "less.h"
  34.  
  35. public int file = -1;    /* File descriptor of the input file */
  36.  
  37. /*
  38.  * Pool of buffers holding the most recently used blocks of the input file.
  39.  */
  40. #define BUFSIZ    1024
  41. static struct buf {
  42.     struct buf *next, *prev;
  43.     long block;
  44.     char data[BUFSIZ];
  45. };
  46. static struct buf *bufs = NULL;
  47. public int nbufs;
  48.  
  49. /*
  50.  * The buffer pool is kept as a doubly-linked circular list,
  51.  * in order from most- to least-recently used.
  52.  * The circular list is anchored by buf_anchor.
  53.  */
  54. static struct {
  55.     struct buf *next, *prev;
  56. } buf_anchor;
  57. #define    END_OF_CHAIN    ((struct buf *)&buf_anchor)
  58. #define    buf_head    buf_anchor.next
  59. #define    buf_tail    buf_anchor.prev
  60.  
  61. /*
  62.  * If we fail to allocate enough memory for buffers, we try to limp
  63.  * along with a minimum number of buffers.  
  64.  */
  65. #define    DEF_NBUFS    2    /* Minimum number of buffers */
  66.  
  67. extern int clean_data;
  68. extern int ispipe;
  69.  
  70. /*
  71.  * Current position in file.
  72.  * Stored as a block number and an offset into the block.
  73.  */
  74. static long ch_block;
  75. static int ch_offset;
  76.  
  77. /* 
  78.  * Length of file, needed if input is a pipe.
  79.  */
  80. static POSITION ch_fsize;
  81.  
  82. /*
  83.  * Largest block number read if input is standard input (a pipe).
  84.  */
  85. static long last_piped_block;
  86.  
  87. /*
  88.  * Get the character pointed to by the read pointer.
  89.  * ch_get() is a macro which is more efficient to call
  90.  * than fch_get (the function), in the usual case 
  91.  * that the block desired is at the head of the chain.
  92.  */
  93. #define    ch_get()   ((buf_head->block == ch_block) ? \
  94.             buf_head->data[ch_offset] : fch_get())
  95.     static int
  96. fch_get()
  97. {
  98.     register struct buf *bp;
  99.     register int n;
  100.     register int end;
  101.     POSITION pos;
  102.  
  103.     /*
  104.      * Look for a buffer holding the desired block.
  105.      */
  106.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  107.         if (bp->block == ch_block)
  108.             goto found;
  109.     /*
  110.      * Block is not in a buffer.  
  111.      * Take the least recently used buffer 
  112.      * and read the desired block into it.
  113.      */
  114.     bp = buf_tail;
  115.     bp->block = ch_block;
  116.     pos = ch_block * BUFSIZ;
  117.     if (ispipe)
  118.     {
  119.         /*
  120.          * The block requested should be one more than
  121.          * the last block read.
  122.          */
  123.         if (ch_block != ++last_piped_block)
  124.         {
  125.             /* This "should not happen". */
  126.             char message[80];
  127.             sprintf(message, "Pipe error: last %ld, want %ld\n",
  128.                 last_piped_block-1, ch_block);
  129.             error(message);
  130.             quit();
  131.         }
  132.     } else
  133.         lseek(file, pos, 0);
  134.  
  135.     /*
  136.      * Read the block.  This may take several reads if the input
  137.      * is coming from standard input, due to the nature of pipes.
  138.      */
  139.     end = 0;
  140.     while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
  141.         if ((end += n) >= BUFSIZ)
  142.             break;
  143.  
  144.     if (n < 0)
  145.     {
  146.         error("read error");
  147.         quit();
  148.     }
  149.  
  150.     /*
  151.      * Set an EOF marker in the buffered data itself.
  152.      * Then ensure the data is "clean": there are no 
  153.      * extra EOF chars in the data and that the "meta"
  154.      * bit (the 0200 bit) is reset in each char.
  155.      */
  156.     if (end < BUFSIZ)
  157.     {
  158.         ch_fsize = pos + end;
  159.         bp->data[end] = EOF;
  160.     }
  161.  
  162.     if (!clean_data)
  163.         while (--end >= 0)
  164.         {
  165.             bp->data[end] &= 0177;
  166.             if (bp->data[end] == EOF)
  167.                 bp->data[end] = '@';
  168.         }
  169.  
  170.     found:
  171.     /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
  172.     {
  173.         /*
  174.          * Move the buffer to the head of the buffer chain.
  175.          * This orders the buffer chain, most- to least-recently used.
  176.          */
  177.         bp->next->prev = bp->prev;
  178.         bp->prev->next = bp->next;
  179.  
  180.         bp->next = buf_head;
  181.         bp->prev = END_OF_CHAIN;
  182.         buf_head->prev = bp;
  183.         buf_head = bp;
  184.     }
  185.     return (bp->data[ch_offset]);
  186. }
  187.  
  188. /*
  189.  * Determine if a specific block is currently in one of the buffers.
  190.  */
  191.     static int
  192. buffered(block)
  193.     long block;
  194. {
  195.     register struct buf *bp;
  196.  
  197.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  198.         if (bp->block == block)
  199.             return (1);
  200.     return (0);
  201. }
  202.  
  203. /*
  204.  * Seek to a specified position in the file.
  205.  * Return 0 if successful, non-zero if can't seek there.
  206.  */
  207.     public int
  208. ch_seek(pos)
  209.     register POSITION pos;
  210. {
  211.     long new_block;
  212.  
  213.     new_block = pos / BUFSIZ;
  214.     if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
  215.     {
  216.         /*
  217.          * Set read pointer.
  218.          */
  219.         ch_block = new_block;
  220.         ch_offset = pos % BUFSIZ;
  221.         return (0);
  222.     }
  223.     return (1);
  224. }
  225.  
  226. /*
  227.  * Seek to the end of the file.
  228.  */
  229.     public int
  230. ch_end_seek()
  231. {
  232.     if (ispipe)
  233.     {
  234.         /*
  235.          * Do it the slow way: read till end of data.
  236.          */
  237.         while (ch_forw_get() != EOF)
  238.             ;
  239.     } else
  240.     {
  241.         (void) ch_seek((POSITION)(lseek(file, (off_t)0, 2)));
  242.     }
  243.     return (0);
  244. }
  245.  
  246. /*
  247.  * Return the length of the file, if known.
  248.  */
  249.     public POSITION
  250. ch_length()
  251. {
  252.     if (ispipe)
  253.         return (ch_fsize);
  254.     return ((POSITION)(lseek(file, (off_t)0, 2)));
  255. }
  256.  
  257. /*
  258.  * Return the current position in the file.
  259.  */
  260.     public POSITION
  261. ch_tell()
  262. {
  263.     return (ch_block * BUFSIZ + ch_offset);
  264. }
  265.  
  266. /*
  267.  * Get the current char and post-increment the read pointer.
  268.  */
  269.     public int
  270. ch_forw_get()
  271. {
  272.     register int c;
  273.  
  274.     c = ch_get();
  275.     if (c != EOF && ++ch_offset >= BUFSIZ)
  276.     {
  277.         ch_offset = 0;
  278.         ch_block ++;
  279.     }
  280.     return (c);
  281. }
  282.  
  283. /*
  284.  * Pre-decrement the read pointer and get the new current char.
  285.  */
  286.     public int
  287. ch_back_get()
  288. {
  289.     register int c;
  290.  
  291.     if (--ch_offset < 0)
  292.     {
  293.         if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
  294.         {
  295.             ch_offset = 0;
  296.             return (EOF);
  297.         }
  298.         ch_offset = BUFSIZ - 1;
  299.         ch_block--;
  300.     }
  301.     c = ch_get();
  302.     return (c);
  303. }
  304.  
  305. /*
  306.  * Initialize the buffer pool to all empty.
  307.  * Caller suggests that we use want_nbufs buffers.
  308.  */
  309.     public void
  310. ch_init(want_nbufs)
  311.     int want_nbufs;
  312. {
  313.     register struct buf *bp;
  314.     char *calloc();
  315.  
  316.     if (nbufs < want_nbufs)
  317.     {
  318.         /*
  319.          * We don't have enough buffers.  
  320.          * Free what we have (if any) and allocate some new ones.
  321.          */
  322.         if (bufs != NULL)
  323.             free((char *)bufs);
  324.         bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
  325.         nbufs = want_nbufs;
  326.         if (bufs == NULL)
  327.         {
  328.             /*
  329.              * Couldn't get that many.
  330.              * Try for a small default number of buffers.
  331.              */
  332.             char message[80];
  333.             sprintf(message,
  334.               "Cannot allocate %d buffers.  Using %d buffers.", 
  335.               nbufs, DEF_NBUFS);
  336.             error(message);
  337.             bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
  338.             nbufs = DEF_NBUFS;
  339.             if (bufs == NULL)
  340.             {
  341.                 /*
  342.                  * Couldn't even get the smaller number of bufs.
  343.                  * Something is wrong here, don't continue.
  344.                  */
  345.                 sprintf(message, 
  346.                 "Cannot even allocate %d buffers!  Quitting.\n",
  347.                   DEF_NBUFS);
  348.                 error(message);
  349.                 quit();
  350.                 /*NOTREACHED*/
  351.             }
  352.         }
  353.     }
  354.  
  355.     /*
  356.      * Initialize the buffers to empty.
  357.      * Set up the circular list.
  358.      */
  359.     for (bp = &bufs[0];  bp < &bufs[nbufs];  bp++)
  360.     {
  361.         bp->next = bp + 1;
  362.         bp->prev = bp - 1;
  363.         bp->block = (long)(-1);
  364.     }
  365.     bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
  366.     buf_head = &bufs[0];
  367.     buf_tail = &bufs[nbufs-1];
  368.     last_piped_block = -1;
  369.     ch_fsize = NULL_POSITION;
  370.     (void) ch_seek((POSITION)0);
  371. }
  372. SHAR_EOF
  373. echo shar: extracting position.c
  374. cat - << \SHAR_EOF > position.c
  375. /*
  376.  * Routines dealing with the "position" table.
  377.  * This is a table which tells the position (in the input file) of the
  378.  * first char on each currently displayed line.
  379.  *
  380.  * {{ The position table is scrolled by moving all the entries.
  381.  *    Would be better to have a circular table 
  382.  *    and just change a couple of pointers. }}
  383.  */
  384.  
  385. #include "less.h"
  386. #include "position.h"
  387.  
  388. #define    NPOS    100        /* {{ sc_height must be less than NPOS }} */
  389. static POSITION table[NPOS];    /* The position table */
  390.  
  391. extern int sc_width, sc_height;
  392.  
  393. /*
  394.  * Return the position of one of:
  395.  *    the top (first) line on the screen
  396.  *    the second line on the screen
  397.  *    the bottom line on the screen
  398.  *    the line after the bottom line on the screen
  399.  */
  400.     public POSITION
  401. position(where)
  402.     int where;
  403. {
  404.     switch (where)
  405.     {
  406.     case BOTTOM:
  407.         where = sc_height - 2;
  408.         break;
  409.     case BOTTOM_PLUS_ONE:
  410.         where = sc_height - 1;
  411.         break;
  412.     }
  413.     return (table[where]);
  414. }
  415.  
  416. /*
  417.  * Add a new file position to the bottom of the position table.
  418.  */
  419.     public void
  420. add_forw_pos(pos)
  421.     POSITION pos;
  422. {
  423.     register int i;
  424.  
  425.     /*
  426.      * Scroll the position table up.
  427.      */
  428.     for (i = 1;  i < sc_height;  i++)
  429.         table[i-1] = table[i];
  430.     table[sc_height - 1] = pos;
  431. }
  432.  
  433. /*
  434.  * Add a new file position to the top of the position table.
  435.  */
  436.     public void
  437. add_back_pos(pos)
  438.     POSITION pos;
  439. {
  440.     register int i;
  441.  
  442.     /*
  443.      * Scroll the position table down.
  444.      */
  445.     for (i = sc_height - 1;  i > 0;  i--)
  446.         table[i] = table[i-1];
  447.     table[0] = pos;
  448. }
  449.  
  450. /*
  451.  * Initialize the position table, done whenever we clear the screen.
  452.  */
  453.     public void
  454. pos_clear()
  455. {
  456.     register int i;
  457.  
  458.     for (i = 0;  i < sc_height;  i++)
  459.         table[i] = NULL_POSITION;
  460. }
  461.  
  462. /*
  463.  * See if the byte at a specified position is currently on the screen.
  464.  * Check the position table to see if the position falls within its range.
  465.  * Return the position table entry if found, -1 if not.
  466.  */
  467.     public int
  468. onscreen(pos)
  469.     POSITION pos;
  470. {
  471.     register int i;
  472.  
  473.     if (pos < table[0])
  474.         return (-1);
  475.     for (i = 1;  i < sc_height;  i++)
  476.         if (pos < table[i])
  477.             return (i-1);
  478.     return (-1);
  479. }
  480. SHAR_EOF
  481. echo shar: extracting input.c
  482. cat - << \SHAR_EOF > input.c
  483. /*
  484.  * High level routines dealing with getting lines of input 
  485.  * from the file being viewed.
  486.  *
  487.  * When we speak of "lines" here, we mean PRINTABLE lines;
  488.  * lines processed with respect to the screen width.
  489.  * We use the term "raw line" to refer to lines simply
  490.  * delimited by newlines; not processed with respect to screen width.
  491.  */
  492.  
  493. #include "less.h"
  494.  
  495. extern int do_bs;
  496. extern int squeeze;
  497. extern char *line;
  498.  
  499. /*
  500.  * Get the next line.
  501.  * A "current" position is passed and a "new" position is returned.
  502.  * The current position is the position of the first character of
  503.  * a line.  The new position is the position of the first character
  504.  * of the NEXT line.  The line obtained is the line starting at curr_pos.
  505.  */
  506.     public POSITION
  507. forw_line(curr_pos)
  508.     POSITION curr_pos;
  509. {
  510.     POSITION new_pos;
  511.     register int c;
  512.  
  513.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos))
  514.         return (NULL_POSITION);
  515.  
  516.     c = ch_forw_get();
  517.     if (c == EOF)
  518.         return (NULL_POSITION);
  519.  
  520.     prewind();
  521.     for (;;)
  522.     {
  523.         if (c == '\n' || c == EOF)
  524.         {
  525.             /*
  526.              * End of the line.
  527.              */
  528.             new_pos = ch_tell();
  529.             break;
  530.         }
  531.  
  532.         /*
  533.          * Append the char to the line and get the next char.
  534.          */
  535.         if (pappend(c))
  536.         {
  537.             /*
  538.              * The char won't fit in the line; the line
  539.              * is too long to print in the screen width.
  540.              * End the line here.
  541.              */
  542.             new_pos = ch_tell() - 1;
  543.             break;
  544.         }
  545.         c = ch_forw_get();
  546.     }
  547.     (void) pappend('\0');
  548.  
  549.     if (squeeze && *line == '\0')
  550.     {
  551.         /*
  552.          * This line is blank.
  553.          * Skip down to the last contiguous blank line
  554.          * and pretend it is the one which we are returning.
  555.          */
  556.         while ((c = ch_forw_get()) == '\n')
  557.             ;
  558.         if (c != EOF)
  559.             (void) ch_back_get();
  560.         new_pos = ch_tell();
  561.     }
  562.  
  563.     return (new_pos);
  564. }
  565.  
  566. /*
  567.  * Get the previous line.
  568.  * A "current" position is passed and a "new" position is returned.
  569.  * The current position is the position of the first character of
  570.  * a line.  The new position is the position of the first character
  571.  * of the PREVIOUS line.  The line obtained is the one starting at new_pos.
  572.  */
  573.     public POSITION
  574. back_line(curr_pos)
  575.     POSITION curr_pos;
  576. {
  577.     POSITION new_pos, begin_new_pos;
  578.     int c;
  579.  
  580.     if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
  581.         ch_seek(curr_pos-1))
  582.         return (NULL_POSITION);
  583.  
  584.     if (squeeze)
  585.     {
  586.         /*
  587.          * Find out if the "current" line was blank.
  588.          */
  589.         (void) ch_forw_get();    /* Skip the newline */
  590.         c = ch_forw_get();    /* First char of "current" line */
  591.         (void) ch_back_get();    /* Restore our position */
  592.         (void) ch_back_get();
  593.  
  594.         if (c == '\n')
  595.         {
  596.             /*
  597.              * The "current" line was blank.
  598.              * Skip over any preceeding blank lines,
  599.              * since we skipped them in forw_line().
  600.              */
  601.             while ((c = ch_back_get()) == '\n')
  602.                 ;
  603.             if (c == EOF)
  604.                 return (NULL_POSITION);
  605.             (void) ch_forw_get();
  606.         }
  607.     }
  608.  
  609.     /*
  610.      * Scan backwards until we hit the beginning of the line.
  611.      */
  612.     for (;;)
  613.     {
  614.         c = ch_back_get();
  615.         if (c == '\n')
  616.         {
  617.             /*
  618.              * This is the newline ending the previous line.
  619.              * We have hit the beginning of the line.
  620.              */
  621.             new_pos = ch_tell() + 1;
  622.             break;
  623.         }
  624.         if (c == EOF)
  625.         {
  626.             /*
  627.              * We have hit the beginning of the file.
  628.              * This must be the first line in the file.
  629.              * This must, of course, be the beginning of the line.
  630.              */
  631.             new_pos = (POSITION)0;
  632.             break;
  633.         }
  634.     }
  635.  
  636.     /*
  637.      * Now scan forwards from the beginning of this line.
  638.      * We keep discarding "printable lines" (based on screen width)
  639.      * until we reach the curr_pos.
  640.      *
  641.      * {{ This algorithm is pretty inefficient if the lines
  642.      *    are much longer than the screen width, 
  643.      *    but I don't know of any better way. }}
  644.      */
  645.     if (ch_seek(new_pos))
  646.         return (NULL_POSITION);
  647.     loop:
  648.     begin_new_pos = new_pos;
  649.     prewind();
  650.  
  651.     do
  652.     {
  653.         c = ch_forw_get();
  654.         new_pos++;
  655.         if (c == '\n')
  656.             break;
  657.         if (pappend(c))
  658.         {
  659.             /*
  660.              * Got a full printable line, but we haven't
  661.              * reached our curr_pos yet.  Discard the line
  662.              * and start a new one.
  663.              */
  664.             (void) pappend('\0');
  665.             (void) ch_back_get();
  666.             new_pos--;
  667.             goto loop;
  668.         }
  669.     } while (new_pos < curr_pos);
  670.  
  671.     (void) pappend('\0');
  672.  
  673.     return (begin_new_pos);
  674. }
  675. SHAR_EOF
  676. echo shar: extracting output.c
  677. cat - << \SHAR_EOF > output.c
  678. /*
  679.  * High level routines dealing with the output to the screen.
  680.  */
  681.  
  682. #include "less.h"
  683.  
  684. extern int sigs;
  685. extern int sc_width, sc_height;
  686. extern int ul_width, ue_width;
  687. extern int so_width, se_width;
  688. extern int tabstop;
  689. extern int twiddle;
  690. extern char *line;
  691. extern char *first_cmd;
  692.  
  693. /*
  694.  * Display the line which is in the line buffer.
  695.  */
  696.     public void
  697. put_line()
  698. {
  699.     register char *p;
  700.     register int c;
  701.     register int column;
  702.     extern int auto_wrap, ignaw;
  703.  
  704.     if (sigs)
  705.         /*
  706.          * Don't output if a signal is pending.
  707.          */
  708.         return;
  709.  
  710.     if (line == NULL)
  711.         line = (twiddle) ? "~" : "";
  712.  
  713.     column = 0;
  714.     for (p = line;  *p != '\0';  p++)
  715.     {
  716.         switch (c = *p)
  717.         {
  718.         case UL_CHAR:
  719.             ul_enter();
  720.             column += ul_width;
  721.             break;
  722.         case UE_CHAR:
  723.             ul_exit();
  724.             column += ue_width;
  725.             break;
  726.         case '\t':
  727.             do
  728.             {
  729.                 putc(' ');
  730.                 column++;
  731.             } while ((column % tabstop) != 0);
  732.             break;
  733.         case '\b':
  734.             putbs();
  735.             column--;
  736.             break;
  737.         default:
  738.             if (c & 0200)
  739.             {
  740.                 putc('^');
  741.                 putc(c & 0177);
  742.                 column += 2;
  743.             } else
  744.             {
  745.                 putc(c);
  746.                 column++;
  747.             }
  748.         }
  749.     }
  750.     if (column < sc_width || !auto_wrap || ignaw)
  751.         putc('\n');
  752. }
  753.  
  754. /*
  755.  * Is a given character a "control" character?
  756.  * {{ ASCII DEPENDENT }}
  757.  */
  758.     public int
  759. control_char(c)
  760.     int c;
  761. {
  762.     return (c < ' ' || c == '\177');
  763. }
  764.  
  765. /*
  766.  * Return the printable character used to identify a control character
  767.  * (printed after a carat; e.g. '\3' => "^C").
  768.  * {{ ASCII DEPENDENT }}
  769.  */
  770.     public int
  771. carat_char(c)
  772.     int c;
  773. {
  774.     return ((c == '\177') ? '?' : (c | 0100));
  775. }
  776.  
  777.  
  778. static char obuf[1024];
  779. static char *ob = obuf;
  780.  
  781. /*
  782.  * Flush buffered output.
  783.  */
  784.     public void
  785. flush()
  786. {
  787.     write(1, obuf, ob-obuf);
  788.     ob = obuf;
  789. }
  790.  
  791. /*
  792.  * Discard buffered output.
  793.  */
  794.     public void
  795. dropout()
  796. {
  797.     ob = obuf;
  798. }
  799.  
  800. /*
  801.  * Output a character.
  802.  */
  803.     public void
  804. putc(c)
  805.     int c;
  806. {
  807.     if (ob >= &obuf[sizeof(obuf)])
  808.         flush();
  809.     *ob++ = c;
  810. }
  811.  
  812. /*
  813.  * Output a string.
  814.  */
  815.     public void
  816. puts(s)
  817.     register char *s;
  818. {
  819.     while (*s != '\0')
  820.         putc(*s++);
  821. }
  822.  
  823. /*
  824.  * Output a message in the lower left corner of the screen
  825.  * and wait for carriage return.
  826.  */
  827.  
  828. static char return_to_continue[] = "  (press RETURN)";
  829.  
  830.     public void
  831. error(s)
  832.     char *s;
  833. {
  834.     register int c;
  835.     static char buf[2];
  836.  
  837.     lower_left();
  838.     clear_eol();
  839.     so_enter();
  840.     puts(s);
  841.     puts(return_to_continue);
  842.     so_exit();
  843.  
  844. #if ONLY_RETURN
  845.     while ((c = getc()) != '\n' && c != '\r')
  846.         bell();
  847. #else
  848.     c = getc();
  849.     if (c != '\n' && c != '\r' && c != ' ')
  850.     {
  851.         buf[0] = c;
  852.         first_cmd = buf;
  853.     }
  854. #endif
  855.  
  856.     if (strlen(s) > sc_width)
  857.         repaint();
  858. }
  859.  
  860.     public int
  861. error_width()
  862. {
  863.     /*
  864.      * Don't use the last position, because some terminals
  865.      * will scroll if you write in the last char of the last line.
  866.      */
  867.     return (sc_width - 
  868.         (sizeof(return_to_continue) + so_width + se_width + 1));
  869. }
  870. SHAR_EOF
  871. echo shar: extracting screen.c
  872. cat - << \SHAR_EOF > screen.c
  873. /*
  874.  * Routines which deal with the characteristics of the terminal.
  875.  * Uses termcap to be as terminal-independent as possible.
  876.  *
  877.  * {{ Someday this should be rewritten to use curses. }}
  878.  */
  879.  
  880. #include "less.h"
  881. #if XENIX
  882. #include <sys/types.h>
  883. #include <sys/ioctl.h>
  884. #endif
  885.  
  886. #if TERMIO
  887. #include <termio.h>
  888. #else
  889. #include <sgtty.h>
  890. #endif
  891.  
  892. /*
  893.  * Strings passed to tputs() to do various terminal functions.
  894.  */
  895. static char
  896.     *sc_pad,        /* Pad string */
  897.     *sc_home,        /* Cursor home */
  898.     *sc_addline,        /* Add line, scroll down following lines */
  899.     *sc_lower_left,        /* Cursor to last line, first column */
  900.     *sc_move,        /* General cursor positioning */
  901.     *sc_clear,        /* Clear screen */
  902.     *sc_eol_clear,        /* Clear to end of line */
  903.     *sc_s_in,        /* Enter standout (highlighted) mode */
  904.     *sc_s_out,        /* Exit standout mode */
  905.     *sc_u_in,        /* Enter underline mode */
  906.     *sc_u_out,        /* Exit underline mode */
  907.     *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  908.     *sc_backspace,        /* Backspace cursor */
  909.     *sc_init,        /* Startup terminal initialization */
  910.     *sc_deinit;        /* Exit terminal de-intialization */
  911. static int dumb;
  912. static int hard;
  913.  
  914. public int auto_wrap;        /* Terminal does \r\n when write past margin */
  915. public int ignaw;        /* Terminal ignores \n immediately after wrap */
  916. public int erase_char, kill_char; /* The user's erase and line-kill chars */
  917. public int sc_width, sc_height;    /* Height & width of screen */
  918. public int ul_width, ue_width;    /* Printing width of underline sequences */
  919. public int so_width, se_width;    /* Printing width of standout sequences */
  920.  
  921. /*
  922.  * These two variables are sometimes defined in,
  923.  * and needed by, the termcap library.
  924.  * It may be necessary on some systems to declare them extern here.
  925.  */
  926. /*extern*/ short ospeed;    /* Terminal output baud rate */
  927. /*extern*/ char PC;        /* Pad character */
  928.  
  929. extern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  930. extern int know_dumb;        /* Don't complain about a dumb terminal */
  931. extern int back_scroll;
  932. char *tgetstr();
  933. char *tgoto();
  934.  
  935. /*
  936.  * Change terminal to "raw mode", or restore to "normal" mode.
  937.  * "Raw mode" means 
  938.  *    1. An outstanding read will complete on receipt of a single keystroke.
  939.  *    2. Input is not echoed.  
  940.  *    3. On output, \n is mapped to \r\n.
  941.  *    4. \t is NOT be expanded into spaces.
  942.  *    5. Signal-causing characters such as ctrl-C (interrupt),
  943.  *       etc. are NOT disabled.
  944.  * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  945.  */
  946.     public void
  947. raw_mode(on)
  948.     int on;
  949. {
  950. #if TERMIO
  951.     struct termio s;
  952.     static struct termio save_term;
  953.  
  954.     if (on)
  955.     {
  956.         /*
  957.          * Get terminal modes.
  958.          */
  959.         ioctl(2, TCGETA, &s);
  960.  
  961.         /*
  962.          * Save modes and set certain variables dependent on modes.
  963.          */
  964.         save_term = s;
  965.         ospeed = s.c_cflag & CBAUD;
  966.         erase_char = s.c_cc[VERASE];
  967.         kill_char = s.c_cc[VKILL];
  968.  
  969.         /*
  970.          * Set the modes to the way we want them.
  971.          */
  972.         s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  973.         s.c_oflag |=  (OPOST|ONLCR|TAB3);
  974.         s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  975.         s.c_cc[VMIN] = 1;
  976.         s.c_cc[VTIME] = 0;
  977.     } else
  978.     {
  979.         /*
  980.          * Restore saved modes.
  981.          */
  982.         s = save_term;
  983.     }
  984.     ioctl(2, TCSETAW, &s);
  985. #else
  986.     struct sgttyb s;
  987.     static struct sgttyb save_term;
  988.  
  989.     if (on)
  990.     {
  991.         /*
  992.          * Get terminal modes.
  993.          */
  994.         ioctl(2, TIOCGETP, &s);
  995.  
  996.         /*
  997.          * Save modes and set certain variables dependent on modes.
  998.          */
  999.         save_term = s;
  1000.         ospeed = s.sg_ospeed;
  1001.         erase_char = s.sg_erase;
  1002.         kill_char = s.sg_kill;
  1003.  
  1004.         /*
  1005.          * Set the modes to the way we want them.
  1006.          */
  1007.         s.sg_flags |= CBREAK;
  1008.         s.sg_flags &= ~(ECHO|XTABS);
  1009.     } else
  1010.     {
  1011.         /*
  1012.          * Restore saved modes.
  1013.          */
  1014.         s = save_term;
  1015.     }
  1016.     ioctl(2, TIOCSETN, &s);
  1017. #endif
  1018. }
  1019.  
  1020. static int couldnt = 0;
  1021.  
  1022.     static void
  1023. cannot(s)
  1024.     char *s;
  1025. {
  1026.     if (know_dumb)
  1027.         /* 
  1028.          * He knows he has a dumb terminal, so don't tell him. 
  1029.          */
  1030.         return;
  1031.  
  1032.     printf("WARNING: terminal cannot \"%s\"\n", s);
  1033.     couldnt = 1;
  1034. }
  1035.  
  1036. /*
  1037.  * Get terminal capabilities via termcap.
  1038.  */
  1039.     public void
  1040. get_term()
  1041. {
  1042.     char termbuf[1024];
  1043.     char *sp;
  1044.     static char sbuf[150];
  1045.  
  1046.     char *getenv();
  1047.  
  1048.     /*
  1049.      * Find out what kind of terminal this is.
  1050.      */
  1051.     if (tgetent(termbuf, getenv("TERM")) <= 0)
  1052.         dumb = 1;
  1053.  
  1054.     /*
  1055.      * Get size of the screen.
  1056.      */
  1057.     if (dumb || (sc_height = tgetnum("li")) < 0 || tgetflag("hc"))
  1058.     {
  1059.         /* Oh no, this is a hardcopy terminal. */
  1060.         hard = 1;
  1061.         sc_height = 24;
  1062.     }
  1063.     if (dumb || (sc_width = tgetnum("co")) < 0)
  1064.         sc_width = 80;
  1065.  
  1066.     auto_wrap = tgetflag("am");
  1067.     ignaw = tgetflag("xn");
  1068.  
  1069.     /*
  1070.      * Assumes termcap variable "sg" is the printing width of
  1071.      * the standout sequence, the end standout sequence,
  1072.      * the underline sequence, and the end underline sequence.
  1073.      */
  1074.     if ((ul_width = tgetnum("sg")) < 0)
  1075.         ul_width = 0;
  1076.     so_width = se_width = ue_width = ul_width;
  1077.  
  1078.     /*
  1079.      * Get various string-valued capabilities.
  1080.      */
  1081.     sp = sbuf;
  1082.  
  1083.     sc_pad = (dumb) ? NULL : tgetstr("pc", &sp);
  1084.     if (sc_pad != NULL)
  1085.         PC = *sc_pad;
  1086.  
  1087.     sc_init = (dumb) ? NULL : tgetstr("ti", &sp);
  1088.     if (sc_init == NULL)
  1089.         sc_init = "";
  1090.  
  1091.     sc_deinit= (dumb) ? NULL : tgetstr("te", &sp);
  1092.     if (sc_deinit == NULL)
  1093.         sc_deinit = "";
  1094.  
  1095.     sc_eol_clear = (dumb) ? NULL : tgetstr("ce", &sp);
  1096.     if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  1097.     {
  1098.         cannot("clear to end of line");
  1099.         sc_eol_clear = "";
  1100.     }
  1101.  
  1102.     sc_clear = (dumb) ? NULL : tgetstr("cl", &sp);
  1103.     if (hard || sc_clear == NULL || *sc_clear == '\0')
  1104.     {
  1105.         cannot("clear screen");
  1106.         sc_clear = "\n\n";
  1107.     }
  1108.  
  1109.     sc_move = (dumb) ? NULL : tgetstr("cm", &sp);
  1110.     if (hard || sc_move == NULL || *sc_move == '\0')
  1111.     {
  1112.         /*
  1113.          * This is not an error here, because we don't 
  1114.          * always need sc_move.
  1115.          * We need it only if we don't have home or lower-left.
  1116.          */
  1117.         sc_move = "";
  1118.     }
  1119.  
  1120.     sc_s_in = (dumb) ? NULL : tgetstr("so", &sp);
  1121.     if (hard || sc_s_in == NULL)
  1122.         sc_s_in = "";
  1123.  
  1124.     sc_s_out = (dumb) ? NULL : tgetstr("se", &sp);
  1125.     if (hard || sc_s_out == NULL)
  1126.         sc_s_out = "";
  1127.  
  1128.     sc_u_in = (dumb) ? NULL : tgetstr("us", &sp);
  1129.     if (hard || sc_u_in == NULL)
  1130.         sc_u_in = sc_s_in;
  1131.  
  1132.     sc_u_out = (dumb) ? NULL : tgetstr("ue", &sp);
  1133.     if (hard || sc_u_out == NULL)
  1134.         sc_u_out = sc_s_out;
  1135.  
  1136.     sc_visual_bell = (dumb) ? NULL : tgetstr("vb", &sp);
  1137.     if (hard || sc_visual_bell == NULL)
  1138.         sc_visual_bell = "";
  1139.  
  1140.     sc_home = (dumb) ? NULL : tgetstr("ho", &sp);
  1141.     if (hard || sc_home == NULL || *sc_home == '\0')
  1142.     {
  1143.         if (*sc_move == '\0')
  1144.         {
  1145.             cannot("home cursor");
  1146.             /*
  1147.              * This last resort for sc_home is supposed to
  1148.              * be an up-arrow suggesting moving to the 
  1149.              * top of the "virtual screen". (The one in
  1150.              * your imagination as you try to use this on
  1151.              * a hard copy terminal.)
  1152.              */
  1153.             sc_home = "|\b^";        
  1154.         } else
  1155.         {
  1156.             /* 
  1157.              * No "home" string,
  1158.              * but we can use "move(0,0)".
  1159.              */
  1160.             strcpy(sp, tgoto(sc_move, 0, 0));
  1161.             sc_home = sp;
  1162.             sp += strlen(sp) + 1;
  1163.         }
  1164.     }
  1165.  
  1166.     sc_lower_left = (dumb) ? NULL : tgetstr("ll", &sp);
  1167.     if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
  1168.     {
  1169.         if (*sc_move == '\0')
  1170.         {
  1171.             cannot("move cursor to lower left of screen");
  1172.             sc_lower_left = "\r";
  1173.         } else
  1174.         {
  1175.             /*
  1176.              * No "lower-left" string, 
  1177.              * but we can use "move(0,last-line)".
  1178.              */
  1179.             strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  1180.             sc_lower_left = sp;
  1181.             sp += strlen(sp) + 1;
  1182.         }
  1183.     }
  1184.  
  1185.     /*
  1186.      * To add a line at top of screen and scroll the display down,
  1187.      * we use "al" (add line) or "sr" (scroll reverse).
  1188.      */
  1189.     if (dumb)
  1190.         sc_addline = NULL;
  1191.     else if ((sc_addline = tgetstr("al", &sp)) == NULL || 
  1192.          *sc_addline == '\0')
  1193.         sc_addline = tgetstr("sr", &sp);
  1194.  
  1195.     if (hard || sc_addline == NULL || *sc_addline == '\0')
  1196.     {
  1197.         cannot("scroll backwards");
  1198.         sc_addline = "";
  1199.         /* Force repaint on any backward movement */
  1200.         back_scroll = 0;
  1201.     }
  1202.  
  1203.     if (dumb || tgetflag("bs"))
  1204.         sc_backspace = "\b";
  1205.     else
  1206.     {
  1207.         sc_backspace = tgetstr("bc", &sp);
  1208.         if (sc_backspace == NULL || *sc_backspace == '\0')
  1209.             sc_backspace = "\b";
  1210.     }
  1211.  
  1212.     if (couldnt)
  1213.         /* Give him time to read all the "cannot" messages. */
  1214.         error("");
  1215. }
  1216.  
  1217.  
  1218. /*
  1219.  * Below are the functions which perform all the 
  1220.  * terminal-specific screen manipulation.
  1221.  */
  1222.  
  1223.  
  1224. /*
  1225.  * Initialize terminal
  1226.  */
  1227.     public void
  1228. init()
  1229. {
  1230.     tputs(sc_init, sc_height, putc);
  1231. }
  1232.  
  1233. /*
  1234.  * Deinitialize terminal
  1235.  */
  1236.     public void
  1237. deinit()
  1238. {
  1239.     tputs(sc_deinit, sc_height, putc);
  1240. }
  1241.  
  1242. /*
  1243.  * Home cursor (move to upper left corner of screen).
  1244.  */
  1245.     public void
  1246. home()
  1247. {
  1248.     tputs(sc_home, 1, putc);
  1249. }
  1250.  
  1251. /*
  1252.  * Add a blank line (called with cursor at home).
  1253.  * Should scroll the display down.
  1254.  */
  1255.     public void
  1256. add_line()
  1257. {
  1258.     tputs(sc_addline, sc_height, putc);
  1259. }
  1260.  
  1261. /*
  1262.  * Move cursor to lower left corner of screen.
  1263.  */
  1264.     public void
  1265. lower_left()
  1266. {
  1267.     tputs(sc_lower_left, 1, putc);
  1268. }
  1269.  
  1270. /*
  1271.  * Ring the terminal bell.
  1272.  */
  1273.     public void
  1274. bell()
  1275. {
  1276.     if (quiet == VERY_QUIET)
  1277.         vbell();
  1278.     else
  1279.         putc('\7');
  1280. }
  1281.  
  1282. /*
  1283.  * Output the "visual bell", if there is one.
  1284.  */
  1285.     public void
  1286. vbell()
  1287. {
  1288.     if (*sc_visual_bell == '\0')
  1289.         return;
  1290.     tputs(sc_visual_bell, sc_height, putc);
  1291. }
  1292.  
  1293. /*
  1294.  * Clear the screen.
  1295.  */
  1296.     public void
  1297. clear()
  1298. {
  1299.     tputs(sc_clear, sc_height, putc);
  1300. }
  1301.  
  1302. /*
  1303.  * Clear from the cursor to the end of the cursor's line.
  1304.  * {{ This must not move the cursor. }}
  1305.  */
  1306.     public void
  1307. clear_eol()
  1308. {
  1309.     tputs(sc_eol_clear, 1, putc);
  1310. }
  1311.  
  1312. /*
  1313.  * Begin "standout" (bold, underline, or whatever).
  1314.  */
  1315.     public void
  1316. so_enter()
  1317. {
  1318.     tputs(sc_s_in, 1, putc);
  1319. }
  1320.  
  1321. /*
  1322.  * End "standout".
  1323.  */
  1324.     public void
  1325. so_exit()
  1326. {
  1327.     tputs(sc_s_out, 1, putc);
  1328. }
  1329.  
  1330. /*
  1331.  * Begin "underline" (hopefully real underlining, 
  1332.  * otherwise whatever the terminal provides).
  1333.  */
  1334.     public void
  1335. ul_enter()
  1336. {
  1337.     tputs(sc_u_in, 1, putc);
  1338. }
  1339.  
  1340. /*
  1341.  * End "underline".
  1342.  */
  1343.     public void
  1344. ul_exit()
  1345. {
  1346.     tputs(sc_u_out, 1, putc);
  1347. }
  1348.  
  1349. /*
  1350.  * Erase the character to the left of the cursor 
  1351.  * and move the cursor left.
  1352.  */
  1353.     public void
  1354. backspace()
  1355. {
  1356.     /* 
  1357.      * Try to erase the previous character by overstriking with a space.
  1358.      */
  1359.     tputs(sc_backspace, 1, putc);
  1360.     putc(' ');
  1361.     tputs(sc_backspace, 1, putc);
  1362. }
  1363.  
  1364. /*
  1365.  * Output a plain backspace, without erasing the previous char.
  1366.  */
  1367.     public void
  1368. putbs()
  1369. {
  1370.     tputs(sc_backspace, 1, putc);
  1371. }
  1372. SHAR_EOF
  1373. echo shar: extracting prompt.c
  1374. cat - << \SHAR_EOF > prompt.c
  1375. /*
  1376.  * Prompting and other messages.
  1377.  * There are three flavors of prompts, SHORT, MEDIUM and LONG,
  1378.  * selected by the -m/-M options.
  1379.  * A prompt is either a colon or a message composed of various
  1380.  * pieces, such as the name of the file being viewed, the percentage
  1381.  * into the file, etc.
  1382.  */
  1383.  
  1384. #include "less.h"
  1385. #include "position.h"
  1386.  
  1387. extern int pr_type;
  1388. extern int ispipe;
  1389. extern int hit_eof;
  1390. extern int new_file;
  1391. extern int sc_width;
  1392. extern char current_file[];
  1393. extern int ac;
  1394. extern char **av;
  1395. extern int curr_ac;
  1396.  
  1397. static char message[500];
  1398.  
  1399. /*
  1400.  * Append the name of the current file (to the message buffer).
  1401.  */
  1402.     static void
  1403. ap_filename()
  1404. {
  1405.     if (!ispipe)
  1406.         sprintf(message + strlen(message), 
  1407.             "%s", current_file);
  1408. }
  1409.  
  1410. /*
  1411.  * Append the "file N of M" message.
  1412.  */
  1413.     static void
  1414. ap_of()
  1415. {
  1416.     if (ac > 1)
  1417.         sprintf(message + strlen(message), 
  1418.             " (file %d of %d)", curr_ac+1, ac);
  1419. }
  1420.  
  1421. /*
  1422.  * Append the byte offset into the current file.
  1423.  */
  1424.     static void
  1425. ap_byte()
  1426. {
  1427.     POSITION pos, len;
  1428.  
  1429.     pos = position(BOTTOM_PLUS_ONE);
  1430.     if (pos != NULL_POSITION)
  1431.     {
  1432.         sprintf(message + strlen(message), 
  1433.             " byte %ld", pos);
  1434.         len = ch_length();
  1435.         if (len > 0)
  1436.             sprintf(message + strlen(message), 
  1437.                 "/%ld", len);
  1438.     }
  1439. }
  1440.  
  1441. /*
  1442.  * Append the percentage into the current file.
  1443.  * If we cannot find the percentage and must_print is true,
  1444.  * the use the byte offset.
  1445.  */
  1446.     static void
  1447. ap_percent(must_print)
  1448. {
  1449.     POSITION pos,len;
  1450.  
  1451.     pos = position(BOTTOM_PLUS_ONE);
  1452.     len = ch_length();
  1453.     if (len > 0 && pos != NULL_POSITION)
  1454.         sprintf(message + strlen(message),
  1455.             " (%ld%%)", (100 * pos) / len);
  1456.     else if (must_print)
  1457.         ap_byte();
  1458. }
  1459.  
  1460. /*
  1461.  * Append the end-of-file message.
  1462.  */
  1463.     static void
  1464. ap_eof()
  1465. {
  1466.     strcat(message, " END");
  1467.     if (curr_ac + 1 < ac)
  1468.         sprintf(message + strlen(message),
  1469.             " - Next: %s", av[curr_ac+1]);
  1470. }
  1471.  
  1472. /*
  1473.  * Return a message suitable for printing by the "=" command.
  1474.  */
  1475.     public char *
  1476. eq_message()
  1477. {
  1478.     message[0] = '\0';
  1479.     ap_filename();
  1480.     ap_of();
  1481.     ap_byte();
  1482.     ap_percent(0);
  1483.     /*
  1484.      * Truncate to the screen width.
  1485.      * {{ This isn't very nice. }}
  1486.      */
  1487.     message[error_width()] = '\0';
  1488.     return (message);
  1489. }
  1490.  
  1491. /*
  1492.  * Return a prompt.
  1493.  * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
  1494.  * If we can't come up with an appropriate prompt, return NULL
  1495.  * and the caller will prompt with a colon.
  1496.  */
  1497.     public char *
  1498. pr_string()
  1499. {
  1500.     message[0] = '\0';
  1501.     switch (pr_type)
  1502.     {
  1503.     case PR_SHORT:
  1504.         if (new_file)
  1505.         {
  1506.             ap_filename();
  1507.             ap_of();
  1508.         }
  1509.         if (hit_eof)
  1510.             ap_eof();
  1511.         break;
  1512.     case PR_MEDIUM:
  1513.         if (new_file)
  1514.         {
  1515.             ap_filename();
  1516.             ap_of();
  1517.         }
  1518.         if (hit_eof)
  1519.             ap_eof();
  1520.         else
  1521.             ap_percent(1);
  1522.         break;
  1523.     case PR_LONG:
  1524.         ap_filename();
  1525.         if (new_file)
  1526.             ap_of();
  1527.         ap_byte();
  1528.         if (hit_eof)
  1529.             ap_eof();
  1530.         else
  1531.             ap_percent(0);
  1532.         break;
  1533.     }
  1534.     new_file = 0;
  1535.     if (message[0] == '\0')
  1536.         return (NULL);
  1537.     /*
  1538.      * Truncate to the screen width.
  1539.      * {{ This isn't very nice. }}
  1540.      */
  1541.     message[sc_width-2] = '\0';
  1542.     return (message);
  1543. }
  1544. SHAR_EOF
  1545. echo shar: extracting line.c
  1546. cat - << \SHAR_EOF > line.c
  1547. /*
  1548.  * Routines to manipulate the "line buffer".
  1549.  * The line buffer holds a line of output as it is being built
  1550.  * in preparation for output to the screen.
  1551.  * We keep track of the PRINTABLE length of the line as it is being built.
  1552.  */
  1553.  
  1554. #include "less.h"
  1555.  
  1556. static char linebuf[1024];    /* Buffer which holds the current output line */
  1557. static char *curr;        /* Pointer into linebuf */
  1558. static int column;        /* Printable length, accounting for
  1559.                    backspaces, etc. */
  1560. /*
  1561.  * A ridiculously complex state machine takes care of backspaces 
  1562.  * when in BS_UNDERLINE mode.  The complexity arises from the attempt
  1563.  * to deal with all cases, especially involving long lines with underlining.
  1564.  * There are still some cases which will break it.
  1565.  *
  1566.  * There are four states:
  1567.  *    UL_NORMAL is the normal state (not in underline mode).
  1568.  *    UL_YES means we are in underline mode.  We expect to get
  1569.  *        either a sequence like "_\bX" or "X\b_" to continue
  1570.  *        underline mode, or just some ordinary characters
  1571.  *        (no backspaces) to end underline mode.
  1572.  *    UL_X means we are one character after UL_YES
  1573.  *        (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
  1574.  *    UL_XB means we are one character after UL_X 
  1575.  *        (we have gotten the backspace in "_\bX" or "X\b_";
  1576.  *        we expect one more ordinary character, 
  1577.  *        which will put us back in state UL_YES).
  1578.  */
  1579. static int ul_state;        /* Currently in underline mode? */
  1580. #define    UL_NORMAL    0    /* Not in underline mode */
  1581. #define    UL_YES        1    /* In underline, need next char */
  1582. #define    UL_X        2    /* In underline, got char, need \b */
  1583. #define    UL_XB        3    /* In underline, got char & \b, need one more */
  1584.  
  1585. public char *line;        /* Pointer to the current line.
  1586.                    Usually points to linebuf. */
  1587.  
  1588. extern int bs_mode;
  1589. extern int tabstop;
  1590. extern int ul_width, ue_width;
  1591. extern int sc_width, sc_height;
  1592.  
  1593. /*
  1594.  * Rewind the line buffer.
  1595.  */
  1596.     public void
  1597. prewind()
  1598. {
  1599.     line = curr = linebuf;
  1600.     ul_state = UL_NORMAL;
  1601.     column = 0;
  1602. }
  1603.  
  1604. /*
  1605.  * Append a character to the line buffer.
  1606.  * Expand tabs into spaces, handle underlining.
  1607.  * Returns 0 if ok, 1 if couldn't fit in buffer.
  1608.  */
  1609.  
  1610. #define    NEW_COLUMN(newcol)    if ((newcol) + ((ul_state)?ue_width:0) > sc_width) \
  1611.                     return (1); else column = (newcol)
  1612.  
  1613.     public int
  1614. pappend(c)
  1615.     int c;
  1616. {
  1617.     if (c == '\0')
  1618.     {
  1619.         /*
  1620.          * Terminate underline mode, if necessary.
  1621.          * Append a '\0' to the end of the line.
  1622.          */
  1623.         switch (ul_state)
  1624.         {
  1625.         case UL_X:
  1626.             curr[0] = curr[-1];
  1627.             curr[-1] = UE_CHAR;
  1628.             curr++;
  1629.             break;
  1630.         case UL_XB:
  1631.         case UL_YES:
  1632.             *curr++ = UE_CHAR;
  1633.             break;
  1634.         }
  1635.         ul_state = UL_NORMAL;
  1636.         *curr = '\0';
  1637.         return (0);
  1638.     }
  1639.  
  1640.     if (curr > linebuf + sizeof(linebuf) - 12)
  1641.         /*
  1642.          * Almost out of room in the line buffer.
  1643.          * Don't take any chances.
  1644.          * {{ Linebuf is supposed to be big enough that this
  1645.          *    will never happen, but may need to be made 
  1646.          *    bigger for wide screens or lots of backspaces. }}
  1647.          */
  1648.         return (1);
  1649.  
  1650.     if (bs_mode == BS_UNDERLINE)
  1651.     {
  1652.         /*
  1653.          * Advance the state machine.
  1654.          */
  1655.         switch (ul_state)
  1656.         {
  1657.         case UL_NORMAL:
  1658.             if (curr <= linebuf + 1 || curr[-1] != '\b')
  1659.                 break;
  1660.             if (c != '_' && curr[-2] != '_')
  1661.             {
  1662.                 curr -= 2;
  1663.                 break;
  1664.             }
  1665.  
  1666.             /*
  1667.              * We have either "_\bX" or "X\b_" (including
  1668.              * the current char).  Switch into underline mode.
  1669.              */
  1670.             if (column + ul_width + ue_width + 1 >= sc_width)
  1671.                 /*
  1672.                  * Not enough room left on the screen to 
  1673.                  * enter and exit underline mode.
  1674.                  */
  1675.                 return (1);
  1676.  
  1677.             if (ul_width > 0 && 
  1678.                 curr > linebuf + 2 && curr[-3] == ' ')
  1679.             {
  1680.                 /*
  1681.                  * Special case for magic cookie terminals:
  1682.                  * if the previous char was a space, replace 
  1683.                  * it with the "enter underline" sequence.
  1684.                  */
  1685.                 curr[-3] = UL_CHAR;
  1686.                 column += ul_width-1;
  1687.             } else
  1688.             {
  1689.                 curr[-1] = curr[-2];
  1690.                 curr[-2] = UL_CHAR;
  1691.                 column += ul_width;
  1692.                 curr++;
  1693.             }
  1694.             /* Fall thru */
  1695.         case UL_XB:
  1696.             /*
  1697.              * Termination of a sequnce "_\bX" or "X\b_".
  1698.              */
  1699.             if (c == '_')
  1700.                 c = curr[-2];
  1701.             curr -= 2;
  1702.             ul_state = UL_YES;
  1703.             break;
  1704.         case UL_YES:
  1705.             if (column + ue_width + 1 >= sc_width)
  1706.                 /*
  1707.                  * We have just barely enough room to 
  1708.                  * exit underline mode.  
  1709.                  */
  1710.                 return (1);
  1711.             ul_state = UL_X;
  1712.             break;
  1713.         case UL_X:
  1714.             if (c == '\b')
  1715.                 ul_state = UL_XB;
  1716.             else
  1717.             {
  1718.                 /*
  1719.                  * Exit underline mode.
  1720.                  * We have to shuffle the chars a bit
  1721.                  * to make this work.
  1722.                  */
  1723.                 curr[0] = curr[-1];
  1724.                 curr[-1] = UE_CHAR;
  1725.                 column += ue_width;
  1726.                 if (ul_width > 0 && curr[0] == ' ')
  1727.                     /*
  1728.                      * Another special case for magic
  1729.                      * cookie terminals: if the next
  1730.                      * char is a space, replace it
  1731.                      * with the "exit underline" sequence.
  1732.                      */
  1733.                     column--;
  1734.                 else
  1735.                     curr++;
  1736.                 ul_state = UL_NORMAL;
  1737.             } 
  1738.             break;
  1739.         }
  1740.     }
  1741.     
  1742.     if (c == '\t') 
  1743.     {
  1744.         /*
  1745.          * Expand a tab into spaces.
  1746.          */
  1747.         do
  1748.         {
  1749.             NEW_COLUMN(column+1);
  1750.         } while ((column % tabstop) != 0);
  1751.         *curr++ = '\t';
  1752.         return (0);
  1753.     }
  1754.  
  1755.     if (c == '\b')
  1756.     {
  1757.         if (bs_mode == BS_CONTROL)
  1758.         {
  1759.             /*
  1760.              * Treat backspace as a control char: output "^H".
  1761.              */
  1762.             NEW_COLUMN(column+2);
  1763.             *curr++ = ('H' | 0200);
  1764.         } else
  1765.         {
  1766.             /*
  1767.              * Output a real backspace.
  1768.              */
  1769.             column--;
  1770.             *curr++ = '\b';
  1771.         }
  1772.         return (0);
  1773.     } 
  1774.  
  1775.     if (control_char(c))
  1776.     {
  1777.         /*
  1778.          * Put a "^X" into the buffer.
  1779.          * The 0200 bit is used to tell put_line() to prefix
  1780.          * the char with a ^.  We don't actually put the ^
  1781.          * in the buffer because we sometimes need to move
  1782.          * chars around, and such movement might separate 
  1783.          * the ^ from its following character.
  1784.          */
  1785.         NEW_COLUMN(column+2);
  1786.         *curr++ = (carat_char(c) | 0200);
  1787.         return (0);
  1788.     }
  1789.  
  1790.     /*
  1791.      * Ordinary character.  Just put it in the buffer.
  1792.      */
  1793.     NEW_COLUMN(column+1);
  1794.     *curr++ = c;
  1795.     return (0);
  1796. }
  1797.  
  1798. /*
  1799.  * Analogous to forw_line(), but deals with "raw lines":
  1800.  * lines which are not split for screen width.
  1801.  * {{ This is supposed to be more efficient than forw_line(). }}
  1802.  */
  1803.     public POSITION
  1804. forw_raw_line(curr_pos)
  1805.     POSITION curr_pos;
  1806. {
  1807.     register char *p;
  1808.     register int c;
  1809.     POSITION new_pos;
  1810.  
  1811.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  1812.         (c = ch_forw_get()) == EOF)
  1813.         return (NULL_POSITION);
  1814.  
  1815.     p = linebuf;
  1816.  
  1817.     for (;;)
  1818.     {
  1819.         if (c == '\n' || c == EOF)
  1820.         {
  1821.             new_pos = ch_tell();
  1822.             break;
  1823.         }
  1824.         if (p >= &linebuf[sizeof(linebuf)-1])
  1825.         {
  1826.             /*
  1827.              * Overflowed the input buffer.
  1828.              * Pretend the line ended here.
  1829.              * {{ The line buffer is supposed to be big
  1830.              *    enough that this never happens. }}
  1831.              */
  1832.             new_pos = ch_tell() - 1;
  1833.             break;
  1834.         }
  1835.         *p++ = c;
  1836.         c = ch_forw_get();
  1837.     }
  1838.     *p = '\0';
  1839.     line = linebuf;
  1840.     return (new_pos);
  1841. }
  1842.  
  1843. /*
  1844.  * Analogous to back_line(), but deals with "raw lines".
  1845.  * {{ This is supposed to be more efficient than back_line(). }}
  1846.  */
  1847.     public POSITION
  1848. back_raw_line(curr_pos)
  1849.     POSITION curr_pos;
  1850. {
  1851.     register char *p;
  1852.     register int c;
  1853.     POSITION new_pos;
  1854.  
  1855.     if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
  1856.         ch_seek(curr_pos-1))
  1857.         return (NULL_POSITION);
  1858.  
  1859.     p = &linebuf[sizeof(linebuf)];
  1860.     *--p = '\0';
  1861.  
  1862.     for (;;)
  1863.     {
  1864.         c = ch_back_get();
  1865.         if (c == '\n')
  1866.         {
  1867.             /*
  1868.              * This is the newline ending the previous line.
  1869.              * We have hit the beginning of the line.
  1870.              */
  1871.             new_pos = ch_tell() + 1;
  1872.             break;
  1873.         }
  1874.         if (c == EOF)
  1875.         {
  1876.             /*
  1877.              * We have hit the beginning of the file.
  1878.              * This must be the first line in the file.
  1879.              * This must, of course, be the beginning of the line.
  1880.              */
  1881.             new_pos = (POSITION)0;
  1882.             break;
  1883.         }
  1884.         if (p <= linebuf)
  1885.         {
  1886.             /*
  1887.              * Overflowed the input buffer.
  1888.              * Pretend the line ended here.
  1889.              */
  1890.             new_pos = ch_tell() + 1;
  1891.             break;
  1892.         }
  1893.         *--p = c;
  1894.     }
  1895.     line = p;
  1896.     return (new_pos);
  1897. }
  1898. SHAR_EOF
  1899. echo shar: extracting signal.c
  1900. cat - << \SHAR_EOF > signal.c
  1901. /*
  1902.  * Routines dealing with signals.
  1903.  *
  1904.  * A signal usually merely causes a bit to be set in the "signals" word.
  1905.  * At some convenient time, the mainline code checks to see if any
  1906.  * signals need processing by calling psignal().
  1907.  * An exception is made if we are reading from the keyboard when the
  1908.  * signal is received.  Some operating systems will simply call the
  1909.  * signal handler and NOT return from the read (with EINTR).
  1910.  * To handle this case, we service the interrupt directly from
  1911.  * the handler if we are reading from the keyboard.
  1912.  */
  1913.  
  1914. #include "less.h"
  1915. #include <signal.h>
  1916. #include <setjmp.h>
  1917.  
  1918. /*
  1919.  * The type of signal handler functions.
  1920.  * Usually int, although it should be void.
  1921.  */
  1922. typedef    int        HANDLER;
  1923.  
  1924. /*
  1925.  * "sigs" contains bits indicating signals which need to be processed.
  1926.  */
  1927. public int sigs;
  1928. #define    S_INTERRUPT    01
  1929. #ifdef SIGTSTP
  1930. #define    S_STOP        02
  1931. #endif
  1932.  
  1933. extern int reading;
  1934. extern char *first_cmd;
  1935. extern jmp_buf main_loop;
  1936.  
  1937. /*
  1938.  * Interrupt signal handler.
  1939.  */
  1940.     static HANDLER
  1941. interrupt()
  1942. {
  1943.     SIGNAL(SIGINT, interrupt);
  1944.     sigs |= S_INTERRUPT;
  1945.     if (reading)
  1946.         psignals();
  1947. }
  1948.  
  1949. #ifdef SIGTSTP
  1950. /*
  1951.  * "Stop" (^Z) signal handler.
  1952.  */
  1953.     static HANDLER
  1954. stop()
  1955. {
  1956.     SIGNAL(SIGTSTP, stop);
  1957.     sigs |= S_STOP;
  1958.     if (reading)
  1959.         psignals();
  1960. }
  1961. #endif
  1962.  
  1963. /*
  1964.  * Set up the signal handlers.
  1965.  */
  1966.     public void
  1967. init_signals()
  1968. {
  1969.     (void) SIGNAL(SIGINT, interrupt);
  1970. #ifdef SIGTSTP
  1971.     (void) SIGNAL(SIGTSTP, stop);
  1972. #endif
  1973. }
  1974.  
  1975. /*
  1976.  * Process any signals we have recieved.
  1977.  * A received signal cause a bit to be set in "sigs".
  1978.  */
  1979.     public void 
  1980. psignals()
  1981. {
  1982.     register int tsignals;
  1983.  
  1984.     tsignals = sigs;
  1985.     sigs = 0;
  1986.     if (tsignals == 0)
  1987.         return;
  1988.  
  1989.     dropout();        /* Discard any buffered output */
  1990.  
  1991. #ifdef SIGTSTP
  1992.     if (tsignals & S_STOP)
  1993.     {
  1994.         /*
  1995.          * Clean up the terminal.
  1996.          */
  1997. #ifdef SIGTTOU
  1998.         SIGNAL(SIGTTOU, SIG_IGN);
  1999. #endif
  2000.         lower_left();
  2001.         clear_eol();
  2002.         flush();
  2003.         raw_mode(0);
  2004. #ifdef SIGTTOU
  2005.         SIGNAL(SIGTTOU, SIG_DFL);
  2006. #endif
  2007.         SIGNAL(SIGTSTP, SIG_DFL);
  2008. #if SIGSETMASK
  2009.         /*
  2010.          * This system will not allow us to send a 
  2011.          * stop signal (SIGTSTP) to ourself
  2012.          * while we are in the signal handler, like maybe now.
  2013.          * (This can be the case if we are reading; see comment above.)
  2014.          * So we ask the silly system for permission to do so.
  2015.          */
  2016.         sigsetmask(0);
  2017. #endif
  2018.         kill(getpid(), SIGTSTP);
  2019.         /*
  2020.          * ... Bye bye. ...
  2021.          * Hopefully we'll be back later and resume here...
  2022.          * Reset the terminal and arrange to repaint the
  2023.          * screen when we get back to the main command loop.
  2024.          */
  2025.         SIGNAL(SIGTSTP, stop);
  2026.         raw_mode(1);
  2027.         first_cmd = "r";
  2028.         longjmp(main_loop, 1);
  2029.     }
  2030. #endif
  2031.     if (tsignals & S_INTERRUPT)
  2032.     {
  2033.         bell();
  2034.         /*
  2035.          * {{ You may wish to replace the bell() with 
  2036.          *    error("Interrupt"); }}
  2037.          */
  2038.     }
  2039.  
  2040.     longjmp(main_loop, 1);
  2041. }
  2042.  
  2043. /*
  2044.  * Pass the specified command to a shell to be executed.
  2045.  * Like plain "system()", but handles resetting terminal modes, etc.
  2046.  */
  2047.     public void
  2048. lsystem(cmd)
  2049.     char *cmd;
  2050. {
  2051.     int inp;
  2052.  
  2053.     /*
  2054.      * Print the command which is to be executed.
  2055.      */
  2056.     lower_left();
  2057.     clear_eol();
  2058.     puts("!");
  2059.     puts(cmd);
  2060.     puts("\n");
  2061.  
  2062.     /*
  2063.      * De-initialize the terminal and take out of raw mode.
  2064.      */
  2065.     deinit();
  2066.     flush();
  2067.     raw_mode(0);
  2068.  
  2069.     /*
  2070.      * Restore signals to their defaults.
  2071.      */
  2072.     SIGNAL(SIGINT, SIG_DFL);
  2073. #ifdef SIGTSTP
  2074.     SIGNAL(SIGTSTP, SIG_DFL);
  2075. #endif
  2076.     /*
  2077.      * Pass the command to the system to be executed.
  2078.      */
  2079.     inp = dup(0);
  2080.     close(0);
  2081.     open("/dev/tty", 0);
  2082.  
  2083.     system(cmd);
  2084.  
  2085.     close(0);
  2086.     dup(inp);
  2087.     close(inp);
  2088.  
  2089.     /*
  2090.      * Reset signals, raw mode, etc.
  2091.      */
  2092.     init_signals();
  2093.     raw_mode(1);
  2094.     init();
  2095. }
  2096. SHAR_EOF
  2097. echo shar: extracting help.c
  2098. cat - << \SHAR_EOF > help.c
  2099. #include  "less.h"
  2100.  
  2101. /*
  2102.  * Display some help.
  2103.  * Help is in two pages.
  2104.  */
  2105.     static void
  2106. help0()
  2107. {
  2108.     puts("f, SPACE       Forward one screen.\n");
  2109.     puts("b              Backward one screen.\n");
  2110.     puts("e, j, CR    *  Forward N lines, default 1.\n");
  2111.     puts("y, k        *  Backward N lines, default 1.\n");
  2112.     puts("d           *  Forward N lines, default 10 or last N to d or u command.\n");
  2113.     puts("u           *  Backward N lines, default 10 or last N to d or u command.\n");
  2114.     puts("r              Repaint screen.\n");
  2115.     puts("g           *  Go to line N, default 1.\n");
  2116.     puts("G           *  Like g, but default is last line in file.\n");
  2117.     puts("=              Print current file name\n");
  2118.     puts("/pattern    *  Search forward for N-th occurence of pattern.\n");
  2119.     puts("?pattern    *  Search backward for N-th occurence of pattern.\n");
  2120.     puts("n           *  Repeat previous search (for N-th occurence).\n");
  2121.     puts("q              Exit.\n");
  2122.     error("More help...");
  2123. }
  2124.  
  2125.     static void
  2126. help1()
  2127. {
  2128.     char message[100];
  2129.     extern char all_options[];
  2130.  
  2131.     puts("R              Repaint screen, discarding buffered input.\n");
  2132.     puts("p, %        *  Position to N percent into the file.\n");
  2133.     puts("m<letter>      Mark the current position with <letter>.\n");
  2134.     puts("'<letter>      Return to a previously marked position.\n");
  2135.     sprintf(message, 
  2136.          "-X             Toggle a flag (X may be one of \"%s\").\n", 
  2137.                 all_options);
  2138.     puts(message);
  2139.     puts("E [file]       Examine a new file.\n");
  2140.     puts("N              Examine the next file (from the command line).\n");
  2141.     puts("P              Examine the previous file (from the command line).\n");
  2142.     puts("V              Print version number.\n");
  2143. #if SHELL_ESCAPE
  2144.     puts("!command       Passes the command to a shell to be executed.\n");
  2145. #endif
  2146. #if EDITOR
  2147.     sprintf(message,
  2148.          "v              Edit the current file with $EDITOR (default %s).\n",
  2149.                 EDIT_PGM);
  2150.     puts(message);
  2151. #endif
  2152.     error("");
  2153. }
  2154.  
  2155.     public void
  2156. help()
  2157. {
  2158.     register int i;
  2159.  
  2160.     for (i = 0;  i < 2;  i++)
  2161.     {
  2162.         clear();
  2163.         puts("Commands marked with * may be preceeded by a number, N.\n\n");
  2164.  
  2165.         switch (i)
  2166.         {
  2167.         case 0:        help0();    break;
  2168.         case 1:        help1();    break;
  2169.         }
  2170.     }
  2171. }
  2172. SHAR_EOF
  2173. echo shar: extracting ttyin.c
  2174. cat - << \SHAR_EOF > ttyin.c
  2175. /*
  2176.  * Routines dealing with getting input from the keyboard (i.e. from the user).
  2177.  */
  2178.  
  2179. #include "less.h"
  2180.  
  2181. /*
  2182.  * The boolean "reading" is set true or false according to whether
  2183.  * we are currently reading from the keyboard.
  2184.  * This information is used by the signal handling stuff in signal.c.
  2185.  * {{ There are probably some race conditions here
  2186.  *    involving the variable "reading". }}
  2187.  */
  2188. public int reading;
  2189.  
  2190. static int tty;
  2191.  
  2192. /*
  2193.  * Open keyboard for input.
  2194.  * (Just use file descriptor 2.)
  2195.  */
  2196.     public void
  2197. open_getc()
  2198. {
  2199.     tty = 2;
  2200. }
  2201.  
  2202. /*
  2203.  * Get a character from the keyboard.
  2204.  */
  2205.     public int
  2206. getc()
  2207. {
  2208.     char c;
  2209.     int result;
  2210.  
  2211.     reading = 1;
  2212.     do
  2213.     {
  2214.         flush();
  2215.         result = read(tty, &c, 1);
  2216.     } while (result != 1);
  2217.     reading = 0;
  2218.     return (c & 0177);
  2219. }
  2220. SHAR_EOF
  2221. echo shar: extracting command.c
  2222. cat - << \SHAR_EOF > command.c
  2223. /*
  2224.  * User-level command processor.
  2225.  */
  2226.  
  2227. #include "less.h"
  2228. #include "position.h"
  2229. #include <setjmp.h>
  2230.  
  2231. extern jmp_buf main_loop;
  2232. extern int erase_char, kill_char;
  2233. extern int pr_type;
  2234. extern int sigs;
  2235. extern int ispipe;
  2236. extern int quit_at_eof;
  2237. extern int hit_eof;
  2238. extern int sc_width, sc_height;
  2239. extern char *first_cmd;
  2240. extern char version[];
  2241. extern char current_file[];
  2242. extern char *editor;
  2243.  
  2244. static char cmdbuf[90];        /* Buffer for holding a multi-char command */
  2245. static char *cp;        /* Pointer into cmdbuf */
  2246. static int cmd_col;        /* Current column of the multi-char command */
  2247. static char mcc;        /* The multi-char command letter (e.g. '/') */
  2248. static char last_mcc;        /* The previous mcc */
  2249.  
  2250. /*
  2251.  * Reset command buffer (to empty).
  2252.  */
  2253. cmd_reset()
  2254. {
  2255.     cp = cmdbuf;
  2256. }
  2257.  
  2258. /*
  2259.  * Backspace in command buffer.
  2260.  */
  2261.     static int
  2262. cmd_erase()
  2263. {
  2264.     if (cp == cmdbuf)
  2265.         /*
  2266.          * Backspace past beginning of the string:
  2267.          * this usually means abort the command.
  2268.          */
  2269.         return (1);
  2270.  
  2271.     if (control_char(*--cp))
  2272.     {
  2273.         /*
  2274.          * Erase an extra character, for the carat.
  2275.          */
  2276.         backspace();
  2277.         cmd_col--;
  2278.     }
  2279.     backspace();
  2280.     cmd_col--;
  2281.     return (0);
  2282. }
  2283.  
  2284. /*
  2285.  * Set up the display to start a new multi-character command.
  2286.  */
  2287. start_mcc()
  2288. {
  2289.     lower_left();
  2290.     clear_eol();
  2291.     putc(mcc);
  2292.     cmd_col = 1;
  2293. }
  2294.  
  2295. /*
  2296.  * Process a single character of a multi-character command, such as
  2297.  * a number, or the pattern of a search command.
  2298.  */
  2299.     static int
  2300. cmd_char(c)
  2301.     int c;
  2302. {
  2303.     if (c == erase_char)
  2304.     {
  2305.         if (cmd_erase())
  2306.             return (1);
  2307.     } else if (c == kill_char)
  2308.     {
  2309.         /* {{ Could do this faster, but who cares? }} */
  2310.         while (cmd_erase() == 0)
  2311.             ;
  2312.     } else
  2313.     {
  2314.         /*
  2315.          * Append the character to the string,
  2316.          * if there is room in the buffer and on the screen.
  2317.          */
  2318.         if (cp < &cmdbuf[sizeof(cmdbuf)-1] && cmd_col < sc_width-3)
  2319.         {
  2320.             *cp++ = c;
  2321.             if (control_char(c))
  2322.             {
  2323.                 putc('^');
  2324.                 cmd_col++;
  2325.                 c = carat_char(c);
  2326.             }
  2327.             putc(c);
  2328.             cmd_col++;
  2329.         } else
  2330.             bell();
  2331.     }
  2332.     return (0);
  2333. }
  2334.  
  2335. /*
  2336.  * Return the number currently in the command buffer.
  2337.  */
  2338.     static int
  2339. cmd_int()
  2340. {
  2341.     *cp = '\0';
  2342.     cp = cmdbuf;
  2343.     return (atoi(cmdbuf));
  2344. }
  2345.  
  2346. /*
  2347.  * Move the cursor to lower left before executing a command.
  2348.  * This looks nicer if the command takes a long time before
  2349.  * updating the screen.
  2350.  */
  2351.     static void
  2352. cmd_exec()
  2353. {
  2354.     lower_left();
  2355.     flush();
  2356. }
  2357.  
  2358. /*
  2359.  * Display the appropriate prompt.
  2360.  */
  2361.     static void
  2362. prompt()
  2363. {
  2364.     register char *p;
  2365.  
  2366.     if (first_cmd != NULL && *first_cmd != '\0')
  2367.         /*
  2368.          * No prompt necessary if commands are from first_cmd
  2369.          * rather than from the user.
  2370.          */
  2371.         return;
  2372.  
  2373.     /*
  2374.      * Select the proper prompt and display it.
  2375.      */
  2376.     p = pr_string();
  2377.     if (p == NULL)
  2378.         putc(':');
  2379.     else
  2380.     {
  2381.         so_enter();
  2382.         puts(p);
  2383.         so_exit();
  2384.     }
  2385. }
  2386.  
  2387. /*
  2388.  * Get command character.
  2389.  * The character normally comes from the keyboard,
  2390.  * but may come from the "first_cmd" string.
  2391.  */
  2392.     static int
  2393. getcc()
  2394. {
  2395.     if (first_cmd == NULL)
  2396.         return (getc());
  2397.  
  2398.     if (*first_cmd == '\0')
  2399.     {
  2400.         /*
  2401.          * Reached end of first_cmd input.
  2402.          */
  2403.         first_cmd = NULL;
  2404.         if (cp > cmdbuf && position(TOP) == NULL_POSITION)
  2405.         {
  2406.             /*
  2407.              * Command is incomplete, so try to complete it.
  2408.              * There are only two cases:
  2409.              * 1. We have "/string" but no newline.  Add the \n.
  2410.              * 2. We have a number but no command.  Treat as #g.
  2411.              * (This is all pretty hokey.)
  2412.              */
  2413.             if (mcc != ':')
  2414.                 return ('\n'); 
  2415.             else
  2416.                 return ('g');
  2417.         }
  2418.         return (getc());
  2419.     }
  2420.     return (*first_cmd++);
  2421. }
  2422.  
  2423. /*
  2424.  * Main command processor.
  2425.  * Accept and execute commands until a quit command, then return.
  2426.  */
  2427.     public void
  2428. commands()
  2429. {
  2430.     register int c;
  2431.     register int n;
  2432.     register int scroll = 10;
  2433.  
  2434.     mcc = last_mcc = 0;
  2435.  
  2436.     setjmp(main_loop);
  2437.     for (;;)
  2438.     {
  2439.         /*
  2440.          * Display prompt and accept a character.
  2441.          */
  2442.         psignals();    /* See if any signals need processing */
  2443.  
  2444.         if (quit_at_eof && hit_eof > 1)
  2445.             /*
  2446.              * After hitting end-of-file for the second time,
  2447.              * automatically advance to the next file.
  2448.              * If there are no more files, quit.
  2449.              */
  2450.             next_file(1);
  2451.  
  2452.         cmd_reset();
  2453.         lower_left();
  2454.         clear_eol();
  2455.         prompt();
  2456.         c = getcc();
  2457.  
  2458.     again:
  2459.         if (sigs)
  2460.             continue;
  2461.  
  2462.         if (mcc)
  2463.         {
  2464.             /*
  2465.              * We are in a multi-character command.  
  2466.              * All chars until newline go into the command buffer.
  2467.              * (Note that mcc == ':' is a special case that
  2468.              *  means a number is being entered.)
  2469.              */
  2470.             if (mcc != ':' && (c == '\n' || c == '\r'))
  2471.             {
  2472.                 /*
  2473.                  * Execute the command.
  2474.                  */
  2475.                 *cp = '\0';
  2476.                 cmd_exec();
  2477.                 if (mcc == 'E')
  2478.                 {
  2479.                     char *p;
  2480.                     /*
  2481.                      * Ignore leading spaces 
  2482.                      * in the filename.
  2483.                      */
  2484.                     for (p = cmdbuf;  *p == ' ';  p++) ;
  2485.                     edit(p);
  2486. #if SHELL_ESCAPE
  2487.                 } else if (mcc == '!')
  2488.                 {
  2489.                     lsystem(cmdbuf);
  2490.                     error("!done");
  2491.                     first_cmd = "r";    /* Repaint */
  2492. #endif
  2493.                 } else
  2494.                     search(mcc, cmdbuf, n);
  2495.                 mcc = 0;
  2496.             } else
  2497.             {
  2498.                 if (mcc == ':' && (c < '0' || c > '9') &&
  2499.                     c != erase_char && c != kill_char)
  2500.                 {
  2501.                     /*
  2502.                      * This is not part of the number
  2503.                      * we were entering.  Process
  2504.                      * it as a regular character.
  2505.                      */
  2506.                     mcc = 0;
  2507.                     goto again;
  2508.                 }
  2509.  
  2510.                 /*
  2511.                  * Append the char to the command buffer.
  2512.                  */
  2513.                 if (cmd_char(c))
  2514.                 {
  2515.                     /* Abort the multi-char command. */
  2516.                     mcc = 0;
  2517.                     continue;
  2518.                 }
  2519.                 c = getcc();
  2520.                 goto again;
  2521.             }
  2522.         } else switch (c)
  2523.         {
  2524.         case '0': case '1': case '2': case '3': case '4':
  2525.         case '5': case '6': case '7': case '8': case '9':
  2526.             /*
  2527.              * First digit of a number.
  2528.              */
  2529.             mcc = ':';
  2530.             start_mcc();
  2531.             goto again;
  2532.  
  2533.         case 'f':
  2534.         case ' ':
  2535.         case CONTROL('F'):
  2536.             /*
  2537.              * Forward one screen.
  2538.              */
  2539.             n = cmd_int();
  2540.             if (n <= 0)
  2541.                 n = sc_height - 1;
  2542.             forward(n, 1);
  2543.             break;
  2544.  
  2545.         case 'b':
  2546.         case CONTROL('B'):
  2547.             /*
  2548.              * Backward one screen.
  2549.              */
  2550.             n = cmd_int();
  2551.             if (n <= 0)
  2552.                 n = sc_height - 1;
  2553.             backward(n, 1);
  2554.             break;
  2555.  
  2556.         case 'e':
  2557.         case 'j':
  2558.         case '\r':
  2559.         case '\n':
  2560.         case CONTROL('E'):
  2561.             /*
  2562.              * Forward N (default 1) line.
  2563.              */
  2564.             n = cmd_int();
  2565.             if (n <= 0)
  2566.                 n = 1;
  2567.             forward(n, 0);
  2568.             break;
  2569.  
  2570.         case 'y':
  2571.         case 'k':
  2572.         case CONTROL('K'):
  2573.         case CONTROL('Y'):
  2574.             /*
  2575.              * Backward N (default 1) line.
  2576.              */
  2577.             n = cmd_int();
  2578.             if (n <= 0)
  2579.                 n = 1;
  2580.             backward(n, 0);
  2581.             break;
  2582.  
  2583.         case 'd':
  2584.         case CONTROL('D'):
  2585.             /*
  2586.              * Forward N lines 
  2587.              * (default same as last 'd' or 'u' command).
  2588.              */
  2589.             n = cmd_int();
  2590.             if (n > 0)
  2591.                 scroll = n;
  2592.             forward(scroll, 0);
  2593.             break;
  2594.  
  2595.         case 'u':
  2596.         case CONTROL('U'):
  2597.             /*
  2598.              * Forward N lines 
  2599.              * (default same as last 'd' or 'u' command).
  2600.              */
  2601.             n = cmd_int();
  2602.             if (n > 0)
  2603.                 scroll = n;
  2604.             backward(scroll, 0);
  2605.             break;
  2606.  
  2607.         case 'R':
  2608.             /*
  2609.              * Flush buffers, then repaint screen.
  2610.              */
  2611.             ch_init(0);
  2612.             /* Fall thru */
  2613.         case 'r':
  2614.         case CONTROL('R'):
  2615.         case CONTROL('L'):
  2616.             /*
  2617.              * Repaint screen.
  2618.              */
  2619.             repaint();
  2620.             break;
  2621.  
  2622.         case 'g':
  2623.             /*
  2624.              * Go to line N, default beginning of file.
  2625.              */
  2626.             n = cmd_int();
  2627.             if (n <= 0)
  2628.                 n = 1;
  2629.             cmd_exec();
  2630.             jump_back(n);
  2631.             break;
  2632.  
  2633.         case 'p':
  2634.         case '%':
  2635.             /*
  2636.              * Go to a specified percentage into the file.
  2637.              */
  2638.             n = cmd_int();
  2639.             if (n < 0)
  2640.                 n = 0;
  2641.             if (n > 100)
  2642.                 n = 100;
  2643.             cmd_exec();
  2644.             jump_percent(n);
  2645.             break;
  2646.  
  2647.         case 'G':
  2648.             /*
  2649.              * Go to line N, default end of file.
  2650.              */
  2651.             n = cmd_int();
  2652.             cmd_exec();
  2653.             if (n <= 0)
  2654.                 jump_forw();
  2655.             else
  2656.                 jump_back(n);
  2657.             break;
  2658.  
  2659.         case '=':
  2660.         case CONTROL('G'):
  2661.             /*
  2662.              * Print file name, etc.
  2663.              */
  2664.             error(eq_message());
  2665.             break;
  2666.             
  2667.         case 'V':
  2668.             /*
  2669.              * Print version number, without the "@(#)".
  2670.              */
  2671.             error(version+4);
  2672.             break;
  2673.  
  2674.         case 'q':
  2675.             /*
  2676.              * Exit.
  2677.              */
  2678.             return;
  2679.  
  2680.         case '/':
  2681.         case '?':
  2682.             /*
  2683.              * Search for a pattern.
  2684.              * Accept chars of the pattern until \n.
  2685.              */
  2686.             n = cmd_int();
  2687.             if (n <= 0)
  2688.                 n = 1;
  2689.             mcc = last_mcc = c;
  2690.             start_mcc();
  2691.             c = getcc();
  2692.             goto again;
  2693.  
  2694.         case 'n':
  2695.             /*
  2696.              * Repeat previous search.
  2697.              */
  2698.             n = cmd_int();
  2699.             if (n <= 0)
  2700.                 n = 1;
  2701.             mcc = last_mcc;
  2702.             start_mcc();
  2703.             cmd_exec();
  2704.             search(mcc, (char *)NULL, n);
  2705.             mcc = 0;
  2706.             break;
  2707.  
  2708.         case 'h':
  2709.             /*
  2710.              * Help.
  2711.              */
  2712.             help();
  2713.             repaint();
  2714.             break;
  2715.  
  2716.         case 'E':
  2717.             /*
  2718.              * Edit a new file.  Get the filename.
  2719.              */
  2720.             cmd_reset();
  2721.             mcc = 'E';
  2722.             start_mcc();
  2723.             puts("dit: ");    /* This looks nicer */
  2724.             cmd_col += 5;
  2725.             c = getcc();
  2726.             goto again;
  2727.             
  2728. #if SHELL_ESCAPE
  2729.         case '!':
  2730.             /*
  2731.              * Shell escape.
  2732.              */
  2733.             cmd_reset();
  2734.             mcc = '!';
  2735.             start_mcc();
  2736.             c = getcc();
  2737.             goto again;
  2738. #endif
  2739.  
  2740. #if EDITOR
  2741.         case 'v':
  2742.             if (ispipe)
  2743.             {
  2744.                 error("Cannot edit standard input");
  2745.                 break;
  2746.             }
  2747.             sprintf(cmdbuf, "%s %s", editor, current_file);
  2748.             lsystem(cmdbuf);
  2749.             first_cmd = "R";
  2750.             break;
  2751. #endif
  2752.  
  2753.         case 'N':
  2754.             /*
  2755.              * Examine next file.
  2756.              */
  2757.             n = cmd_int();
  2758.             if (n <= 0)
  2759.                 n = 1;
  2760.             next_file(n);
  2761.             break;
  2762.  
  2763.         case 'P':
  2764.             /*
  2765.              * Examine previous file.
  2766.              */
  2767.             n = cmd_int();
  2768.             if (n <= 0)
  2769.                 n = 1;
  2770.             prev_file(n);
  2771.             break;
  2772.  
  2773.         case '-':
  2774.             /*
  2775.              * Toggle a flag setting.
  2776.              */
  2777.             mcc = '-';
  2778.             start_mcc();
  2779.             c = getcc();
  2780.             mcc = 0;
  2781.             if (c == erase_char || c == kill_char)
  2782.                 break;
  2783.             toggle_option(c);
  2784.             break;
  2785.  
  2786.         case 'm':
  2787.             /*
  2788.              * Set a mark.
  2789.              */
  2790.             lower_left();
  2791.             clear_eol();
  2792.             puts("mark: ");
  2793.             c = getcc();
  2794.             if (c == erase_char || c == kill_char)
  2795.                 break;
  2796.             setmark(c);
  2797.             break;
  2798.  
  2799.         case '\'':
  2800.             /*
  2801.              * Go to a mark.
  2802.              */
  2803.             lower_left();
  2804.             clear_eol();
  2805.             puts("goto mark: ");
  2806.             c = getcc();
  2807.             if (c == erase_char || c == kill_char)
  2808.                 break;
  2809.             gomark(c);
  2810.             break;
  2811.  
  2812.         default:
  2813.             bell();
  2814.             break;
  2815.         }
  2816.     }
  2817. }
  2818. SHAR_EOF
  2819. echo shar: extracting version.c
  2820. cat - << \SHAR_EOF > version.c
  2821. /*
  2822.  *        less
  2823.  *    Copyright (c) 1984,1985  Mark Nudelman
  2824.  *
  2825.  *    This program may be freely used and/or modified, 
  2826.  *    with the following provisions:
  2827.  *    1. This notice and the above copyright notice must remain intact.
  2828.  *    2. Neither this program, nor any modification of it,
  2829.  *       may not be sold for profit without written consent of the author.
  2830.  *
  2831.  *    -----------------------------------------------------------------
  2832.  *
  2833.  *    This program is a paginator similar to "more", 
  2834.  *    but allows you to move both forward and backward in the file.  
  2835.  *    Commands are based on "more" and "vi".
  2836.  *
  2837.  *    ----------------------- CHANGES ---------------------------------
  2838.  *
  2839.  *        Allowed use on standard input        1/29/84   markn
  2840.  *        Added E, N, P commands            2/1/84    markn
  2841.  *        Added '=' command, 'stop' signal handling    4/17/84   markn
  2842.  *        Added line folding                4/20/84   markn
  2843.  *    v2: Fixed '=' command to use BOTTOM_PLUS_ONE, 
  2844.  *        instead of TOP, added 'p' & 'v' commands    4/27/84   markn
  2845.  *    v3: Added -m and -t options, '-' command    5/3/84    markn
  2846.  *    v4: Added LESS environment variable        5/3/84    markn
  2847.  *    v5: New comments, fixed '-' command slightly    5/3/84    markn
  2848.  *    v6: Added -Q, visual bell            5/15/84   markn
  2849.  *    v7: Fixed jump_back(n) bug: n should count real
  2850.  *        lines, not folded lines.  Also allow number
  2851.  *        on G command.                5/24/84   markn
  2852.  *    v8: Re-do -q and -Q commands            5/30/84   markn
  2853.  *    v9: Added "+<cmd>" argument            9/25/84   markn
  2854.  *    v10: Fixed bug in -b<n> argument processing    10/10/84  markn
  2855.  *    v11: Made error() ring bell if \n not entered.    10/18/84  markn
  2856.  *    -----------------------------------------------------------------
  2857.  *    v12: Reorganized signal handling and made
  2858.  *         portable to 4.2bsd.            2/13/85   mark
  2859.  *    v13: Reword error message for '-' command.    2/16/85   mark
  2860.  *    v14: Added -bf and -bp variants of -b.        2/22/85   mark
  2861.  *    v15: Miscellaneous changes.            2/25/85   mark
  2862.  *    v16: Added -u flag for backspace processing.    3/13/85   mark
  2863.  *    v17: Added j and k commands, 
  2864.  *        changed -t default.            4/13/85   mark
  2865.  *    v18: Rewrote signal handling code.        4/20/85   mark
  2866.  *    v19: Got rid of "verbose" eq_message().        5/2/85    mark
  2867.  *         Made search() scroll in some cases.
  2868.  *    v20: Fixed screen.c ioctls for System V.    5/21/85   mark
  2869.  *    v21: Fixed some first_cmd bugs.            5/23/85   mark
  2870.  *    v22: Added support for no RECOMP nor REGCMP.    5/24/85   mark
  2871.  *    v23: Miscellanous changes and prettying up.    5/25/85   mark
  2872.  *      v24: Added ti,te terminal init & de-init       6/3/85 Mike Kersenbrock
  2873.  *    v25: Added -U flag, standout mode underlining.    6/8/85    mark
  2874.  *    v26: Added -M flag.                6/9/85    mark
  2875.  *         Use underline termcap (us) if it exists.
  2876.  *    v27: Renamed some variables to make unique in    6/15/85   mark
  2877.  *         6 chars.  Minor fix to -m.
  2878.  *    v28: Fixed right margin bug.            6/28/85   mark
  2879.  *    v29: Incorporated M.Rose's changes to signal.c    6/28/85   mark
  2880.  *    v30: Fixed stupid bug in argument processing.    6/29/85   mark
  2881.  *    v31: Added -p flag, changed repaint algorithm.  7/15/85   mark
  2882.  *         Added kludge for magic cookie terminals.
  2883.  *    v32: Added cat_file if output not a tty.    7/16/85   mark
  2884.  *    v33: Added -e flag and EDITOR.            7/23/85   mark
  2885.  *    v34: Added -s flag.                7/26/85   mark
  2886.  *    v35: Rewrote option handling; added option.c.    7/27/85   mark
  2887.  *    v36: Fixed -e flag to work if not last file.    7/29/85   mark
  2888.  *    v37: Added -x flag.                8/10/85   mark
  2889.  *    v38: Changed prompting; created prompt.c.    8/19/85   mark
  2890.  *    v39: (Not -p) does not initially clear screen.    8/24/85   mark
  2891.  *    v40: Added "skipping" indicator in forw().    8/26/85   mark
  2892.  *    v41: ONLY_RETURN, control char commands,    9/17/85   mark
  2893.  *         faster search, other minor fixes.
  2894.  *    v42: Added ++ command line syntax;        9/25/85   mark
  2895.  *         ch_fsize for pipes.
  2896.  *    v43: Added -h flag, changed prim.c algorithms.    10/15/85  mark
  2897.  *    v44: Made END print in all cases of eof;    10/16/85  mark
  2898.  *         ignore SIGTTOU after receiving SIGTSTP.
  2899.  *    v45: Never print backspaces unless -u.        10/16/85  mark
  2900.  *    v46: Backwards scroll in jump_loc.        10/24/85  mark
  2901.  *    v47: Fixed bug in edit(): *first_cmd==0        10/30/85  mark
  2902.  *    v48: Use TIOCSETN instead of TIOCSETP.        11/16/85  mark
  2903.  *         Added marks (m and ' commands).
  2904.  *    -----------------------------------------------------------------
  2905.  */
  2906.  
  2907. char version[] = "@(#) less  version 48";
  2908. SHAR_EOF
  2909.  
  2910.