home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / cnews.tar / libc / fgetfln.c < prev    next >
C/C++ Source or Header  |  1993-03-09  |  4KB  |  147 lines

  1. /*
  2.  * fgetfln - read an arbitrarily long line;
  3.  * return a pointer to it, in malloced memory.
  4.  */
  5.  
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <ctype.h>
  9. #include <sys/types.h>
  10. #include <fgetfln.h>
  11. #include <hdbm.h>
  12.  
  13. #define max(a,b) ((a) > (b)? (a): (b))
  14. #define min(a,b) ((a) < (b)? (a): (b))
  15.  
  16. #define islimit(lim) ((lim) >= 0)
  17. #define atlimit(n, lim) (islimit(lim) && (n) >= (lim))
  18.  
  19. /* One could make these arguments, with defaults. */
  20. #define INITLN 90        /* initial allocation per line */
  21. #define GROWLN 200        /* additional allocation size */
  22. #define LARGELN 2048        /* too silly: if was larger, shrink buffer */
  23.  
  24. #define YES 1
  25. #define NO 0
  26.  
  27. /* forwards */
  28. static void setup();
  29.  
  30. struct stream {
  31.     char    *line;        /* currently allocated input line */
  32.     unsigned allocn;    /* bytes currently allocated (in line) */
  33.     unsigned maxrd;        /* upper bound on reads into line */
  34.     int    incr;        /* for allocn */
  35.     char    *segment;    /* start of line segment in "line" */
  36.     char    *morep;        /* last byte possibly containing input */
  37.     int    lastlen;    /* last line length */
  38.     FILE    *iostrm;    /* permanent copy of fp for hashing code */
  39. };
  40. static HASHTABLE *streamtab;
  41.  
  42. /*
  43.  * `fget flagged line' with limit on bytes read.
  44.  * The limit is like fgets's; limit-1 bytes can be read.  -1 means "no limit".
  45.  * The resulting pointer may or may not be malloced and must not be freed.
  46.  * The length of the string is returned via lengthp.
  47.  */
  48. char *
  49. fgetfln(fp, limit, lengthp)
  50. FILE *fp;
  51. register int limit;                /* TODO: make this long? */
  52. int *lengthp;
  53. {
  54.     register int thislen;
  55.     register struct stream *stp;
  56.     HDBMDATUM strkey, strdata;
  57.     static struct stream zstr;
  58.  
  59.     /*
  60.      * map fp to internal state for that stream, notably buffer.
  61.      * Multiple streams can share an fd, so some form of hashing is needed.
  62.      * One would like this mapping to be very fast.
  63.      */
  64.     if (streamtab == NULL)
  65.         streamtab = hdbmcreate(30, (unsigned (*)())NULL);
  66.     strkey.dat_ptr = (char *)&fp;
  67.     strkey.dat_len = sizeof fp;
  68.     strdata = hdbmfetch(streamtab, strkey);
  69.     if (strdata.dat_ptr == NULL) {        /* first use of this fp? */
  70.         stp = (struct stream *)malloc(sizeof *stp);
  71.         if (stp == NULL)
  72.             return NULL;
  73.         *stp = zstr;
  74.         stp->allocn = INITLN;
  75.         stp->incr = GROWLN;
  76.         stp->iostrm = fp;
  77.         strkey.dat_ptr = (char *)&stp->iostrm;    /* permanent location */
  78.         strdata.dat_ptr = (char *)stp;
  79.         strdata.dat_len = sizeof *stp;
  80.         if (hdbmstore(streamtab, strkey, strdata) == 0)
  81.             return NULL;
  82.     }
  83.     stp = (struct stream *)strdata.dat_ptr;
  84.  
  85.     /* initial allocation for an initial segment of a line */
  86.     if (stp->line == NULL) {
  87.         stp->line = malloc(stp->allocn);
  88.         if (stp->line == NULL)
  89.             return NULL;        /* no memory, can't go on */
  90.     }
  91.     stp->segment = stp->line;
  92.     setup(stp, limit);            /* compute maxrd, morep */
  93.  
  94.     /* read the first segment of a line */
  95.     *stp->morep = '\0';            /* mark end of segment */
  96.     if (fgets(stp->segment,
  97.         (int)stp->maxrd - (stp->segment - stp->line), fp) == NULL)
  98.         return NULL;            /* EOF: give up */
  99.  
  100.     /* read more of this line, if it didn't fit */
  101.     while (*stp->morep != '\0' && *stp->morep != '\n' &&
  102.         !atlimit(stp->maxrd, limit)) {
  103.         register int oldalloc = stp->allocn;
  104.  
  105.         /* extend the allocation */
  106.         stp->allocn += stp->incr;
  107.         stp->line = realloc(stp->line, stp->allocn);
  108.         if (stp->line == NULL)
  109.             return NULL;        /* no memory, can't go on */
  110.  
  111.         /* -1 starts on terminating NUL of the prev. segment */
  112.         stp->segment = stp->line + oldalloc - 1;
  113.         setup(stp, limit);        /* recompute maxrd, morep */
  114.  
  115.         /* read the next segment */
  116.         *stp->morep = '\0';
  117.         /*
  118.          * +1 because segment includes terminating NUL of the
  119.          * prev. segment
  120.          */
  121.         if (fgets(stp->segment, stp->incr+1, fp) == NULL)
  122.             return NULL;        /* EOF: give up */
  123.     }
  124.  
  125.     thislen = (stp->segment - stp->line) + strlen(stp->segment);
  126.     if (lengthp != NULL)
  127.         *lengthp = thislen;
  128.  
  129.     /* contract allocation if worthwhile */
  130.     if (stp->lastlen > LARGELN && thislen < LARGELN) {
  131.         stp->allocn = thislen + 1 + stp->incr;
  132.                     /* incr is slop for future lines */
  133.         stp->line = realloc(stp->line, stp->allocn);    /* save space */
  134.     }
  135.     stp->lastlen = thislen;
  136.     return stp->line;
  137. }
  138.  
  139. static void
  140. setup(stp, limit)                /* compute maxrd, morep */
  141. register struct stream *stp;
  142. register int limit;
  143. {
  144.     stp->maxrd = (islimit(limit)? min(stp->allocn, limit): stp->allocn);
  145.     stp->morep = stp->line + stp->maxrd - 2;
  146. }
  147.