[BACK]Return to ch.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / less

Diff for /src/usr.bin/less/ch.c between version 1.3 and 1.4

version 1.3, 2001/11/19 19:02:14 version 1.4, 2003/04/13 18:26:25
Line 1 
Line 1 
 /*      $OpenBSD$       */  
   
 /*  /*
  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman   * Copyright (C) 1984-2002  Mark Nudelman
  * All rights reserved.  
  *   *
  * Redistribution and use in source and binary forms, with or without   * You may distribute under the terms of either the GNU General Public
  * modification, are permitted provided that the following conditions   * License or the Less License, as specified in the README file.
  * are met:  
  * 1. Redistributions of source code must retain the above copyright  
  *    notice, this list of conditions and the following disclaimer.  
  * 2. Redistributions in binary form must reproduce the above copyright  
  *    notice in the documentation and/or other materials provided with  
  *    the distribution.  
  *   *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY   * For more information about less, or for information on how to
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE   * contact the author, see the README file.
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR  
  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE  
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR  
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN  
  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
  */   */
   
   
Line 34 
Line 16 
  */   */
   
 #include "less.h"  #include "less.h"
   #if MSDOS_COMPILER==WIN32C
   #include <errno.h>
   #include <windows.h>
   #endif
   
   typedef POSITION BLOCKNUM;
   
 public int ignore_eoi;  public int ignore_eoi;
   
 /*  /*
Line 43 
Line 31 
  * in order from most- to least-recently used.   * in order from most- to least-recently used.
  * The circular list is anchored by the file state "thisfile".   * The circular list is anchored by the file state "thisfile".
  */   */
 #define LBUFSIZE        1024  #define LBUFSIZE        8192
 struct buf {  struct buf {
         struct buf *next, *prev;  /* Must be first to match struct filestate */          struct buf *next, *prev;
         long block;          struct buf *hnext, *hprev;
           BLOCKNUM block;
         unsigned int datasize;          unsigned int datasize;
         unsigned char data[LBUFSIZE];          unsigned char data[LBUFSIZE];
 };  };
   
   struct buflist {
           /* -- Following members must match struct buf */
           struct buf *buf_next, *buf_prev;
           struct buf *buf_hnext, *buf_hprev;
   };
   
 /*  /*
  * The file state is maintained in a filestate structure.   * The file state is maintained in a filestate structure.
  * A pointer to the filestate is kept in the ifile structure.   * A pointer to the filestate is kept in the ifile structure.
  */   */
   #define BUFHASH_SIZE    64
 struct filestate {  struct filestate {
         /* -- Following members must match struct buf */  
         struct buf *buf_next, *buf_prev;          struct buf *buf_next, *buf_prev;
         long buf_block;          struct buflist hashtbl[BUFHASH_SIZE];
         /* -- End of struct buf copy */  
         int file;          int file;
         int flags;          int flags;
         POSITION fpos;          POSITION fpos;
         int nbufs;          int nbufs;
         long block;          BLOCKNUM block;
         int offset;          unsigned int offset;
         POSITION fsize;          POSITION fsize;
 };  };
   
   
 #define END_OF_CHAIN    ((struct buf *)thisfile)  
 #define ch_bufhead      thisfile->buf_next  #define ch_bufhead      thisfile->buf_next
 #define ch_buftail      thisfile->buf_prev  #define ch_buftail      thisfile->buf_prev
 #define ch_nbufs        thisfile->nbufs  #define ch_nbufs        thisfile->nbufs
