home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / CNews / Source / libc / fgetfln.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-04-17  |  4.1 KB  |  144 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. };
  39. static HASHTABLE *streamtab;
  40.  
  41. /*
  42.  * `fget flagged line' with limit on bytes read.
  43.  * The limit is like fgets's; limit-1 bytes can be read.  -1 means "no limit".
  44.  * The resulting pointer may or may not be malloced and must not be freed.
  45.  * The length of the string is returned via lengthp.
  46.  */
  47. char *
  48. fgetfln(fp, limit, lengthp)
  49. FILE *fp;
  50. register int limit;                /* TODO: make this long? */
  51. int *lengthp;
  52. {
  53.     register int thislen, idx;
  54.     register struct stream *stp;
  55.     HDBMDATUM strkey, strdata;
  56.     static struct stream zstr;
  57.  
  58.     /*
  59.      * map fp to internal state for that stream, notably buffer.
  60.      * Multiple streams can share an fd, so some form of hashing is needed.
  61.      * One would like this mapping to be very fast.
  62.      */
  63.     if (streamtab == NULL)
  64.         streamtab = hdbmcreate(30, (unsigned (*)())NULL);
  65.     strkey.dat_ptr = (char *)&fp;
  66.     strkey.dat_len = sizeof fp;
  67.     strdata = hdbmfetch(streamtab, strkey);
  68.     if (strdata.dat_ptr == NULL) {        /* first use of this fp? */
  69.         stp = (struct stream *)malloc(sizeof *stp);
  70.         if (stp == NULL)
  71.             return NULL;
  72.         strdata.dat_ptr = (char *)stp;
  73.         strdata.dat_len = sizeof *stp;
  74.         *stp = zstr;
  75.         stp->allocn = INITLN;
  76.         stp->incr = GROWLN;
  77.         if (hdbmstore(streamtab, strkey, strdata) == 0)
  78.             return NULL;
  79.     }
  80.     stp = (struct stream *)strdata.dat_ptr;
  81.  
  82.     /* initial allocation for an initial segment of a line */
  83.     if (stp->line == NULL) {
  84.         stp->line = malloc(stp->allocn);
  85.         if (stp->line == NULL)
  86.             return NULL;        /* no memory, can't go on */
  87.     }
  88.     stp->segment = stp->line;
  89.     setup(stp, limit);            /* compute maxrd, morep */
  90.  
  91.     /* read the first segment of a line */
  92.     *stp->morep = '\0';            /* mark end of segment */
  93.     if (fgets(stp->segment,
  94.         (int)stp->maxrd - (stp->segment - stp->line), fp) == NULL)
  95.         return NULL;            /* EOF: give up */
  96.  
  97.     /* read more of this line, if it didn't fit */
  98.     while (*stp->morep != '\0' && *stp->morep != '\n' &&
  99.         !atlimit(stp->maxrd, limit)) {
  100.         register int oldalloc = stp->allocn;
  101.  
  102.         /* extend the allocation */
  103.         stp->allocn += stp->incr;
  104.         stp->line = realloc(stp->line, stp->allocn);
  105.         if (stp->line == NULL)
  106.             return NULL;        /* no memory, can't go on */
  107.  
  108.         /* -1 starts on terminating NUL of the prev. segment */
  109.         stp->segment = stp->line + oldalloc - 1;
  110.         setup(stp, limit);        /* recompute maxrd, morep */
  111.  
  112.         /* read the next segment */
  113.         *stp->morep = '\0';
  114.         /*
  115.          * +1 because segment includes terminating NUL of the
  116.          * prev. segment
  117.          */
  118.         if (fgets(stp->segment, stp->incr+1, fp) == NULL)
  119.             return NULL;        /* EOF: give up */
  120.     }
  121.  
  122.     thislen = (stp->segment - stp->line) + strlen(stp->segment);
  123.     if (lengthp != NULL)
  124.         *lengthp = thislen;
  125.  
  126.     /* contract allocation if worthwhile */
  127.     if (stp->lastlen > LARGELN && thislen < LARGELN) {
  128.         stp->allocn = thislen + 1 + stp->incr;
  129.                     /* incr is slop for future lines */
  130.         stp->line = realloc(stp->line, stp->allocn);    /* save space */
  131.     }
  132.     stp->lastlen = thislen;
  133.     return stp->line;
  134. }
  135.  
  136. static void
  137. setup(stp, limit)                /* compute maxrd, morep */
  138. register struct stream *stp;
  139. register int limit;
  140. {
  141.     stp->maxrd = (islimit(limit)? min(stp->allocn, limit): stp->allocn);
  142.     stp->morep = stp->line + stp->maxrd - 2;
  143. }
  144.