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

Reply via email to