Line 81 
Line 73 
 #define ch_flags        thisfile->flags  #define ch_flags        thisfile->flags
 #define ch_file         thisfile->file  #define ch_file         thisfile->file
   
   #define END_OF_CHAIN    ((struct buf *)&thisfile->buf_next)
   #define END_OF_HCHAIN(h) ((struct buf *)&thisfile->hashtbl[h])
   #define BUFHASH(blk)    ((blk) & (BUFHASH_SIZE-1))
   
   #define FOR_BUFS_IN_CHAIN(h,bp) \
           for (bp = thisfile->hashtbl[h].buf_hnext;  \
                bp != END_OF_HCHAIN(h);  bp = bp->hnext)
   
   #define HASH_RM(bp) \
           (bp)->hnext->hprev = (bp)->hprev; \
           (bp)->hprev->hnext = (bp)->hnext;
   
   #define HASH_INS(bp,h) \
           (bp)->hnext = thisfile->hashtbl[h].buf_hnext; \
           (bp)->hprev = END_OF_HCHAIN(h); \
           thisfile->hashtbl[h].buf_hnext->hprev = (bp); \
           thisfile->hashtbl[h].buf_hnext = (bp);
   
 static struct filestate *thisfile;  static struct filestate *thisfile;
 static int ch_ungotchar = -1;  static int ch_ungotchar = -1;
   static int maxbufs = -1;
   
 extern int autobuf;  extern int autobuf;
 extern int sigs;  extern int sigs;
 extern int cbufs;  extern int secure;
   extern constant char helpdata[];
   extern constant int size_helpdata;
 extern IFILE curr_ifile;  extern IFILE curr_ifile;
 #if LOGFILE  #if LOGFILE
 extern int logfile;  extern int logfile;
Line 108 
Line 121 
         int          int
 fch_get()  fch_get()
 {  {
         struct buf *bp;          register struct buf *bp;
         int n;          register int n;
         int slept;          register int slept;
           register int h;
         POSITION pos;          POSITION pos;
         POSITION len;          POSITION len;
   
Line 119 
Line 133 
         /*          /*
          * Look for a buffer holding the desired block.           * Look for a buffer holding the desired block.
          */           */
         for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)          h = BUFHASH(ch_block);
           FOR_BUFS_IN_CHAIN(h, bp)
           {
                 if (bp->block == ch_block)                  if (bp->block == ch_block)
                 {                  {
                         if (ch_offset >= bp->datasize)                          if (ch_offset >= bp->datasize)
Line 129 
Line 145 
                                 goto read_more;                                  goto read_more;
                         goto found;                          goto found;
                 }                  }
           }
         /*          /*
          * Block is not in a buffer.           * Block is not in a buffer.
          * Take the least recently used buffer           * Take the least recently used buffer
Line 136 
Line 153 
          * If the LRU buffer has data in it,           * If the LRU buffer has data in it,
          * then maybe allocate a new buffer.           * then maybe allocate a new buffer.
          */           */
         if (ch_buftail == END_OF_CHAIN || ch_buftail->block != (long)(-1))          if (ch_buftail == END_OF_CHAIN || ch_buftail->block != -1)
         {          {
                 /*                  /*
                  * There is no empty buffer to use.                   * There is no empty buffer to use.
Line 145 
Line 162 
                  * 2. We haven't allocated the max buffers for this file yet.                   * 2. We haven't allocated the max buffers for this file yet.
                  */                   */
                 if ((autobuf && !(ch_flags & CH_CANSEEK)) ||                  if ((autobuf && !(ch_flags & CH_CANSEEK)) ||
                     (cbufs == -1 || ch_nbufs < cbufs))                      (maxbufs < 0 || ch_nbufs < maxbufs))
                         if (ch_addbuf())                          if (ch_addbuf())
                                 /*                                  /*
                                  * Allocation failed: turn off autobuf.                                   * Allocation failed: turn off autobuf.
Line 153 
Line 170 
                                 autobuf = OPT_OFF;                                  autobuf = OPT_OFF;
         }          }
         bp = ch_buftail;          bp = ch_buftail;
           HASH_RM(bp); /* Remove from old hash chain. */
         bp->block = ch_block;          bp->block = ch_block;
         bp->datasize = 0;          bp->datasize = 0;
           HASH_INS(bp, h); /* Insert into new hash chain. */
   
     read_more:      read_more:
         pos = (ch_block * LBUFSIZE) + bp->datasize;          pos = (ch_block * LBUFSIZE) + bp->datasize;
Line 187 
Line 206 
          * If we read less than a full block, that's ok.           * If we read less than a full block, that's ok.
          * We use partial block and pick up the rest next time.           * We use partial block and pick up the rest next time.
          */           */
         if (ch_ungotchar == -1)          if (ch_ungotchar != -1)
         {          {
                 n = iread(ch_file, &bp->data[bp->datasize],  
                         (unsigned int)(LBUFSIZE - bp->datasize));  
         } else  
         {  
                 bp->data[bp->datasize] = ch_ungotchar;                  bp->data[bp->datasize] = ch_ungotchar;
                 n = 1;                  n = 1;
                 ch_ungotchar = -1;                  ch_ungotchar = -1;
           } else if (ch_flags & CH_HELPFILE)
           {
                   bp->data[bp->datasize] = helpdata[ch_fpos];
                   n = 1;
           } else
           {
                   n = iread(ch_file, &bp->data[bp->datasize],
                           (unsigned int)(LBUFSIZE - bp->datasize));
         }          }
   
         if (n == READ_INTR)          if (n == READ_INTR)
                 return (EOI);                  return (EOI);
         if (n < 0)          if (n < 0)
         {          {
                 error("read error", NULL_PARG);  #if MSDOS_COMPILER==WIN32C
                 clear_eol();                  if (errno != EPIPE)
   #endif
                   {
                           error("read error", NULL_PARG);
                           clear_eol();
                   }
                 n = 0;                  n = 0;
         }          }
   
Line 211 
Line 239 
         /*          /*
          * If we have a log file, write the new data to it.           * If we have a log file, write the new data to it.
          */           */
         if (logfile >= 0 && n > 0)          if (!secure && logfile >= 0 && n > 0)
                 write(logfile, (char *) &bp->data[bp->datasize], n);                  write(logfile, (char *) &bp->data[bp->datasize], n);
 #endif  #endif
   
Line 232 
Line 260 
                          * Wait a while, then try again.                           * Wait a while, then try again.
                          */                           */
                         if (!slept)                          if (!slept)
                                 ierror("Waiting for data", NULL_PARG);                          {
 #if !MSOFTC                                  PARG parg;
                                   parg.p_string = wait_message();
                                   ierror("%s", &parg);
                           }
   #if !MSDOS_COMPILER
                         sleep(1);                          sleep(1);
   #else
   #if MSDOS_COMPILER==WIN32C
                           Sleep(1000);
 #endif  #endif
   #endif
                         slept = TRUE;                          slept = TRUE;
                 }                  }
                 if (ABORT_SIGS())                  if (sigs)
                         return (EOI);                          return (EOI);
         }          }
   
