home *** CD-ROM | disk | FTP | other *** search
/ Frozen Fish 1: Amiga / FrozenFish-Apr94.iso / bbs / alib / d9xx / d902 / less.lha / Less / Source / source.lha / ch.c next >
C/C++ Source or Header  |  1993-01-21  |  13KB  |  496 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.  
  10. /* Prototypes for functions defined in ch.c */
  11.  
  12. static int fch_get __PROTO((void));
  13. static int buffered __PROTO((long block));
  14.  
  15.  
  16. public int file = -1;   /* File descriptor of the input file */
  17.  
  18. /*
  19.  * Pool of buffers holding the most recently used blocks of the input file.
  20.  */
  21. #define BUFSIZ  1024
  22. struct buf {
  23.         struct buf *next, *prev;
  24.         long block;
  25.         char data[BUFSIZ];
  26. };
  27. static struct buf *bufs = NULL;
  28. public int nbufs;
  29.  
  30. /*
  31.  * The buffer pool is kept as a doubly-linked circular list,
  32.  * in order from most- to least-recently used.
  33.  * The circular list is anchored by buf_anchor.
  34.  */
  35. static struct {
  36.         struct buf *next, *prev;
  37. } buf_anchor;
  38. #define END_OF_CHAIN    ((struct buf *)&buf_anchor)
  39. #define buf_head        buf_anchor.next
  40. #define buf_tail        buf_anchor.prev
  41.  
  42. /*
  43.  * If we fail to allocate enough memory for buffers, we try to limp
  44.  * along with a minimum number of buffers.
  45.  */
  46. #define DEF_NBUFS       2       /* Minimum number of buffers */
  47.  
  48. #ifndef AMIGA
  49. extern int clean_data;
  50. #endif
  51. extern int ispipe;
  52. extern int sigs;
  53.  
  54. #if LOGFILE
  55. extern int logfile;
  56. #endif
  57.  
  58. /*
  59.  * Current position in file.
  60.  * Stored as a block number and an offset into the block.
  61.  */
  62. static long ch_block;
  63. static int ch_offset;
  64.  
  65. /*
  66.  * Length of file, needed if input is a pipe.
  67.  */
  68. static POSITION ch_fsize;
  69.  
  70. /*
  71.  * Largest block number read if input is standard input (a pipe).
  72.  */
  73. static long last_piped_block;
  74.  
  75. /*
  76.  * Get the character pointed to by the read pointer.
  77.  * ch_get() is a macro which is more efficient to call
  78.  * than fch_get (the function), in the usual case
  79.  * that the block desired is at the head of the chain.
  80.  */
  81. #define ch_get()   ((buf_head->block == ch_block) ? \
  82.                         buf_head->data[ch_offset] : fch_get())
  83.  
  84. #ifdef __STDC__
  85. static int fch_get (void)
  86. #else
  87.         static int
  88. fch_get()
  89. #endif
  90. {
  91.         register struct buf *bp;
  92.         register int n;
  93.         register int end;
  94.         POSITION pos;
  95.  
  96.         /*
  97.          * Look for a buffer holding the desired block.
  98.          */
  99.         for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  100.                 if (bp->block == ch_block)
  101.                         goto found;
  102.         /*
  103.          * Block is not in a buffer.
  104.          * Take the least recently used buffer
  105.          * and read the desired block into it.
  106.          */
  107.         bp = buf_tail;
  108.         bp->block = ch_block;
  109.         pos = ch_block * BUFSIZ;
  110.         if (ispipe)
  111.         {
  112.                 /*
  113.                  * The block requested should be one more than
  114.                  * the last block read.
  115.                  */
  116.                 if (ch_block != ++last_piped_block)
  117.                 {
  118.                         /* This "should not happen". */
  119.                         char message[80];
  120.                         sprintf(message, "Pipe error: last %ld, want %ld\n",
  121.                                 (long)last_piped_block-1, (long)ch_block);
  122.                         error(message);
  123.                         quit();
  124.                 }
  125.         } else
  126.                 lseek(file, pos, 0);
  127.  
  128.         /*
  129.          * Read the block.  This may take several reads if the input
  130.          * is coming from standard input, due to the nature of pipes.
  131.          */
  132.         end = 0;
  133.         while ((n = read(file, &bp->data[end], BUFSIZ-end)) > 0)
  134.                 if ((end += n) >= BUFSIZ)
  135.                         break;
  136.  
  137.         if (n < 0)
  138.         {
  139.                 error("read error");
  140.                 quit();
  141.         }
  142.  
  143. #if LOGFILE
  144.         /*
  145.          * If we have a log file, write this block to it.
  146.          */
  147.         if (logfile >= 0 && end > 0)
  148.                 write(logfile, bp->data, end);
  149. #endif
  150.  
  151.         /*
  152.          * Set an EOF marker in the buffered data itself.
  153.          * Then ensure the data is "clean": there are no
  154.          * extra EOF chars in the data and that the "meta"
  155.          * bit (the 0200 bit) is reset in each char.
  156.          */
  157.         if (end < BUFSIZ)
  158.         {
  159.                 ch_fsize = pos + end;
  160.                 bp->data[end] = EOF;
  161.         }
  162.  
  163. #ifndef AMIGA
  164.         if (!clean_data)
  165. #endif
  166.                 while (--end >= 0)
  167.                 {
  168. #ifdef EIGHTBIT
  169.                         /* We handle all 8-bit characters, except these,
  170.                            which are flags for special printing
  171.                          */
  172.                         switch ( bp->data[end] )
  173.                         {
  174.                         case UL_CHAR:
  175.                         case UE_CHAR:
  176.                         case BO_CHAR:
  177.                         case BE_CHAR:
  178.                         case IT_CHAR:
  179.                         case IE_CHAR:
  180.                         case NV_CHAR:
  181.                         case NE_CHAR:
  182.                                 bp->data[end] = '\177';
  183.                         default:
  184.                                 break;
  185.                         }
  186. #else
  187. #ifdef ANSIGR
  188.                         if ( (unsigned char)(bp->data[end]) != 0x9b )
  189. #endif
  190.                         bp->data[end] &= 0177;
  191. #endif
  192.                         if (bp->data[end] == EOF)
  193.                                 bp->data[end] = '@';
  194.                 }
  195.  
  196.     found:
  197.         /* if (buf_head != bp) {this is guaranteed by the ch_get macro} */
  198.         {
  199.                 /*
  200.                  * Move the buffer to the head of the buffer chain.
  201.                  * This orders the buffer chain, most- to least-recently used.
  202.                  */
  203.                 bp->next->prev = bp->prev;
  204.                 bp->prev->next = bp->next;
  205.  
  206.                 bp->next = buf_head;
  207.                 bp->prev = END_OF_CHAIN;
  208.                 buf_head->prev = bp;
  209.                 buf_head = bp;
  210.         }
  211.         return (int)(bp->data[ch_offset]);
  212. }
  213.  
  214. #if LOGFILE
  215. /*
  216.  * Close the logfile.
  217.  * If we haven't read all of standard input into it, do that now.
  218.  */
  219.         public void
  220. end_logfile()
  221. {
  222.         static int tried;
  223.  
  224.         if (logfile < 0)
  225.                 return;
  226.         if (!tried && ch_fsize == NULL_POSITION)
  227.         {
  228.                 tried = 1;
  229.                 lower_left();
  230.                 clear_eol();
  231.                 so_enter();
  232.                 putstr("finishing logfile... (interrupt to abort)");
  233.                 so_exit();
  234.                 flush();
  235.                 while (sigs == 0 && ch_forw_get() != EOF)
  236.                         ;
  237.         }
  238.         close(logfile);
  239.         logfile = -1;
  240. }
  241. #endif
  242.  
  243. /*
  244.  * Determine if a specific block is currently in one of the buffers.
  245.  */
  246. #ifdef __STDC__
  247. static int buffered (long block)
  248. #else
  249.         static int
  250. buffered(block)
  251.         long block;
  252. #endif
  253. {
  254.         register struct buf *bp;
  255.  
  256.         for (bp = buf_head;  bp != END_OF_CHAIN;  bp = bp->next)
  257.                 if (bp->block == block)
  258.                         return (1);
  259.         return (0);
  260. }
  261.  
  262. /*
  263.  * Seek to a specified position in the file.
  264.  * Return 0 if successful, non-zero if can't seek there.
  265.  */
  266. #ifdef __STDC__
  267. int ch_seek (register POSITION pos)
  268. #else
  269.         public int
  270. ch_seek(pos)
  271.         register POSITION pos;
  272. #endif
  273. {
  274.         long new_block;
  275.  
  276.         new_block = pos / BUFSIZ;
  277.         if (!ispipe || new_block == last_piped_block + 1 || buffered(new_block))
  278.         {
  279.                 /*
  280.                  * Set read pointer.
  281.                  */
  282.                 ch_block = new_block;
  283.                 ch_offset = pos % BUFSIZ;
  284.                 return (0);
  285.         }
  286.         return (1);
  287. }
  288.  
  289. /*
  290.  * Seek to the end of the file.
  291.  */
  292. #ifdef __STDC__
  293. int ch_end_seek (void)
  294. #else
  295.         public int
  296. ch_end_seek()
  297. #endif
  298. {
  299.         if (ispipe)
  300.         {
  301.                 /*
  302.                  * Do it the slow way: read till end of data.
  303.                  */
  304.                 while (ch_forw_get() != EOF)
  305.                         ;
  306.         } else
  307.         {
  308.                 (void) ch_seek((POSITION)(lseek(file, (offset_t)0, 2)));
  309.         }
  310.         return (0);
  311. }
  312.  
  313. /*
  314.  * Seek to the beginning of the file, or as close to it as we can get.
  315.  * We may not be able to seek there if input is a pipe and the
  316.  * beginning of the pipe is no longer buffered.
  317.  */
  318. #ifdef __STDC__
  319. int ch_beg_seek (void)
  320. #else
  321.         public int
  322. ch_beg_seek()
  323. #endif
  324. {
  325.         register struct buf *bp, *firstbp;
  326.  
  327.         /*
  328.          * Try a plain ch_seek first.
  329.          */
  330.         if (ch_seek((POSITION)0) == 0)
  331.                 return (0);
  332.  
  333.         /*
  334.          * Can't get to position 0.
  335.          * Look thru the buffers for the one closest to position 0.
  336.          */
  337.         firstbp = bp = buf_head;
  338.         if (bp == END_OF_CHAIN)
  339.                 return (1);
  340.         while ((bp = bp->next) != END_OF_CHAIN)
  341.                 if (bp->block < firstbp->block)
  342.                         firstbp = bp;
  343.         ch_block = firstbp->block;
  344.         ch_offset = 0;
  345.         return (0);
  346. }
  347.  
  348. /*
  349.  * Return the length of the file, if known.
  350.  */
  351. #ifdef __STDC__
  352. POSITION ch_length (void)
  353. #else
  354.         public POSITION
  355. ch_length()
  356. #endif
  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. #ifdef __STDC__
  367. POSITION ch_tell (void)
  368. #else
  369.         public POSITION
  370. ch_tell()
  371. #endif
  372. {
  373.         return (ch_block * BUFSIZ + ch_offset);
  374. }
  375.  
  376. /*
  377.  * Get the current char and post-increment the read pointer.
  378.  */
  379. #ifdef __STDC__
  380. int ch_forw_get (void)
  381. #else
  382.         public int
  383. ch_forw_get()
  384. #endif
  385. {
  386.         register int c;
  387.  
  388.         c = ch_get();
  389.         if (c != EOF && ++ch_offset >= BUFSIZ)
  390.         {
  391.                 ch_offset = 0;
  392.                 ch_block ++;
  393.         }
  394.         return (c);
  395. }
  396.  
  397. /*
  398.  * Pre-decrement the read pointer and get the new current char.
  399.  */
  400. #ifdef __STDC__
  401. int ch_back_get (void)
  402. #else
  403.         public int
  404. ch_back_get()
  405. #endif
  406. {
  407.         register int c;
  408.  
  409.         if (--ch_offset < 0)
  410.         {
  411.                 if (ch_block <= 0 || (ispipe && !buffered(ch_block-1)))
  412.                 {
  413.                         ch_offset = 0;
  414.                         return (EOF);
  415.                 }
  416.                 ch_offset = BUFSIZ - 1;
  417.                 ch_block--;
  418.         }
  419.         c = ch_get();
  420.         return (c);
  421. }
  422.  
  423. /*
  424.  * Initialize the buffer pool to all empty.
  425.  * Caller suggests that we use want_nbufs buffers.
  426.  */
  427. #ifdef __STDC__
  428. void ch_init (int want_nbufs)
  429. #else
  430.         public void
  431. ch_init(want_nbufs)
  432.         int want_nbufs;
  433. #endif
  434. {
  435.         register struct buf *bp;
  436. #ifndef AMIGA
  437.         char *calloc();
  438. #endif
  439.  
  440.         if (nbufs < want_nbufs)
  441.         {
  442.                 /*
  443.                  * We don't have enough buffers.
  444.                  * Free what we have (if any) and allocate some new ones.
  445.                  */
  446.                 if (bufs != NULL)
  447.                         free((char *)bufs);
  448.                 bufs = (struct buf *) calloc(want_nbufs, sizeof(struct buf));
  449.                 nbufs = want_nbufs;
  450.                 if (bufs == NULL)
  451.                 {
  452.                         /*
  453.                          * Couldn't get that many.
  454.                          * Try for a small default number of buffers.
  455.                          */
  456.                         char message[80];
  457.                         sprintf(message,
  458.                           "Cannot allocate %ld buffers.  Using %ld buffers.",
  459.                           nbufs, DEF_NBUFS);
  460.                         error(message);
  461.                         bufs = (struct buf *) calloc(DEF_NBUFS, sizeof(struct buf));
  462.                         nbufs = DEF_NBUFS;
  463.                         if (bufs == NULL)
  464.                         {
  465.                                 /*
  466.                                  * Couldn't even get the smaller number of bufs.
  467.                                  * Something is wrong here, don't continue.
  468.                                  */
  469.                                 sprintf(message,
  470.                                 "Cannot even allocate %ld buffers!  Quitting.",
  471.                                   DEF_NBUFS);
  472.                                 error(message);
  473.                                 quit();
  474.                                 /*NOTREACHED*/
  475.                         }
  476.                 }
  477.         }
  478.  
  479.         /*
  480.          * Initialize the buffers to empty.
  481.          * Set up the circular list.
  482.          */
  483.         for (bp = &bufs[0];  bp < &bufs[nbufs];  bp++)
  484.         {
  485.                 bp->next = bp + 1;
  486.                 bp->prev = bp - 1;
  487.                 bp->block = (long)(-1);
  488.         }
  489.         bufs[0].prev = bufs[nbufs-1].next = END_OF_CHAIN;
  490.         buf_head = &bufs[0];
  491.         buf_tail = &bufs[nbufs-1];
  492.         last_piped_block = -1;
  493.         ch_fsize = NULL_POSITION;
  494.         (void) ch_seek((POSITION)0);
  495. }
  496.