home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 11 Util / 11-Util.zip / LESS177.ZIP / src / ch.c < prev    next >
C/C++ Source or Header  |  1992-07-18  |  11KB  |  568 lines

  1. /*
  2.  * Low level character input from the input file.
  3.  * We use these special purpose routines which optimize moving
  4.  * both forward and backward from the current read pointer.
  5.  */
  6.  
  7. #include "less.h"
  8.  
  9. public int file = -1;        /* File descriptor of the input file */
  10. public int ignore_eoi;
  11.  
  12. /*
  13.  * Pool of buffers holding the most recently used blocks of the input file.
  14.  */
  15. #define BUFSIZ    1024
  16. struct buf {
  17.     struct buf *next, *prev;  /* Must be first to match struct filestate */
  18.     long block;
  19.     unsigned int datasize;
  20.     unsigned char data[BUFSIZ];
  21. };
  22.  
  23. /*
  24.  * The buffer pool is kept as a doubly-linked circular list,
  25.  * in order from most- to least-recently used.
  26.  * The circular list is anchored by the file state "thisfile".
  27.  *
  28.  * The file state is maintained in a filestate structure.
  29.  * There are two such structures, one used when input is a pipe
  30.  * and the other when input is an ordinary file.
  31.  * This is so that we can leave a pipe, look and other files,
  32.  * and return to the pipe without losing buffered data.
  33.  * Buffered data can be reconstructed for a non-pipe file by
  34.  * simply re-reading the file, but a pipe cannot be re-read.
  35.  */
  36.  
  37. struct filestate {
  38.     struct buf *next, *prev;   /* Must be first to match struct buf */
  39.     POSITION fpos;
  40.     int nbufs;
  41.     long block;
  42.     int offset;
  43.     POSITION fsize;
  44. };
  45.  
  46. #define    END_OF_CHAIN    ((struct buf *)thisfile)
  47. #define    buf_head    thisfile->next
  48. #define    buf_tail    thisfile->prev
  49. #define    ch_nbufs    thisfile->nbufs
  50. #define    ch_block    thisfile->block
  51. #define    ch_offset    thisfile->offset
  52. #define    ch_fpos        thisfile->fpos
  53. #define    ch_fsize    thisfile->fsize
  54.  
  55. static struct filestate pipefile =
  56.     { (struct buf *)&pipefile, (struct buf *)&pipefile };
  57.  
  58. static struct filestate nonpipefile = 
  59.     { (struct buf *)&nonpipefile, (struct buf *)&nonpipefile };
  60.  
  61. static struct filestate *thisfile;
  62.  
  63. extern int ispipe;
  64. extern int autobuf;
  65. extern int sigs;
  66. #if LOGFILE
  67. extern int logfile;
  68. extern char *namelogfile;
  69. #endif
  70.  
  71. static int ch_addbuf();
  72.  
  73.  
  74. /*
  75.  * Get the character pointed to by the read pointer.
  76.  * ch_get() is a macro which is more efficient to call
  77.  * than fch_get (the function), in the usual case 
  78.  * that the block desired is at the head of the chain.
  79.  */
  80. #define    ch_get()   ((ch_block == buf_head->block && \
  81.              ch_offset < buf_head->datasize) ? \
  82.             buf_head->data[ch_offset] : fch_get())
  83.     static int
  84. fch_get()
  85. {
  86.     register struct buf *bp;
  87.     register int n;
  88.     register int slept;
  89.     POSITION pos;
  90.     POSITION len;
  91.  
  92.     slept = 0;
  93.  
  94.     /*
  95.      * Look for a buffer holding the desired block.
  96.      */
  97.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  98.         if (bp->block == ch_block)
  99.         {
  100.             if (ch_offset >= bp->datasize)
  101.                 /*
  102.                  * Need more data in this buffer.
  103.                  */
  104.                 goto read_more;
  105.             goto found;
  106.         }
  107.     /*
  108.      * Block is not in a buffer.  
  109.      * Take the least recently used buffer 
  110.      * and read the desired block into it.
  111.      * If the LRU buffer has data in it, 
  112.      * and autobuf is true, and input is a pipe, 
  113.      * then try to allocate a new buffer first.
  114.      */
  115.     if (autobuf && ispipe && buf_tail->block != (long)(-1))
  116.         if (ch_addbuf(1))
  117.             /*
  118.              * Allocation failed: turn off autobuf.
  119.              */
  120.             autobuf = 0;
  121.     bp = buf_tail;
  122.     bp->block = ch_block;
  123.     bp->datasize = 0;
  124.  
  125.     read_more:
  126.     pos = (ch_block * BUFSIZ) + bp->datasize;
  127.     if ((len = ch_length()) != NULL_POSITION && pos >= len)
  128.         /*
  129.          * At end of file.
  130.          */
  131.         return (EOI);
  132.  
  133.     if (pos != ch_fpos)
  134.     {
  135.         /*
  136.          * Not at the correct position: must seek.
  137.          * If input is a pipe, we're in trouble (can't seek on a pipe).
  138.          * Some data has been lost: just return "?".
  139.          */
  140.         if (ispipe)
  141.             return ('?');
  142.         if (lseek(file, (offset_t)pos, 0) == BAD_LSEEK)
  143.         {
  144.              error("seek error", NULL_PARG);
  145.              quit(1);
  146.          }
  147.          ch_fpos = pos;
  148.      }
  149.  
  150.     /*
  151.      * Read the block.
  152.      * If we read less than a full block, that's ok.
  153.      * We use partial block and pick up the rest next time.
  154.      */
  155.     n = iread(file, &bp->data[bp->datasize], 
  156.         (unsigned int)(BUFSIZ - bp->datasize));
  157.     if (n == READ_INTR)
  158.         return (EOI);
  159.     if (n < 0)
  160.     {
  161.         error("read error", NULL_PARG);
  162.         quit(1);
  163.     }
  164.     ch_fpos += n;
  165.  
  166. #if LOGFILE
  167.     /*
  168.      * If we have a log file, write the new data to it.
  169.      */
  170.     if (logfile >= 0 && n > 0)
  171.         write(logfile, (char *) &bp->data[bp->datasize], n);
  172. #endif
  173.  
  174.     bp->datasize += n;
  175.  
  176.     /*
  177.      * If we have read to end of file, set ch_fsize to indicate
  178.      * the position of the end of file.
  179.      */
  180.     if (n == 0)
  181.     {
  182.         ch_fsize = pos;
  183.         if (ignore_eoi)
  184.         {
  185.             /*
  186.              * We are ignoring EOF.
  187.              * Wait a while, then try again.
  188.              */
  189.             if (!slept)
  190.                 ierror("Waiting for data", NULL_PARG);
  191.             sleep(1);
  192.             slept = 1;
  193.         }
  194.         if (sigs)
  195.             return (EOI);
  196.     }
  197.  
  198.     found:
  199.     if (buf_head != bp)
  200.     {
  201.         /*
  202.          * Move the buffer to the head of the buffer chain.
  203.          * This orders the buffer chain, most- to least-recently used.
  204.          */
  205.         bp->next->prev = bp->prev;
  206.         bp->prev->next = bp->next;
  207.  
  208.         bp->next = buf_head;
  209.         bp->prev = END_OF_CHAIN;
  210.         buf_head->prev = bp;
  211.         buf_head = bp;
  212.     }
  213.  
  214.     if (ch_offset >= bp->datasize)
  215.         /*
  216.          * After all that, we still don't have enough data.
  217.          * Go back and try again.
  218.          */
  219.         goto read_more;
  220.  
  221.     return (bp->data[ch_offset]);
  222. }
  223.  
  224. #if LOGFILE
  225. /*
  226.  * Close the logfile.
  227.  * If we haven't read all of standard input into it, do that now.
  228.  */
  229.     public void
  230. end_logfile()
  231. {
  232.     static int tried = 0;
  233.  
  234.     if (logfile < 0)
  235.         return;
  236.     if (!tried && ch_fsize == NULL_POSITION)
  237.     {
  238.         tried = 1;
  239.         ierror("Finishing logfile", NULL_PARG);
  240.         while (ch_forw_get() != EOI)
  241.             if (sigs)
  242.                 break;
  243.     }
  244.     close(logfile);
  245.     logfile = -1;
  246.     namelogfile = NULL;
  247. }
  248.  
  249. /*
  250.  * Start a log file AFTER less has already been running.
  251.  * Invoked from the - command; see toggle_option().
  252.  * Write all the existing buffered data to the log file.
  253.  */
  254.     public void
  255. sync_logfile()
  256. {
  257.     register struct buf *bp;
  258.     long block;
  259.     long last_block;
  260.  
  261.     last_block = (ch_fpos + BUFSIZ - 1) / BUFSIZ;
  262.     for (block = 0;  block <= last_block;  block++)
  263.         for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  264.             if (bp->block == block)
  265.             {
  266.                 write(logfile, (char *) bp->data, bp->datasize);
  267.                 break;
  268.             }
  269. }
  270.  
  271. #endif
  272.  
  273. /*
  274.  * Determine if a specific block is currently in one of the buffers.
  275.  */
  276.     static int
  277. buffered(block)
  278.     long block;
  279. {
  280.     register struct buf *bp;
  281.  
  282.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  283.         if (bp->block == block)
  284.             return (1);
  285.     return (0);
  286. }
  287.  
  288. /*
  289.  * Seek to a specified position in the file.
  290.  * Return 0 if successful, non-zero if can't seek there.
  291.  */
  292.     public int
  293. ch_seek(pos)
  294.     register POSITION pos;
  295. {
  296.     long new_block;
  297.     POSITION len;
  298.  
  299.     len = ch_length();
  300.     if (pos < ch_zero() || (len != NULL_POSITION && pos > len))
  301.         return (1);
  302.  
  303.     new_block = pos / BUFSIZ;
  304.     if (ispipe && pos != ch_fpos && !buffered(new_block))
  305.         return (1);
  306.     /*
  307.      * Set read pointer.
  308.      */
  309.     ch_block = new_block;
  310.     ch_offset = pos % BUFSIZ;
  311.     return (0);
  312. }
  313.  
  314. /*
  315.  * Seek to the end of the file.
  316.  */
  317.     public int
  318. ch_end_seek()
  319. {
  320.     POSITION len;
  321.  
  322.     if (!ispipe)
  323.         ch_fsize = filesize(file);
  324.  
  325.     len = ch_length();
  326.     if (len != NULL_POSITION)
  327.         return (ch_seek(len));
  328.  
  329.     /*
  330.      * Do it the slow way: read till end of data.
  331.      */
  332.     while (ch_forw_get() != EOI)
  333.         if (sigs)
  334.             return (1);
  335.     return (0);
  336. }
  337.  
  338. /*
  339.  * Seek to the beginning of the file, or as close to it as we can get.
  340.  * We may not be able to seek there if input is a pipe and the
  341.  * beginning of the pipe is no longer buffered.
  342.  */
  343.     public int
  344. ch_beg_seek()
  345. {
  346.     register struct buf *bp, *firstbp;
  347.  
  348.     /*
  349.      * Try a plain ch_seek first.
  350.      */
  351.     if (ch_seek(ch_zero()) == 0)
  352.         return (0);
  353.  
  354.     /*
  355.      * Can't get to position 0.
  356.      * Look thru the buffers for the one closest to position 0.
  357.      */
  358.     firstbp = bp = buf_head;
  359.     if (bp == END_OF_CHAIN)
  360.         return (1);
  361.     while ((bp = bp->next) != END_OF_CHAIN)
  362.         if (bp->block < firstbp->block)
  363.             firstbp = bp;
  364.     ch_block = firstbp->block;
  365.     ch_offset = 0;
  366.     return (0);
  367. }
  368.  
  369. /*
  370.  * Return the length of the file, if known.
  371.  */
  372.     public POSITION
  373. ch_length()
  374. {
  375.     if (ignore_eoi)
  376.         return (NULL_POSITION);
  377.     return (ch_fsize);
  378. }
  379.  
  380. /*
  381.  * Return the current position in the file.
  382.  */
  383. #define    tellpos(blk,off)   ((POSITION)((((long)(blk)) * BUFSIZ) + (off)))
  384.  
  385.     public POSITION
  386. ch_tell()
  387. {
  388.     return (tellpos(ch_block, ch_offset));
  389. }
  390.  
  391. /*
  392.  * Get the current char and post-increment the read pointer.
  393.  */
  394.     public int
  395. ch_forw_get()
  396. {
  397.     register int c;
  398.  
  399.     c = ch_get();
  400.     if (c == EOI)
  401.         return (EOI);
  402.     if (ch_offset < BUFSIZ-1)
  403.         ch_offset++;
  404.     else
  405.     {
  406. #if __ZOFFSET /* NOT WORKING */
  407.         if (ch_fsize != NULL_POSITION && 
  408.             tellpos(ch_block+1, 0) >= ch_fsize)
  409.             return (EOI);
  410. #endif
  411.         ch_block ++;
  412.         ch_offset = 0;
  413.     }
  414.     return (c);
  415. }
  416.  
  417. /*
  418.  * Pre-decrement the read pointer and get the new current char.
  419.  */
  420.     public int
  421. ch_back_get()
  422. {
  423.     if (ch_offset > 0)
  424.         ch_offset --;
  425.     else
  426.     {
  427. #if __ZOFFSET /* NOT WORKING */
  428.         if (tellpos(ch_block-1, BUFSIZ-1) < ch_zero())
  429.             return (EOI);
  430. #else
  431.         if (ch_block <= 0)
  432.             return (EOI);
  433. #endif
  434.         if (ispipe && !buffered(ch_block-1))
  435.             return (EOI);
  436.         ch_block--;
  437.         ch_offset = BUFSIZ-1;
  438.     }
  439.     return (ch_get());
  440. }
  441.  
  442. /*
  443.  * Allocate buffers.
  444.  * Caller wants us to have a total of at least want_nbufs buffers.
  445.  */
  446.     public int
  447. ch_nbuf(want_nbufs)
  448.     int want_nbufs;
  449. {
  450.     PARG parg;
  451.  
  452.     if (ch_nbufs < want_nbufs && ch_addbuf(want_nbufs - ch_nbufs))
  453.     {
  454.         /*
  455.          * Cannot allocate enough buffers.
  456.          * If we don't have ANY, then quit.
  457.          * Otherwise, just report the error and return.
  458.          */
  459.         parg.p_int = want_nbufs - ch_nbufs;
  460.         error("Cannot allocate %d buffers", &parg);
  461.         if (ch_nbufs == 0)
  462.             quit(1);
  463.     }
  464.     return (ch_nbufs);
  465. }
  466.  
  467. /*
  468.  * Flush any saved file state, including buffer contents.
  469.  */
  470.     public void
  471. ch_flush()
  472. {
  473.     register struct buf *bp;
  474.  
  475.     if (ispipe)
  476.     {
  477.         /*
  478.          * If input is a pipe, we don't flush buffer contents,
  479.          * since the contents can't be recovered.
  480.          */
  481.         ch_fsize = NULL_POSITION;
  482.         return;
  483.     }
  484.  
  485.     /*
  486.      * Initialize all the buffers.
  487.      */
  488.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  489.         bp->block = (long)(-1);
  490.  
  491.     /*
  492.      * Figure out the size of the file, if we can.
  493.      */
  494.     ch_fsize = filesize(file);
  495.  
  496.     /*
  497.      * Seek to a known position: the beginning of the file.
  498.      */
  499.     ch_fpos = 0;
  500.     ch_block = ch_fpos / BUFSIZ;
  501.     ch_offset = ch_fpos % BUFSIZ;
  502.  
  503.     if (lseek(file, (offset_t)0, 0) == BAD_LSEEK)
  504.     {
  505.         /*
  506.          * Warning only; even if the seek fails for some reason,
  507.          * there's a good chance we're at the beginning anyway.
  508.          * {{ I think this is bogus reasoning. }}
  509.          */
  510.         error("seek error to 0", NULL_PARG);
  511.     }
  512. }
  513.  
  514. /*
  515.  * Allocate some new buffers.
  516.  * The buffers are added to the tail of the buffer chain.
  517.  */
  518.     static int
  519. ch_addbuf(nnew)
  520.     int nnew;
  521. {
  522.     register struct buf *bp;
  523.     register struct buf *newbufs;
  524.  
  525.     /*
  526.      * We don't have enough buffers.  
  527.      * Allocate some new ones.
  528.      */
  529.     newbufs = (struct buf *) calloc(nnew, sizeof(struct buf));
  530.     if (newbufs == NULL)
  531.         return (1);
  532.  
  533.     /*
  534.      * Initialize the new buffers and link them together.
  535.      * Link them all onto the tail of the buffer list.
  536.      */
  537.     ch_nbufs += nnew;
  538.     for (bp = &newbufs[0];  bp < &newbufs[nnew];  bp++)
  539.     {
  540.         bp->next = bp + 1;
  541.         bp->prev = bp - 1;
  542.         bp->block = (long)(-1);
  543.     }
  544.     newbufs[nnew-1].next = END_OF_CHAIN;
  545.     newbufs[0].prev = buf_tail;
  546.     buf_tail->next = &newbufs[0];
  547.     buf_tail = &newbufs[nnew-1];
  548.     return (0);
  549. }
  550.  
  551. /*
  552.  * Use the pipe file state.
  553.  */
  554.     public void
  555. ch_pipe()
  556. {
  557.     thisfile = &pipefile;
  558. }
  559.  
  560. /*
  561.  * Use the non-pipe file state.
  562.  */
  563.     public void
  564. ch_nonpipe()
  565. {
  566.     thisfile = &nonpipefile;
  567. }
  568.