home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume16 / less5 / part03 / ch.c next >
Encoding:
C/C++ Source or Header  |  1988-09-22  |  9.6 KB  |  489 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.  
  11. /*
  12.  * Pool of buffers holding the most recently used blocks of the input file.
  13.  */
  14. #define BUFSIZ    1024
  15. struct buf {
  16.     struct buf *next, *prev;
  17.     long block;
  18.     int datasize;
  19.     char data[BUFSIZ];
  20. };
  21. public int nbufs;
  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 buf_anchor.
  27.  */
  28. #define    END_OF_CHAIN    ((struct buf *)&buf_anchor)
  29. #define    buf_head    buf_anchor.next
  30. #define    buf_tail    buf_anchor.prev
  31.  
  32. static struct {
  33.     struct buf *next, *prev;
  34. } buf_anchor = { END_OF_CHAIN, END_OF_CHAIN };
  35.  
  36. extern int clean_data;
  37. extern int ispipe;
  38. extern int autobuf;
  39. extern int cbufs;
  40. extern int sigs;
  41. #if LOGFILE
  42. extern int logfile;
  43. #endif
  44.  
  45. /*
  46.  * Current position in file.
  47.  * Stored as a block number and an offset into the block.
  48.  */
  49. static long ch_block;
  50. static int ch_offset;
  51.  
  52. /* 
  53.  * Length of file, needed if input is a pipe.
  54.  */
  55. static POSITION ch_fsize;
  56.  
  57. /*
  58.  * Number of bytes read, if input is standard input (a pipe).
  59.  */
  60. static POSITION last_piped_pos;
  61.  
  62. /*
  63.  * Get the character pointed to by the read pointer.
  64.  * ch_get() is a macro which is more efficient to call
  65.  * than fch_get (the function), in the usual case 
  66.  * that the block desired is at the head of the chain.
  67.  */
  68. #define    ch_get()   ((buf_head->block == ch_block && \
  69.              ch_offset < buf_head->datasize) ? \
  70.             buf_head->data[ch_offset] : fch_get())
  71.     static int
  72. fch_get()
  73. {
  74.     register struct buf *bp;
  75.     register int n;
  76.     register char *p;
  77.     POSITION pos;
  78.  
  79.     /*
  80.      * Look for a buffer holding the desired block.
  81.      */
  82.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  83.         if (bp->block == ch_block)
  84.         {
  85.             if (ch_offset >= bp->datasize)
  86.                 /*
  87.                  * Need more data in this buffer.
  88.                  */
  89.                 goto read_more;
  90.             /*
  91.              * On a pipe, we don't sort the buffers LRU
  92.              * because this can cause gaps in the buffers.
  93.              * For example, suppose we've got 12 1K buffers,
  94.              * and a 15K input stream.  If we read the first 12K
  95.              * sequentially, then jump to line 1, then jump to
  96.              * the end, the buffers have blocks 0,4,5,6,..,14.
  97.              * If we then jump to line 1 again and try to
  98.              * read sequentially, we're out of luck when we
  99.              * get to block 1 (we'd get the "pipe error" below).
  100.              * To avoid this, we only sort buffers on a pipe
  101.              * when we actually READ the data, not when we
  102.              * find it already buffered.
  103.              */
  104.             if (ispipe)
  105.                 return (bp->data[ch_offset]);
  106.             goto found;
  107.         }
  108.     /*
  109.      * Block is not in a buffer.  
  110.      * Take the least recently used buffer 
  111.      * and read the desired block into it.
  112.      * If the LRU buffer has data in it, 
  113.      * and autobuf is true, and input is a pipe, 
  114.      * then try to allocate a new buffer first.
  115.      */
  116.     if (autobuf && ispipe && buf_tail->block != (long)(-1))
  117.         (void) ch_addbuf(1);
  118.     bp = buf_tail;
  119.     bp->block = ch_block;
  120.     bp->datasize = 0;
  121.  
  122.     read_more:
  123.     pos = (ch_block * BUFSIZ) + bp->datasize;
  124.     if (ispipe)
  125.     {
  126.         /*
  127.          * The data requested should be immediately after
  128.          * the last data read from the pipe.
  129.          */
  130.         if (pos != last_piped_pos)
  131.         {
  132.             error("pipe error");
  133.             quit();
  134.         }
  135.     } else
  136.         lseek(file, pos, 0);
  137.  
  138.     /*
  139.      * Read the block.
  140.      * If we read less than a full block, we just return the
  141.      * partial block and pick up the rest next time.
  142.      */
  143.     n = iread(file, &bp->data[bp->datasize], BUFSIZ - bp->datasize);
  144.     if (n == READ_INTR)
  145.         return (EOI);
  146.     if (n < 0)
  147.     {
  148.         error("read error");
  149.         quit();
  150.     }
  151.     if (ispipe)
  152.         last_piped_pos += n;
  153.  
  154. #if LOGFILE
  155.     /*
  156.      * If we have a log file, write the new data to it.
  157.      */
  158.     if (logfile >= 0 && n > 0)
  159.         write(logfile, &bp->data[bp->datasize], n);
  160. #endif
  161.  
  162.     bp->datasize += n;
  163.  
  164.     /*
  165.      * Set an EOI marker in the buffered data itself.
  166.      * Then ensure the data is "clean": there are no 
  167.      * extra EOI chars in the data and that the "meta"
  168.      * bit (the 0200 bit) is reset in each char.
  169.      */
  170.     if (n == 0)
  171.     {
  172.         ch_fsize = pos;
  173.         bp->data[bp->datasize++] = EOI;
  174.     }
  175.  
  176.     if (!clean_data)
  177.     {
  178.         p = &bp->data[bp->datasize];
  179.         while (--n >= 0)
  180.         {
  181.             *--p &= 0177;
  182.             if (*p == EOI)
  183.                 *p = '@';
  184.         }
  185.     }
  186.  
  187.     found:
  188.     if (buf_head != bp)
  189.     {
  190.         /*
  191.          * Move the buffer to the head of the buffer chain.
  192.          * This orders the buffer chain, most- to least-recently used.
  193.          */
  194.         bp->next->prev = bp->prev;
  195.         bp->prev->next = bp->next;
  196.  
  197.         bp->next = buf_head;
  198.         bp->prev = END_OF_CHAIN;
  199.         buf_head->prev = bp;
  200.         buf_head = bp;
  201.     }
  202.  
  203.     if (ch_offset >= bp->datasize)
  204.         /*
  205.          * After all that, we still don't have enough data.
  206.          * Go back and try again.
  207.          */
  208.         goto read_more;
  209.  
  210.     return (bp->data[ch_offset]);
  211. }
  212.  
  213. #if LOGFILE
  214. /*
  215.  * Close the logfile.
  216.  * If we haven't read all of standard input into it, do that now.
  217.  */
  218.     public void
  219. end_logfile()
  220. {
  221.     static int tried = 0;
  222.  
  223.     if (logfile < 0)
  224.         return;
  225.     if (!tried && ch_fsize == NULL_POSITION)
  226.     {
  227.         tried = 1;
  228.         ierror("finishing logfile");
  229.         while (ch_forw_get() != EOI)
  230.             if (sigs)
  231.                 break;
  232.     }
  233.     close(logfile);
  234.     logfile = -1;
  235. }
  236.  
  237. /*
  238.  * Start a log file AFTER less has already been running.
  239.  * Invoked from the - command; see toggle_option().
  240.  * Write all the existing buffered data to the log file.
  241.  */
  242.     public void
  243. sync_logfile()
  244. {
  245.     register struct buf *bp;
  246.     register int n;
  247.     long block;
  248.     long last_block;
  249.  
  250.     last_block = (last_piped_pos + BUFSIZ - 1) / BUFSIZ;
  251.     for (block = 0;  block <= last_block;  block++)
  252.         for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  253.             if (bp->block == block)
  254.             {
  255.                 n = bp->datasize;
  256.                 if (bp->data[n-1] == EOI)
  257.                     n--;
  258.                 write(logfile, bp->data, n);
  259.                 break;
  260.             }
  261. }
  262.  
  263. #endif
  264.  
  265. /*
  266.  * Determine if a specific block is currently in one of the buffers.
  267.  */
  268.     static int
  269. buffered(block)
  270.     long block;
  271. {
  272.     register struct buf *bp;
  273.  
  274.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  275.         if (bp->block == block)
  276.             return (1);
  277.     return (0);
  278. }
  279.  
  280. /*
  281.  * Seek to a specified position in the file.
  282.  * Return 0 if successful, non-zero if can't seek there.
  283.  */
  284.     public int
  285. ch_seek(pos)
  286.     register POSITION pos;
  287. {
  288.     long new_block;
  289.  
  290.     new_block = pos / BUFSIZ;
  291.     if (!ispipe || pos == last_piped_pos || buffered(new_block))
  292.     {
  293.         /*
  294.          * Set read pointer.
  295.          */
  296.         ch_block = new_block;
  297.         ch_offset = pos % BUFSIZ;
  298.         return (0);
  299.     }
  300.     return (1);
  301. }
  302.  
  303. /*
  304.  * Seek to the end of the file.
  305.  */
  306.     public int
  307. ch_end_seek()
  308. {
  309.     if (!ispipe)
  310.         return (ch_seek(ch_length()));
  311.  
  312.     /*
  313.      * Do it the slow way: read till end of data.
  314.      */
  315.     while (ch_forw_get() != EOI)
  316.         if (sigs)
  317.             return (1);
  318.     return (0);
  319. }
  320.  
  321. /*
  322.  * Seek to the beginning of the file, or as close to it as we can get.
  323.  * We may not be able to seek there if input is a pipe and the
  324.  * beginning of the pipe is no longer buffered.
  325.  */
  326.     public int
  327. ch_beg_seek()
  328. {
  329.     register struct buf *bp, *firstbp;
  330.  
  331.     /*
  332.      * Try a plain ch_seek first.
  333.      */
  334.     if (ch_seek((POSITION)0) == 0)
  335.         return (0);
  336.  
  337.     /*
  338.      * Can't get to position 0.
  339.      * Look thru the buffers for the one closest to position 0.
  340.      */
  341.     firstbp = bp = buf_head;
  342.     if (bp == END_OF_CHAIN)
  343.         return (1);
  344.     while ((bp = bp->next) != END_OF_CHAIN)
  345.         if (bp->block < firstbp->block)
  346.             firstbp = bp;
  347.     ch_block = firstbp->block;
  348.     ch_offset = 0;
  349.     return (0);
  350. }
  351.  
  352. /*
  353.  * Return the length of the file, if known.
  354.  */
  355.     public POSITION
  356. ch_length()
  357. {
  358.     if (ispipe)
  359.         return (ch_fsize);
  360.     return ((POSITION)(lseek(file, (offset_t)0, 2)));
  361. }
  362.  
  363. /*
  364.  * Return the current position in the file.
  365.  */
  366.     public POSITION
  367. ch_tell()
  368. {
  369.     return (ch_block * BUFSIZ + ch_offset);
  370. }
  371.  
  372. /*
  373.  * Get the current char and post-increment the read pointer.
  374.  */
  375.     public int
  376. ch_forw_get()
  377. {
  378.     register int c;
  379.  
  380.     c = ch_get();
  381.     if (c != EOI && ++ch_offset >= BUFSIZ)
  382.     {
  383.         ch_offset = 0;
  384.         ch_block ++;
  385.     }
  386.     return (c);
  387. }
  388.  
  389. /*
  390.  * Pre-decrement the read pointer and get the new current char.
  391.  */
  392.     public int
  393. ch_back_get()
  394. {
  395.     if (--ch_offset < 0)
  396.     {
  397.         if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
  398.         {
  399.             ch_offset = 0;
  400.             return (EOI);
  401.         }
  402.         ch_offset = BUFSIZ - 1;
  403.         ch_block--;
  404.     }
  405.     return (ch_get());
  406. }
  407.  
  408. /*
  409.  * Allocate buffers.
  410.  * Caller wants us to have a total of at least want_nbufs buffers.
  411.  * keep==1 means keep the data in the current buffers;
  412.  * otherwise discard the old data.
  413.  */
  414.     public void
  415. ch_init(want_nbufs, keep)
  416.     int want_nbufs;
  417.     int keep;
  418. {
  419.     register struct buf *bp;
  420.     char message[80];
  421.  
  422.     cbufs = nbufs;
  423.     if (nbufs < want_nbufs && ch_addbuf(want_nbufs - nbufs))
  424.     {
  425.         /*
  426.          * Cannot allocate enough buffers.
  427.          * If we don't have ANY, then quit.
  428.          * Otherwise, just report the error and return.
  429.          */
  430.         sprintf(message, "cannot allocate %d buffers",
  431.             want_nbufs - nbufs);
  432.         error(message);
  433.         if (nbufs == 0)
  434.             quit();
  435.         return;
  436.     }
  437.  
  438.     if (keep)
  439.         return;
  440.  
  441.     /*
  442.      * We don't want to keep the old data,
  443.      * so initialize all the buffers now.
  444.      */
  445.     for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  446.         bp->block = (long)(-1);
  447.     last_piped_pos = (POSITION)0;
  448.     ch_fsize = NULL_POSITION;
  449.     (void) ch_seek((POSITION)0);
  450. }
  451.  
  452. /*
  453.  * Allocate some new buffers.
  454.  * The buffers are added to the tail of the buffer chain.
  455.  */
  456.     static int
  457. ch_addbuf(nnew)
  458.     int nnew;
  459. {
  460.     register struct buf *bp;
  461.     register struct buf *newbufs;
  462.  
  463.     /*
  464.      * We don't have enough buffers.  
  465.      * Allocate some new ones.
  466.      */
  467.     newbufs = (struct buf *) calloc(nnew, sizeof(struct buf));
  468.     if (newbufs == NULL)
  469.         return (1);
  470.  
  471.     /*
  472.      * Initialize the new buffers and link them together.
  473.      * Link them all onto the tail of the buffer list.
  474.      */
  475.     nbufs += nnew;
  476.     cbufs = nbufs;
  477.     for (bp = &newbufs[0];  bp < &newbufs[nnew];  bp++)
  478.     {
  479.         bp->next = bp + 1;
  480.         bp->prev = bp - 1;
  481.         bp->block = (long)(-1);
  482.     }
  483.     newbufs[nnew-1].next = END_OF_CHAIN;
  484.     newbufs[0].prev = buf_tail;
  485.     buf_tail->next = &newbufs[0];
  486.     buf_tail = &newbufs[nnew-1];
  487.     return (0);
  488. }
  489.