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