Line 251 
Line 287 
                  */                   */
                 bp->next->prev = bp->prev;                  bp->next->prev = bp->prev;
                 bp->prev->next = bp->next;                  bp->prev->next = bp->next;
   
                 bp->next = ch_bufhead;                  bp->next = ch_bufhead;
                 bp->prev = END_OF_CHAIN;                  bp->prev = END_OF_CHAIN;
                 ch_bufhead->prev = bp;                  ch_bufhead->prev = bp;
                 ch_bufhead = bp;                  ch_bufhead = bp;
   
                   /*
                    * Move to head of hash chain too.
                    */
                   HASH_RM(bp);
                   HASH_INS(bp, h);
         }          }
   
         if (ch_offset >= bp->datasize)          if (ch_offset >= bp->datasize)
Line 314 
Line 355 
         public void          public void
 sync_logfile()  sync_logfile()
 {  {
         struct buf *bp;          register struct buf *bp;
         int warned = FALSE;          int warned = FALSE;
         long block;          BLOCKNUM block;
         long nblocks;          BLOCKNUM nblocks;
   
         nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;          nblocks = (ch_fpos + LBUFSIZE - 1) / LBUFSIZE;
         for (block = 0;  block < nblocks;  block++)          for (block = 0;  block < nblocks;  block++)
Line 350 
Line 391 
  */   */
         static int          static int
 buffered(block)  buffered(block)
         long block;          BLOCKNUM block;
 {  {
         struct buf *bp;          register struct buf *bp;
           register int h;
   
         for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)          h = BUFHASH(block);
           FOR_BUFS_IN_CHAIN(h, bp)
           {
                 if (bp->block == block)                  if (bp->block == block)
                         return (TRUE);                          return (TRUE);
           }
         return (FALSE);          return (FALSE);
 }  }
   
Line 366 
Line 411 
  */   */
         public int          public int
 ch_seek(pos)  ch_seek(pos)
         POSITION pos;          register POSITION pos;
 {  {
         long new_block;          BLOCKNUM new_block;
         POSITION len;          POSITION len;
   
         len = ch_length();          len = ch_length();
Line 429 
Line 474 
         public int          public int
 ch_beg_seek()  ch_beg_seek()
 {  {
         struct buf *bp, *firstbp;          register struct buf *bp, *firstbp;
   
         /*          /*
          * Try a plain ch_seek first.           * Try a plain ch_seek first.
Line 460 
Line 505 
 {  {
         if (ignore_eoi)          if (ignore_eoi)
                 return (NULL_POSITION);                  return (NULL_POSITION);
           if (ch_flags & CH_HELPFILE)
                   return (size_helpdata);
         return (ch_fsize);          return (ch_fsize);
 }  }
   
 /*  /*
  * Return the current position in the file.   * Return the current position in the file.
  */   */
 #define tellpos(blk,off)   ((POSITION)((((long)(blk)) * LBUFSIZE) + (off)))  
   
         public POSITION          public POSITION
 ch_tell()  ch_tell()
 {  {
         return (tellpos(ch_block, ch_offset));          return (ch_block * LBUFSIZE) + ch_offset;
 }  }
   
 /*  /*
Line 480 
Line 525 
         public int          public int
 ch_forw_get()  ch_forw_get()
 {  {
         int c;          register int c;
   
         c = ch_get();          c = ch_get();
         if (c == EOI)          if (c == EOI)
Line 516 
Line 561 
 }  }
   
 /*  /*
  * Allocate buffers.   * Set max amount of buffer space.
  * Caller wants us to have a total of at least want_nbufs buffers.   * bufspace is in units of 1024 bytes.  -1 mean no limit.
  */   */
         public int          public void
 ch_nbuf(want_nbufs)  ch_setbufspace(bufspace)
         int want_nbufs;          int bufspace;
 {  {
         PARG parg;          if (bufspace < 0)
                   maxbufs = -1;
         while (ch_nbufs < want_nbufs)          else
         {          {
                 if (ch_addbuf())                  maxbufs = ((bufspace * 1024) + LBUFSIZE-1) / LBUFSIZE;
                 {                  if (maxbufs < 1)
                         /*                          maxbufs = 1;
                          * Cannot allocate enough buffers.  
                          * If we don't have ANY, then quit.  
                          * Otherwise, just report the error and return.  
                          */  
                         parg.p_int = want_nbufs - ch_nbufs;  
                         error("Cannot allocate %d buffers", &parg);  
                         if (ch_nbufs == 0)  
                                 quit(QUIT_ERROR);  
                         break;  
                 }  
         }          }
         return (ch_nbufs);  
 }  }
   
 /*  /*
Line 550 
Line 584 
         public void          public void
 ch_flush()  ch_flush()
 {  {
         struct buf *bp;          register struct buf *bp;
   
         if (!(ch_flags & CH_CANSEEK))          if (!(ch_flags & CH_CANSEEK))
         {          {
Line 566 
Line 600 
          * Initialize all the buffers.           * Initialize all the buffers.
          */           */
         for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)          for (bp = ch_bufhead;  bp != END_OF_CHAIN;  bp = bp->next)
                 bp->block = (long)(-1);                  bp->block = -1;
   
         /*          /*
          * Figure out the size of the file, if we can.           * Figure out the size of the file, if we can.
Line 580 
Line 614 
         ch_block = 0; /* ch_fpos / LBUFSIZE; */          ch_block = 0; /* ch_fpos / LBUFSIZE; */
         ch_offset = 0; /* ch_fpos % LBUFSIZE; */          ch_offset = 0; /* ch_fpos % LBUFSIZE; */
   
   #if 1
           /*
            * This is a kludge to workaround a Linux kernel bug: files in
            * /proc have a size of 0 according to fstat() but have readable
            * data.  They are sometimes, but not always, seekable.
            * Force them to be non-seekable here.
            */
           if (ch_fsize == 0)
           {
                   ch_fsize = NULL_POSITION;
                   ch_flags &= ~CH_CANSEEK;
           }
   #endif
   
         if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK)          if (lseek(ch_file, (off_t)0, 0) == BAD_LSEEK)
         {          {
                 /*                  /*
Line 598 
Line 646 
         static int          static int
 ch_addbuf()  ch_addbuf()
 {  {
         struct buf *bp;          register struct buf *bp;
   
         /*          /*
          * Allocate and initialize a new buffer and link it           * Allocate and initialize a new buffer and link it
Line 608 
Line 656 
         if (bp == NULL)          if (bp == NULL)
                 return (1);                  return (1);
         ch_nbufs++;          ch_nbufs++;
         bp->block = (long)(-1);          bp->block = -1;
         bp->next = END_OF_CHAIN;          bp->next = END_OF_CHAIN;
         bp->prev = ch_buftail;          bp->prev = ch_buftail;
         ch_buftail->next = bp;          ch_buftail->next = bp;
         ch_buftail = bp;          ch_buftail = bp;
           HASH_INS(bp, 0);
         return (0);          return (0);
 }  }
   
 /*  /*
    *
    */
           static void
   init_hashtbl()
   {
           register int h;
   
           for (h = 0;  h < BUFHASH_SIZE;  h++)
           {
                   thisfile->hashtbl[h].buf_hnext = END_OF_HCHAIN(h);
                   thisfile->hashtbl[h].buf_hprev = END_OF_HCHAIN(h);
           }
   }
   
   /*
  * Delete all buffers for this file.   * Delete all buffers for this file.
  */   */
         static void          static void
 ch_delbufs()  ch_delbufs()
 {  {
         struct buf *bp;          register struct buf *bp;
   
         while (ch_bufhead != END_OF_CHAIN)          while (ch_bufhead != END_OF_CHAIN)
         {          {
Line 632 
Line 696 
                 free(bp);                  free(bp);
         }          }
         ch_nbufs = 0;          ch_nbufs = 0;
           init_hashtbl();
 }  }
   
 /*  /*
Line 641 
Line 706 
 seekable(f)  seekable(f)
         int f;          int f;
 {  {
   #if MSDOS_COMPILER
           extern int fd0;
           if (f == fd0 && !isatty(fd0))
           {
                   /*
                    * In MS-DOS, pipes are seekable.  Check for
                    * standard input, and pretend it is not seekable.
                    */
                   return (0);
           }
   #endif
         return (lseek(f, (off_t)1, 0) != BAD_LSEEK);          return (lseek(f, (off_t)1, 0) != BAD_LSEEK);
 }  }
   
Line 664 
Line 740 
                 thisfile = (struct filestate *)                  thisfile = (struct filestate *)
                                 calloc(1, sizeof(struct filestate));                                  calloc(1, sizeof(struct filestate));
                 thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN;                  thisfile->buf_next = thisfile->buf_prev = END_OF_CHAIN;
                 thisfile->buf_block = (long)(-1);  
                 thisfile->nbufs = 0;                  thisfile->nbufs = 0;
                 thisfile->flags = 0;                  thisfile->flags = 0;
                 thisfile->fpos = 0;                  thisfile->fpos = 0;
Line 673 
Line 748 
                 thisfile->file = -1;                  thisfile->file = -1;
                 thisfile->fsize = NULL_POSITION;                  thisfile->fsize = NULL_POSITION;
                 ch_flags = flags;                  ch_flags = flags;
                   init_hashtbl();
                 /*                  /*
                  * Try to seek; set CH_CANSEEK if it works.                   * Try to seek; set CH_CANSEEK if it works.
                  */                   */
                 if (seekable(f))                  if ((flags & CH_CANSEEK) && !seekable(f))
                         ch_flags |= CH_CANSEEK;                          ch_flags &= ~CH_CANSEEK;
                 set_filestate(curr_ifile, (void *) thisfile);                  set_filestate(curr_ifile, (void *) thisfile);
         }          }
         if (thisfile->file == -1)          if (thisfile->file == -1)
Line 693 
Line 769 
 {  {
         int keepstate = FALSE;          int keepstate = FALSE;
   
         if (ch_flags & (CH_CANSEEK|CH_POPENED))          if (ch_flags & (CH_CANSEEK|CH_POPENED|CH_HELPFILE))
         {          {
                 /*                  /*
                  * We can seek or re-open, so we don't need to keep buffers.                   * We can seek or re-open, so we don't need to keep buffers.
Line 709 
Line 785 
                  * But don't really close it if it was opened via popen(),                   * But don't really close it if it was opened via popen(),
                  * because pclose() wants to close it.                   * because pclose() wants to close it.
                  */                   */
                 if (!(ch_flags & CH_POPENED))                  if (!(ch_flags & (CH_POPENED|CH_HELPFILE)))
                         close(ch_file);                          close(ch_file);
                 ch_file = -1;                  ch_file = -1;
         } else          } else

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.4