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