home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD1.bin / useful / text / show / 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 buf