home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / libstdio / fgets.c next >
C/C++ Source or Header  |  1992-11-07  |  3KB  |  119 lines

  1. #include <stdio.h>
  2. #include <string.h>            /* the ANSI one, for memcpy */
  3. #ifndef PTR_TYPE
  4. #define    PTR_TYPE    char *        /* type of _ptr in stdio.h */
  5. #endif
  6.  
  7. #define THRESHOLD 12            /* memcpy vs in-line threshold */
  8.  
  9. char *
  10. fgets(s, lim, fp)            /* optimised version */
  11. char *s;
  12. int lim;
  13. register FILE *fp;
  14. {
  15.     char *rets = s;            /* normal return by default */
  16.  
  17.     --lim;                /* leave room for terminating null */
  18.     while (lim > 0) {        /* room left in s */
  19.         int origbytesleft;
  20.         char *nlp = NULL;    /* points at newline */
  21.  
  22.         /*
  23.          * Find next newline (if any) by running through the
  24.          * stdio buffer until it runs out or a newline is seen.
  25.          * This dominates the running time for long lines.
  26.          */
  27.         {
  28.             register char *bp = (char *)fp->_ptr;
  29.             register int loops;
  30.             /* bytes to the end of s or the stdio buffer */
  31.             register int bytesleft = fp->_cnt;    /* to EOB */
  32.  
  33.             if (bytesleft < 0)
  34.                 bytesleft = 0;
  35.             if (bytesleft > lim)    /* buffer longer than s */
  36.                 bytesleft = lim; /* only copy to s's end */
  37.             origbytesleft = bytesleft;
  38.  
  39.             /*
  40.              * This code uses Duff's Device (tm Tom Duff)
  41.              * to unroll the newline recogniser:
  42.              * for (++bytesleft; --bytesleft > 0; )
  43.              *    if (*bp++ == '\n') {
  44.              *        nlp = bp;    # NB points after \n
  45.              *        break;
  46.              *    }
  47.              * Sorry the code is so ugly.
  48.              */
  49.             if (bytesleft > 0) {
  50.                 /*
  51.                  * warning: this will need to be changed for
  52.                  * non-binary machines, if they exist.
  53.                  */
  54.                 loops = (bytesleft+8-1) >> 3;    /* /8 round up */
  55.  
  56.                 switch (bytesleft&(8-1)) {    /* %8 */
  57.                 case 0: do {
  58. #define SPOTNL if (*bp++ == '\n') { nlp = bp; break; }
  59.                         SPOTNL;
  60.                     case 7:    SPOTNL;
  61.                     case 6:    SPOTNL;
  62.                     case 5:    SPOTNL;
  63.                     case 4:    SPOTNL;
  64.                     case 3:    SPOTNL;
  65.                     case 2:    SPOTNL;
  66.                     case 1:    SPOTNL;
  67.                     } while (--loops > 0);
  68.                 }
  69.             }
  70.         }
  71.         /*
  72.          * If no newline was seen, copy remaining bytes from stdio
  73.          * buffer; else copy up to and including the newline.
  74.          * Adjust counts, then copy the bytes & adjust pointers.
  75.          * This dominates the running time for short lines.
  76.          */
  77.         {
  78.             register int copy = (nlp == NULL?
  79.                 origbytesleft: nlp - (char *)fp->_ptr);
  80.  
  81.             lim -= copy;
  82.             fp->_cnt -= copy;
  83.             if (copy < THRESHOLD) {
  84.                 register char *rs = s, *bp = (char *)fp->_ptr;
  85.  
  86.                 for (++copy; --copy > 0; )
  87.                     *rs++ = *bp++;
  88.                 s = rs;
  89.                 fp->_ptr = (PTR_TYPE)bp;
  90.             } else {
  91.                 memcpy(s, (char *)fp->_ptr, copy);
  92.                 s += copy;
  93.                 fp->_ptr += copy;
  94.             }
  95.         }
  96.         /*
  97.          * Quit if we saw a newline or "s" is full,
  98.          * else refill the stdio buffer and go again.
  99.          */
  100.         if (nlp != NULL || lim <= 0)
  101.             break;
  102.         else if (fp->_cnt <= 0) {        /* buffer empty */
  103.             register int c = getc(fp);    /* fill buffer */
  104.  
  105.             if (c == EOF) {
  106.                 if (s == rets)        /* s is empty */
  107.                     rets = NULL;
  108.                 break;            /* EOF return */
  109.             } else {
  110.                 if ((*s++ = c) == '\n')
  111.                     break;        /* newline return */
  112.                 --lim;
  113.             }
  114.         }
  115.     }
  116.     *s = '\0';    /* terminate s */
  117.     return rets;
  118. }
  119.