Reads good & works for me, ok nicm
On Wed, Jul 06, 2011 at 01:02:31PM -0400, Todd C. Miller wrote: > Since cgetnext() iterates over the file it can just pass the entire > record in to getent() so getent() doesn't need to re-parse the file > again to find it. This speeds up cap_mkdb when building termcap.db. > > Other potential speedups include useing stdio in getent() and to > using pfp instead of opening an fd each time. I'll send those out > once this goes in. > > - todd > > Index: lib/libc/gen/getcap.c > =================================================================== > RCS file: /home/cvs/openbsd/src/lib/libc/gen/getcap.c,v > retrieving revision 1.27 > diff -u -r1.27 getcap.c > --- lib/libc/gen/getcap.c 15 May 2006 04:18:19 -0000 1.27 > +++ lib/libc/gen/getcap.c 6 Jul 2011 15:22:09 -0000 > @@ -657,123 +657,141 @@ > * upon returning an entry with more remaining, and -1 if an error occurs. > */ > int > -cgetnext(char **bp, char **db_array) > +cgetnext(char **cap, char **db_array) > { > size_t len; > - int status, done; > - char *line, *np, buf[BSIZE], nbuf[BSIZE]; > + int serrno, status = -1; > + char nbuf[BSIZE]; > + char *record = NULL, *r_end, *rp; > + char buf[BUFSIZ]; > + char *b_end, *bp; > + int c; > u_int dummy; > > if (dbp == NULL) > dbp = db_array; > > - if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { > - (void)cgetclose(); > - return (-1); > + if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) > + goto done; > + > + /* > + * Check if we have an unused top record from cgetset(). > + */ > + if (toprec && !gottoprec) { > + gottoprec = 1; > + goto lookup; > } > + > + /* > + * Allocate first chunk of memory. > + */ > + if ((record = malloc(BFRAG)) == NULL) > + goto done; > + r_end = record + BFRAG; > + > + /* > + * Find the next capability record > + */ > + /* > + * Loop invariants: > + * There is always room for one more character in record. > + * R_end always points just past end of record. > + * Rp always points just past last character in record. > + * B_end always points just past last character in buf. > + * Bp always points at next character in buf. > + */ > + b_end = buf; > + bp = buf; > for (;;) { > - if (toprec && !gottoprec) { > - gottoprec = 1; > - line = toprec; > - } else { > - line = fgetln(pfp, &len); > - if (line == NULL) { > - if (ferror(pfp)) { > - (void)cgetclose(); > - return (-1); > - } else { > + /* > + * Read in a line implementing (\, newline) > + * line continuation. > + */ > + rp = record; > + for (;;) { > + if (bp >= b_end) { > + size_t n; > + > + n = fread(buf, 1, sizeof(buf), pfp); > + if (n == 0) { > + if (ferror(pfp)) > + goto done; > (void)fclose(pfp); > pfp = NULL; > if (*++dbp == NULL) { > - (void)cgetclose(); > - return (0); > + status = 0; > + goto done; > } else if ((pfp = > fopen(*dbp, "r")) == NULL) { > - (void)cgetclose(); > - return (-1); > + goto done; > } else > continue; > } > - } else > - line[len - 1] = '\0';/* XXX - assumes newline */ > - if (len == 1) { > - slash = 0; > - continue; > - } > - if (isspace(*line) || > - *line == ':' || *line == '#' || slash) { > - if (line[len - 2] == '\\') > - slash = 1; > - else > - slash = 0; > - continue; > + b_end = buf + n; > + bp = buf; > } > - if (line[len - 2] == '\\') > - slash = 1; > - else > - slash = 0; > - } > - > > - /* > - * Line points to a name line. > - */ > - done = 0; > - np = nbuf; > - for (;;) { > - len = strcspn(line, ":\\"); > - if (line[len] == ':') { > - done = 1; > - ++len; > - } > - /* copy substring */ > - if (len >= sizeof(nbuf) - (np - nbuf)) { > - (void)cgetclose(); > - return (-1); > + c = *bp++; > + if (c == '\n') { > + if (rp > record && *(rp-1) == '\\') { > + rp--; > + continue; > + } else > + break; > } > - memcpy(np, line, len); > - np += len; > + *rp++ = c; > > - if (done) { > - *np = '\0'; > - break; > - } else { /* name field extends beyond the line */ > - line = fgetln(pfp, &len); > - if (line == NULL) { > - if (ferror(pfp)) { > - (void)cgetclose(); > - return (-1); > - } > - /* Move on to next file. */ > - (void)fclose(pfp); > - pfp = NULL; > - ++dbp; > - /* NUL terminate nbuf. */ > - *np = '\0'; > - break; > - } else > - /* XXX - assumes newline */ > - line[len - 1] = '\0'; > + /* > + * Enforce loop invariant: if no room > + * left in record buffer, try to get > + * some more. > + */ > + if (rp >= r_end) { > + size_t newsize, pos; > + char *nrecord; > + > + pos = rp - record; > + newsize = r_end - record + BFRAG; > + nrecord = realloc(record, newsize); > + if (nrecord == NULL) > + goto done; > + record = nrecord; > + r_end = record + newsize; > + rp = record + pos; > } > } > - len = strcspn(nbuf, "|:"); > - memcpy(buf, nbuf, len); > - buf[len] = '\0'; > + /* loop invariant lets us do this */ > + *rp++ = '\0'; > + > /* > - * XXX > - * Last argument of getent here should be nbuf if we want true > - * sequential access in the case of duplicates. > - * With NULL, getent will return the first entry found > - * rather than the duplicate entry record. This is a > - * matter of semantics that should be resolved. > + * If not blank or comment, set toprec and topreclen so > + * getent() doesn't have to re-parse the file to find it. > */ > - status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); > - if (status == -2 || status == -3) > - (void)cgetclose(); > - > - return (status + 1); > + if (*record != '\0' && *record != '#') { > + /* Rewind to end of record */ > + fseeko(pfp, (off_t)(bp - b_end), SEEK_CUR); > + toprec = record; > + topreclen = rp - record; > + gottoprec = 1; > + break; > + } > } > - /* NOTREACHED */ > +lookup: > + /* extract name from record */ > + len = strcspn(record, "|:"); > + memcpy(nbuf, record, len); > + nbuf[len] = '\0'; > + > + /* return value of getent() is one less than cgetnext() */ > + status = getent(cap, &dummy, db_array, -1, nbuf, 0, NULL) + 1; > +done: > + serrno = errno; > + free(record); > + if (status <= 0) > + (void)cgetclose(); > + errno = serrno; > + > + return (status); > } > > /*