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);
> }
>
> /*