home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 036 / less232.zip / CH.C < prev    next >
C/C++ Source or Header  |  1994-09-24  |  16KB  |  745 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. /*
  29.  * Low level character input from the input file.
  30.  * We use these special purpose routines which optimize moving
  31.  * both forward and backward from the current read pointer.
  32.  */
  33.  
  34. #include "less.h"
  35.  
  36. public int file = -1;        /* File descriptor of the input file */
  37. public int ignore_eoi;
  38.  
  39. /*
  40.  * Pool of buffers holding the most recently used blocks of the input file.
  41.  */
  42. #define LBUFSIZE    1024
  43. struct buf {
  44.     struct buf *next, *prev;  /* Must be first to match struct filestate */
  45.     long block;
  46.     unsigned int datasize;
  47.     unsigned char data[LBUFSIZE];
  48. };
  49.  
  50. /*
  51.  * The buffer pool is kept as a doubly-linked circular list,
  52.  * in order from most- to least-recently used.
  53.  * The circular list is anchored by the file state "thisfile".
  54.  *
  55.  * The file state is maintained in a filestate structure.
  56.  * There are two such structures, one used when input is a pipe
  57.  * and the other when input is an ordinary file.
  58.  * This is so that we can leave a pipe, look and other files,
  59.  * and return to the pipe without losing buffered data.
  60.  * Buffered data can be reconstructed for a non-pipe file by
  61.  * simply re-reading the file, but a pipe cannot be re-read.
  62.  */
  63.  
  64. struct filestate {
  65.     struct buf *next, *prev;   /* Must be first to match struct buf */
  66.     POSITION fpos;
  67.     int nbufs;
  68.     long block;
  69.     int offset;
  70.     POSITION fsize;
  71. };
  72.  
  73. #define    END_OF_CHAIN    ((struct buf *)thisfile)
  74. #define    buf_head    thisfile->next
  75. #define    buf_tail    thisfile->prev
  76. #define    ch_nbufs    thisfile->nbufs
  77. #define    ch_block    thisfile->block
  78. #define    ch_offset    thisfile->offset
  79. #define    ch_fpos        thisfile->fpos
  80. #define    ch_fsize    thisfile->fsize
  81.  
  82. static struct filestate pipefile =
  83.     { (struct buf *)&pipefile, (struct buf *)&pipefile };
  84.  
  85. static struct filestate nonpipefile = 
  86.     { (struct buf *)&nonpipefile, (struct buf *)&nonpipefile };
  87.  
  88. static struct filestate *thisfile;
  89.  
  90. extern int ispipe, iscompressed;
  91. extern int autobuf;
  92. extern int sigs;
  93. #if LOGFILE
  94. extern int logfile;
  95. extern char *namelogfile;
  96. #endif
  97.  
  98. static int ch_addbuf();
  99.  
  100.  
  101. /*
  102.  * Get the character pointed to by the read pointer.
  103.  * ch_get() is a macro which is more efficient to call
  104.  * than fch_get (the function), in the usual case 
  105.  * that the block desired is at the head of the chain.
  106.  */
  107. #define    ch_get()   ((ch_block == buf_head->block && \
  108.              ch_offset < buf_head->datasize) ? \
  109.             buf_head->data[ch_offset] : fch_get())
  110.     int
  111. fch_get()
  112. {
  113.     register struct buf *bp;
  114.     register int n;
  115.     register int slept;
  116.     POSITION pos;
  117.     POSITION len;
  118.  
  119.     slept = 0;
  120.  
  121.     /*
  122.      * Look for a buffer holding the desired block.
  123.      */
  124.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  125.         if (bp->block == ch_block)
  126.         {
  127.             if (ch_offset >= bp->datasize)
  128.                 /*
  129.                  * Need more data in this buffer.
  130.                  */
  131.                 goto read_more;
  132.             goto found;
  133.         }
  134.     /*
  135.      * Block is not in a buffer.  
  136.      * Take the least recently used buffer 
  137.      * and read the desired block into it.
  138.      * If the LRU buffer has data in it, 
  139.      * and autobuf is true, and input is a pipe, 
  140.      * then try to allocate a new buffer first.
  141.      */
  142.     if (autobuf && ispipe && buf_tail->block != (long)(-1))
  143.         if (ch_addbuf(1))
  144.             /*
  145.              * Allocation failed: turn off autobuf.
  146.              */
  147.             autobuf = 0;
  148.     bp = buf_tail;
  149.     bp->block = ch_block;
  150.     bp->datasize = 0;
  151.  
  152.     read_more:
  153.     pos = (ch_block * LBUFSIZE) + bp->datasize;
  154.     if ((len = ch_length()) != NULL_POSITION && pos >= len)
  155.         /*
  156.          * At end of file.
  157.          */
  158.         return (EOI);
  159.  
  160.     if (pos != ch_fpos)
  161.     {
  162.         /*
  163.          * Not at the correct position: must seek.
  164.          * If input is a pipe, we're in trouble (can't seek on a pipe).
  165.          * Some data has been lost: just return "?".
  166.          */
  167.         if (ispipe)
  168.             return ('?');
  169.         if (lseek(file, (off_t)pos, 0) == BAD_LSEEK)
  170.         {
  171.              error("seek error", NULL_PARG);
  172.              quit(1);
  173.          }
  174.          ch_fpos = pos;
  175.      }
  176.  
  177.     /*
  178.      * Read the block.
  179.      * If we read less than a full block, that's ok.
  180.      * We use partial block and pick up the rest next time.
  181.      */
  182.     n = iread(file, &bp->data[bp->datasize], 
  183.         (unsigned int)(LBUFSIZE - bp->datasize));
  184.     if (n == READ_INTR)
  185.         return (EOI);
  186.     if (n < 0)
  187.     {
  188.         error("read error", NULL_PARG);
  189.         quit(1);
  190.     }
  191.     ch_fpos += n;
  192.  
  193. #if LOGFILE
  194.     /*
  195.      * If we have a log file, write the new data to it.
  196.      */
  197.     if (logfile >= 0 && n > 0)
  198.         write(logfile, (char *) &bp->data[bp->datasize], n);
  199. #endif
  200.  
  201.     bp->datasize += n;
  202.  
  203.     /*
  204.      * If we have read to end of file, set ch_fsize to indicate
  205.      * the position of the end of file.
  206.      */
  207.     if (n == 0)
  208.     {
  209.         ch_fsize = pos;
  210.         if (ignore_eoi)
  211.         {
  212.             /*
  213.              * We are ignoring EOF.
  214.              * Wait a while, then try again.
  215.              */
  216.             if (!slept)
  217.                 ierror("Waiting for data", NULL_PARG);
  218. #if !MSOFTC
  219.              sleep(1);
  220. #endif
  221.             slept = 1;
  222.         }
  223.         if (ABORT_SIGS())
  224.             return (EOI);
  225.     }
  226.  
  227.     found:
  228.     if (buf_head != bp)
  229.     {
  230.         /*
  231.          * Move the buffer to the head of the buffer chain.
  232.          * This orders the buffer chain, most- to least-recently used.
  233.          */
  234.         bp->next->prev = bp->prev;
  235.         bp->prev->next = bp->next;
  236.  
  237.         bp->next = buf_head;
  238.         bp->prev = END_OF_CHAIN;
  239.         buf_head->prev = bp;
  240.         buf_head = bp;
  241.     }
  242.  
  243.     if (ch_offset >= bp->datasize)
  244.         /*
  245.          * After all that, we still don't have enough data.
  246.          * Go back and try again.
  247.          */
  248.         goto read_more;
  249.  
  250.     return (bp->data[ch_offset]);
  251. }
  252.  
  253. #if LOGFILE
  254. /*
  255.  * Close the logfile.
  256.  * If we haven't read all of standard input into it, do that now.
  257.  */
  258.     public void
  259. end_logfile()
  260. {
  261.     static int tried = 0;
  262.  
  263.     if (logfile < 0)
  264.         return;
  265.     if (!tried && ch_fsize == NULL_POSITION)
  266.     {
  267.         tried = 1;
  268.         ierror("Finishing logfile", NULL_PARG);
  269.         while (ch_forw_get() != EOI)
  270.             if (ABORT_SIGS())
  271.                 break;
  272.     }
  273.     close(logfile);
  274.     logfile = -1;
  275.     namelogfile = NULL;
  276. }
  277.  
  278. /*
  279.  * Start a log file AFTER less has already been running.
  280.  * Invoked from the - command; see toggle_option().
  281.  * Write all the existing buffered data to the log file.
  282.  */
  283.     public void
  284. sync_logfile()
  285. {
  286.     register struct buf *bp;
  287.     long block;
  288.     long last_block;
  289.  
  290.     last_block = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
  291.     for (block = 0;  block <= last_block;  block++)
  292.         for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  293.             if (bp->block == block)
  294.             {
  295.                 write(logfile, (char *) bp->data, bp->datasize);
  296.                 break;
  297.             }
  298. }
  299.  
  300. #endif
  301.  
  302. /*
  303.  * Determine if a specific block is currently in one of the buffers.
  304.  */
  305.     static int
  306. buffered(block)
  307.     long block;
  308. {
  309.     register struct buf *bp;
  310.  
  311.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  312.         if (bp->block == block)
  313.             return (1);
  314.     return (0);
  315. }
  316.  
  317. /*
  318.  * Seek to a specified position in the file.
  319.  * Return 0 if successful, non-zero if can't seek there.
  320.  */
  321.     public int
  322. ch_seek(pos)
  323.     register POSITION pos;
  324. {
  325.     long new_block;
  326.     POSITION len;
  327.  
  328.     len = ch_length();
  329.     if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
  330.         return (1);
  331.  
  332.     new_block = pos / LBUFSIZE;
  333.     if (ispipe && pos != ch_fpos && !buffered(new_block))
  334.         return (1);
  335.     /*
  336.      * Set read pointer.
  337.      */
  338.     ch_block = new_block;
  339.     ch_offset = pos % LBUFSIZE;
  340.     return (0);
  341. }
  342.  
  343. /*
  344.  * Seek to the end of the file.
  345.  */
  346.     public int
  347. ch_end_seek()
  348. {
  349.     POSITION len;
  350.  
  351.     if (!ispipe)
  352.         ch_fsize = filesize(file);
  353.  
  354.     len = ch_length();
  355.     if (len != NULL_POSITION)
  356.         return (ch_seek(len));
  357.  
  358.     /*
  359.      * Do it the slow way: read till end of data.
  360.      */
  361.     while (ch_forw_get() != EOI)
  362.         if (ABORT_SIGS())
  363.             return (1);
  364.     return (0);
  365. }
  366.  
  367. /*
  368.  * Seek to the beginning of the file, or as close to it as we can get.
  369.  * We may not be able to seek there if input is a pipe and the
  370.  * beginning of the pipe is no longer buffered.
  371.  */
  372.     public int
  373. ch_beg_seek()
  374. {
  375.     register struct buf *bp, *firstbp;
  376.  
  377.     /*
  378.      * Try a plain ch_seek first.
  379.      */
  380.     if (ch_seek(ch_zero()) == 0)
  381.         return (0);
  382.  
  383.     /*
  384.      * Can't get to position 0.
  385.      * Look thru the buffers for the one closest to position 0.
  386.      */
  387.     firstbp = bp = buf_head;
  388.     if (bp == END_OF_CHAIN)
  389.         return (1);
  390.     while ((bp = bp->next) != END_OF_CHAIN)
  391.         if (bp->block < firstbp->block)
  392.             firstbp = bp;
  393.     ch_block = firstbp->block;
  394.     ch_offset = 0;
  395.     return (0);
  396. }
  397.  
  398. /*
  399.  * Return the length of the file, if known.
  400.  */
  401.     public POSITION
  402. ch_length()
  403. {
  404.     if (ignore_eoi)
  405.         return (NULL_POSITION);
  406.     return (ch_fsize);
  407. }
  408.  
  409. /*
  410.  * Return the current position in the file.
  411.  */
  412. #define    tellpos(blk,off)   ((POSITION)((((long)(blk)) * LBUFSIZE) + (off)))
  413.  
  414.     public POSITION
  415. ch_tell()
  416. {
  417.     return (tellpos(ch_block, ch_offset));
  418. }
  419.  
  420. /*
  421.  * Get the current char and post-increment the read pointer.
  422.  */
  423.     public int
  424. ch_forw_get()
  425. {
  426.     register int c;
  427.  
  428.     c = ch_get();
  429.     if (c == EOI)
  430.         return (EOI);
  431.     if (ch_offset < LBUFSIZE-1)
  432.         ch_offset++;
  433.     else
  434.     {
  435. #if __ZOFFSET /* NOT WORKING */
  436.         if (ch_fsize != NULL_POSITION && 
  437.             tellpos(ch_block+1, 0) >= ch_fsize)
  438.             return (EOI);
  439. #endif
  440.         ch_block ++;
  441.         ch_offset = 0;
  442.     }
  443.     return (c);
  444. }
  445.  
  446. /*
  447.  * Pre-decrement the read pointer and get the new current char.
  448.  */
  449.     public int
  450. ch_back_get()
  451. {
  452.     if (ch_offset > 0)
  453.         ch_offset --;
  454.     else
  455.     {
  456. #if __ZOFFSET /* NOT WORKING */
  457.         if (tellpos(ch_block-1, LBUFSIZE-1) < ch_zero())
  458.             return (EOI);
  459. #else
  460.         if (ch_block <= 0)
  461.             return (EOI);
  462. #endif
  463.         if (ispipe && !buffered(ch_block-1))
  464.             return (EOI);
  465.         ch_block--;
  466.         ch_offset = LBUFSIZE-1;
  467.     }
  468.     return (ch_get());
  469. }
  470.  
  471. /*
  472.  * Allocate buffers.
  473.  * Caller wants us to have a total of at least want_nbufs buffers.
  474.  */
  475.     public int
  476. ch_nbuf(want_nbufs)
  477.     int want_nbufs;
  478. {
  479.     PARG parg;
  480.  
  481.     if (ch_nbufs < want_nbufs && ch_addbuf(want_nbufs - ch_nbufs))
  482.     {
  483.         /*
  484.          * Cannot allocate enough buffers.
  485.          * If we don't have ANY, then quit.
  486.          * Otherwise, just report the error and return.
  487.          */
  488.         parg.p_int = want_nbufs - ch_nbufs;
  489.         error("Cannot allocate %d buffers", &parg);
  490.         if (ch_nbufs == 0)
  491.             quit(1);
  492.     }
  493.     return (ch_nbufs);
  494. }
  495.  
  496. /*
  497.  * Flush any saved file state, including buffer contents.
  498.  */
  499.     public void
  500. ch_flush()
  501. {
  502.     register struct buf *bp;
  503.  
  504.     if (ispipe && !iscompressed)
  505.     {
  506.         /*
  507.          * If input is a pipe, we don't flush buffer contents,
  508.          * since the contents can't be recovered.
  509.          */
  510.         ch_fsize = NULL_POSITION;
  511.         return;
  512.     }
  513.  
  514.     /*
  515.      * Initialize all the buffers.
  516.      */
  517.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  518.         bp->block = (long)(-1);
  519.  
  520.     /*
  521.      * Figure out the size of the file, if we can.
  522.      */
  523.     ch_fsize = filesize(file);
  524.  
  525.     /*
  526.      * Seek to a known position: the beginning of the file.
  527.      */
  528.     ch_fpos = 0;
  529.     ch_block = ch_fpos / LBUFSIZE;
  530.     ch_offset = ch_fpos % LBUFSIZE;
  531.  
  532.     if (iscompressed)
  533.     {
  534.           ch_fsize = NULL_POSITION;
  535.           return;
  536.     }
  537.  
  538.     if (lseek(file, (off_t)0, 0) == BAD_LSEEK)
  539.     {
  540.         /*
  541.          * Warning only; even if the seek fails for some reason,
  542.          * there's a good chance we're at the beginning anyway.
  543.          * {{ I think this is bogus reasoning. }}
  544.          */
  545.         error("seek error to 0", NULL_PARG);
  546.     }
  547. }
  548.  
  549. /*
  550.  * Allocate some new buffers.
  551.  * The buffers are added to the tail of the buffer chain.
  552.  */
  553.     static int
  554. ch_addbuf(nnew)
  555.     int nnew;
  556. {
  557.     register struct buf *bp;
  558.     register struct buf *newbufs;
  559.  
  560.     /*
  561.      * We don't have enough buffers.  
  562.      * Allocate some new ones.
  563.      */
  564.     newbufs = (struct buf *) calloc(nnew, sizeof(struct buf));
  565.     if (newbufs == NULL)
  566.         return (1);
  567.  
  568.     /*
  569.      * Initialize the new buffers and link them together.
  570.      * Link them all onto the tail of the buffer list.
  571.      */
  572.     ch_nbufs += nnew;
  573.     for (bp = &newbufs[0];  bp < &newbufs[nnew];  bp++)
  574.     {
  575.         bp->next = bp + 1;
  576.         bp->prev = bp - 1;
  577.         bp->block = (long)(-1);
  578.     }
  579.     newbufs[nnew-1].next = END_OF_CHAIN;
  580.     newbufs[0].prev = buf_tail;
  581.     buf_tail->next = &newbufs[0];
  582.     buf_tail = &newbufs[nnew-1];
  583.     return (0);
  584. }
  585.  
  586. /*
  587.  * Use the pipe file state.
  588.  */
  589.     public void
  590. ch_pipe()
  591. {
  592.     thisfile = &pipefile;
  593. }
  594.  
  595. /*
  596.  * Use the non-pipe file state.
  597.  */
  598.     public void
  599. ch_nonpipe()
  600. {
  601.     thisfile = &nonpipefile;
  602. }
  603.  
  604. #if 0
  605. /* TESTING */
  606.  
  607. extern VOID_POINTER memchr();
  608. extern char linebuf[];
  609. extern int size_linebuf;
  610.  
  611. /*
  612.  * Analogous to forw_line(), but deals with "raw lines":
  613.  * lines which are not split for screen width.
  614.  * {{ This is supposed to be more efficient than forw_line(). }}
  615.  */
  616.     public POSITION
  617. forw_raw_line(curr_pos, linep)
  618.     POSITION curr_pos;
  619.     char **linep;
  620. {
  621.     register char *p;
  622.     register int bc;
  623.     register int avail;
  624.     register int space;
  625.     register char *q;
  626.  
  627.     if (curr_pos == NULL_POSITION || ch_seek(curr_pos) || fch_get() == EOI)
  628.         return (NULL_POSITION);
  629.  
  630.     p = linebuf;
  631.     space = size_linebuf - 1;
  632.  
  633.     do
  634.     {
  635.         /*
  636.          * avail = bytes available in data buffer.
  637.          * space = space left in line buffer.
  638.          * bc    = byte count to move from data buffer to line buffer.
  639.          */
  640.         bc = avail = buf_head->datasize - ch_offset;
  641.         if (avail <= 0)
  642.             break;
  643.         /*
  644.          * Find the next newline in data buffer.
  645.          */
  646.         q = (char *) memchr((char *) &buf_head->data[ch_offset], '\n', avail);
  647.         if (q != NULL)
  648.         {
  649.             /*
  650.              * Found a newline.
  651.              * Copy data buffer to line buffer and we're done.
  652.              */
  653.             bc = q - (char *) &buf_head->data[ch_offset];
  654.             if (bc > space)
  655.                 bc = space;
  656.             memcpy(p, (char *) &buf_head->data[ch_offset], bc);
  657.             p += bc;
  658.             ch_offset += bc + 1;
  659.             if (ch_offset >= LBUFSIZE)
  660.             {
  661.                 ch_offset = 0;
  662.                 ch_block++;
  663.             }
  664.             break;
  665.         } 
  666.         /*
  667.          * Didn't find a newline.
  668.          * Copy data buffer to line buffer,
  669.          * get the next data buffer and try again.
  670.          */
  671.         bc = avail;
  672.         if (bc > space)
  673.             bc = space;
  674.         memcpy(p, (char *) &buf_head->data[ch_offset], bc);
  675.         space -= bc;
  676.         p += bc;
  677.         ch_offset = 0;
  678.         ch_block++; 
  679.     } while (fch_get() != EOI && space > 0);
  680.  
  681.     *p = '\0';
  682.     if (linep != NULL)
  683.         *linep = linebuf;
  684.     return (ch_tell());
  685. }
  686.  
  687. /*
  688.  * Analogous to back_line(), but deals with "raw lines".
  689.  * {{ This is supposed to be more efficient than back_line(). }}
  690.  */
  691.     public POSITION
  692. back_raw_line(curr_pos, linep)
  693.     POSITION curr_pos;
  694.     char **linep;
  695. {
  696.     register char *p;
  697.     register char *q;    /* scratch pointer */
  698.     register i;        /* scratch index */
  699.     register int bc;    /* byte count to transfer */
  700.     register int space;    /* space left in linebuf */
  701.     
  702.     if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)1 ||
  703.         ch_seek(curr_pos-2) || ch_get() == EOI)
  704.         return (NULL_POSITION);
  705.  
  706.     p = &linebuf[size_linebuf];
  707.     *--p = '\0';
  708.     space = size_linebuf - 1;
  709.  
  710.     do {
  711.         bc = ch_offset + 1;
  712.         if (bc > space)
  713.             bc = space;
  714.         /*
  715.          * Find the previous newline in data buffer.
  716.          */
  717.         q = (char *) &buf_head->data[ch_offset] + 1;
  718.         for (i = bc;  i > 0;  i--)
  719.             if ((*--p = *--q) == '\n')
  720.             {
  721.                 /*
  722.                  * Found a newline.
  723.                  * We're done.
  724.                  */
  725.                 p++;
  726.                 ch_offset -= bc - i;
  727.                 ch_seek(ch_tell() + 1);
  728.                 goto done;
  729.             }
  730.         /*
  731.          * Didn't find a newline.
  732.          */
  733.         bc--;
  734.         ch_offset -= bc;
  735.         space -= bc;
  736.     } while (space > 0 && ch_block > 0 && ch_seek(ch_tell() - 1) == 0 && 
  737.          fch_get() != EOI);
  738.  
  739.     done:
  740.     if (linep != NULL)
  741.         *linep = p;
  742.     return (ch_tell());
  743. }
  744. #endif
  745.