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