Annotation of src/usr.bin/info_mkdb/getinfo.c, Revision 1.1
1.1 ! tholo 1: /* $OpenBSD$ */
! 2:
! 3: /*-
! 4: * Copyright (c) 1996 SigmaSoft, Th. Lockert <tholo@sigmasoft.com>
! 5: * All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 3. All advertising materials mentioning features or use of this software
! 16: * must display the following acknowledgement:
! 17: * This product includes software developed by SigmaSoft, Th. Lockert.
! 18: * 4. The name of the author may not be used to endorse or promote products
! 19: * derived from this software without specific prior written permission.
! 20: *
! 21: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! 22: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
! 23: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
! 24: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 25: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 26: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! 27: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! 28: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! 29: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! 30: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 31: */
! 32:
! 33: #ifndef lint
! 34: static char rcsid[] = "$OpenBSD$";
! 35: #endif /* not lint */
! 36:
! 37: #include <sys/types.h>
! 38:
! 39: #include <ctype.h>
! 40: #include <db.h>
! 41: #include <errno.h>
! 42: #include <fcntl.h>
! 43: #include <limits.h>
! 44: #include <stdio.h>
! 45: #include <stdlib.h>
! 46: #include <string.h>
! 47: #include <unistd.h>
! 48:
! 49: #define BFRAG 1024
! 50: #define BSIZE 1024
! 51: #define ESC ('[' & 037) /* ASCII ESC */
! 52: #define MAX_RECURSION 32 /* maximum getent recursion */
! 53: #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
! 54:
! 55: #define RECOK (char)0
! 56: #define TCERR (char)1
! 57: #define SHADOW (char)2
! 58:
! 59: static int getent __P((char **, u_int *, char **, int, char *, int));
! 60: static char *igetcap __P((char *, char *, int));
! 61: static int igetmatch __P((char *, char *));
! 62: static int igetclose __P((void));
! 63:
! 64: int igetnext __P((char **, char **));
! 65:
! 66: /*
! 67: * Cgetcap searches the capability record buf for the capability cap with
! 68: * type `type'. A pointer to the value of cap is returned on success, NULL
! 69: * if the requested capability couldn't be found.
! 70: *
! 71: * Specifying a type of ',' means that nothing should follow cap (,cap,).
! 72: * In this case a pointer to the terminating ',' or NUL will be returned if
! 73: * cap is found.
! 74: *
! 75: * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
! 76: * return NULL.
! 77: */
! 78: static char *
! 79: igetcap(buf, cap, type)
! 80: char *buf, *cap;
! 81: int type;
! 82: {
! 83: register char *bp, *cp;
! 84:
! 85: bp = buf;
! 86: for (;;) {
! 87: /*
! 88: * Skip past the current capability field - it's either the
! 89: * name field if this is the first time through the loop, or
! 90: * the remainder of a field whose name failed to match cap.
! 91: */
! 92: for (;;)
! 93: if (*bp == '\0')
! 94: return (NULL);
! 95: else
! 96: if (*bp++ == ',')
! 97: break;
! 98:
! 99: /*
! 100: * Try to match (cap, type) in buf.
! 101: */
! 102: for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
! 103: continue;
! 104: if (*cp != '\0')
! 105: continue;
! 106: if (*bp == '@')
! 107: return (NULL);
! 108: if (type == ',') {
! 109: if (*bp != '\0' && *bp != ',')
! 110: continue;
! 111: return(bp);
! 112: }
! 113: if (*bp != type)
! 114: continue;
! 115: bp++;
! 116: return (*bp == '@' ? NULL : bp);
! 117: }
! 118: /* NOTREACHED */
! 119: }
! 120:
! 121: /*
! 122: * Getent implements the functions of igetent. If fd is non-negative,
! 123: * *db_array has already been opened and fd is the open file descriptor. We
! 124: * do this to save time and avoid using up file descriptors for use=
! 125: * recursions.
! 126: *
! 127: * Getent returns the same success/failure codes as igetent. On success, a
! 128: * pointer to a malloc'ed capability record with all use= capabilities fully
! 129: * expanded and its length (not including trailing ASCII NUL) are left in
! 130: * *cap and *len.
! 131: *
! 132: * Basic algorithm:
! 133: * + Allocate memory incrementally as needed in chunks of size BFRAG
! 134: * for capability buffer.
! 135: * + Recurse for each use=name and interpolate result. Stop when all
! 136: * names interpolated, a name can't be found, or depth exceeds
! 137: * MAX_RECURSION.
! 138: */
! 139: static int
! 140: getent(cap, len, db_array, fd, name, depth)
! 141: char **cap, **db_array, *name;
! 142: u_int *len;
! 143: int fd, depth;
! 144: {
! 145: register char *r_end, *rp, **db_p;
! 146: int myfd, eof, foundit;
! 147: char *record;
! 148: int tc_not_resolved;
! 149:
! 150: /*
! 151: * Return with ``loop detected'' error if we've recursed more than
! 152: * MAX_RECURSION times.
! 153: */
! 154: if (depth > MAX_RECURSION)
! 155: return (-3);
! 156:
! 157: /*
! 158: * Allocate first chunk of memory.
! 159: */
! 160: if ((record = malloc(BFRAG)) == NULL) {
! 161: errno = ENOMEM;
! 162: return (-2);
! 163: }
! 164: r_end = record + BFRAG;
! 165: foundit = 0;
! 166: rp = NULL;
! 167: myfd = -1;
! 168: /*
! 169: * Loop through database array until finding the record.
! 170: */
! 171:
! 172: for (db_p = db_array; *db_p != NULL; db_p++) {
! 173: eof = 0;
! 174:
! 175: /*
! 176: * Open database if not already open.
! 177: */
! 178:
! 179: if (fd >= 0) {
! 180: (void)lseek(fd, (off_t)0, L_SET);
! 181: myfd = 0;
! 182: } else {
! 183: fd = open(*db_p, O_RDONLY, 0);
! 184: if (fd < 0) {
! 185: /* No error on unfound file. */
! 186: continue;
! 187: }
! 188: myfd = 1;
! 189: }
! 190: /*
! 191: * Find the requested capability record ...
! 192: */
! 193: {
! 194: char buf[BUFSIZ];
! 195: register char *b_end, *bp;
! 196: register int c;
! 197:
! 198: /*
! 199: * Loop invariants:
! 200: * There is always room for one more character in record.
! 201: * R_end always points just past end of record.
! 202: * Rp always points just past last character in record.
! 203: * B_end always points just past last character in buf.
! 204: * Bp always points at next character in buf.
! 205: */
! 206: b_end = buf;
! 207: bp = buf;
! 208: for (;;) {
! 209:
! 210: /*
! 211: * Read in a line implementing (\, newline)
! 212: * line continuation.
! 213: */
! 214: rp = record;
! 215: for (;;) {
! 216: if (bp >= b_end) {
! 217: int n;
! 218:
! 219: n = read(fd, buf, sizeof(buf));
! 220: if (n <= 0) {
! 221: if (myfd)
! 222: (void)close(fd);
! 223: if (n < 0) {
! 224: free(record);
! 225: return (-2);
! 226: } else {
! 227: fd = -1;
! 228: eof = 1;
! 229: break;
! 230: }
! 231: }
! 232: b_end = buf+n;
! 233: bp = buf;
! 234: }
! 235:
! 236: c = *bp++;
! 237: if (c == '\n') {
! 238: if (bp >= b_end) {
! 239: int n;
! 240:
! 241: n = read(fd, buf, sizeof(buf));
! 242: if (n <= 0) {
! 243: if (myfd)
! 244: (void)close(fd);
! 245: if (n < 0) {
! 246: free(record);
! 247: return (-2);
! 248: } else {
! 249: fd = -1;
! 250: eof = 1;
! 251: break;
! 252: }
! 253: }
! 254: b_end = buf+n;
! 255: bp = buf;
! 256: }
! 257: if (rp > record && isspace(*bp))
! 258: continue;
! 259: else
! 260: break;
! 261: }
! 262: if (rp <= record || *(rp - 1) != ',' || !isspace(c))
! 263: *rp++ = c;
! 264:
! 265: /*
! 266: * Enforce loop invariant: if no room
! 267: * left in record buffer, try to get
! 268: * some more.
! 269: */
! 270: if (rp >= r_end) {
! 271: u_int pos;
! 272: size_t newsize;
! 273:
! 274: pos = rp - record;
! 275: newsize = r_end - record + BFRAG;
! 276: record = realloc(record, newsize);
! 277: if (record == NULL) {
! 278: errno = ENOMEM;
! 279: if (myfd)
! 280: (void)close(fd);
! 281: return (-2);
! 282: }
! 283: r_end = record + newsize;
! 284: rp = record + pos;
! 285: }
! 286: }
! 287: /* loop invariant let's us do this */
! 288: *rp++ = '\0';
! 289:
! 290: /*
! 291: * If encountered eof check next file.
! 292: */
! 293: if (eof)
! 294: break;
! 295:
! 296: /*
! 297: * Toss blank lines and comments.
! 298: */
! 299: if (*record == '\0' || *record == '#')
! 300: continue;
! 301:
! 302: /*
! 303: * See if this is the record we want ...
! 304: */
! 305: if (igetmatch(record, name) == 0) {
! 306: foundit = 1;
! 307: break; /* found it! */
! 308: }
! 309: }
! 310: }
! 311: if (foundit)
! 312: break;
! 313: }
! 314:
! 315: if (!foundit)
! 316: return (-1);
! 317:
! 318: /*
! 319: * Got the capability record, but now we have to expand all use=name
! 320: * references in it ...
! 321: */
! 322: {
! 323: register char *newicap, *s;
! 324: register int newilen;
! 325: u_int ilen;
! 326: int diff, iret, tclen;
! 327: char *icap, *scan, *tc, *tcstart, *tcend;
! 328:
! 329: /*
! 330: * Loop invariants:
! 331: * There is room for one more character in record.
! 332: * R_end points just past end of record.
! 333: * Rp points just past last character in record.
! 334: * Scan points at remainder of record that needs to be
! 335: * scanned for use=name constructs.
! 336: */
! 337: scan = record;
! 338: tc_not_resolved = 0;
! 339: for (;;) {
! 340: if ((tc = igetcap(scan, "use", '=')) == NULL)
! 341: break;
! 342:
! 343: /*
! 344: * Find end of use=name and stomp on the trailing `,'
! 345: * (if present) so we can use it to call ourselves.
! 346: */
! 347: s = tc;
! 348: for (;;)
! 349: if (*s == '\0')
! 350: break;
! 351: else
! 352: if (*s++ == ',') {
! 353: *(s - 1) = '\0';
! 354: break;
! 355: }
! 356: tcstart = tc - 3;
! 357: tclen = s - tcstart;
! 358: tcend = s;
! 359:
! 360: iret = getent(&icap, &ilen, db_p, fd, tc, depth+1);
! 361: newicap = icap; /* Put into a register. */
! 362: newilen = ilen;
! 363: if (iret != 0) {
! 364: /* an error */
! 365: if (iret < -1) {
! 366: if (myfd)
! 367: (void)close(fd);
! 368: free(record);
! 369: return (iret);
! 370: }
! 371: if (iret == 1)
! 372: tc_not_resolved = 1;
! 373: /* couldn't resolve tc */
! 374: if (iret == -1) {
! 375: *(s - 1) = ',';
! 376: scan = s - 1;
! 377: tc_not_resolved = 1;
! 378: continue;
! 379:
! 380: }
! 381: }
! 382: /* not interested in name field of tc'ed record */
! 383: s = newicap;
! 384: for (;;)
! 385: if (*s == '\0')
! 386: break;
! 387: else
! 388: if (*s++ == ',')
! 389: break;
! 390: newilen -= s - newicap;
! 391: newicap = s;
! 392:
! 393: /* make sure interpolated record is `,'-terminated */
! 394: s += newilen;
! 395: if (*(s-1) != ',') {
! 396: *s = ','; /* overwrite NUL with , */
! 397: newilen++;
! 398: }
! 399:
! 400: /*
! 401: * Make sure there's enough room to insert the
! 402: * new record.
! 403: */
! 404: diff = newilen - tclen;
! 405: if (diff >= r_end - rp) {
! 406: u_int pos, tcpos, tcposend;
! 407: size_t newsize;
! 408:
! 409: pos = rp - record;
! 410: newsize = r_end - record + diff + BFRAG;
! 411: tcpos = tcstart - record;
! 412: tcposend = tcend - record;
! 413: record = realloc(record, newsize);
! 414: if (record == NULL) {
! 415: errno = ENOMEM;
! 416: if (myfd)
! 417: (void)close(fd);
! 418: free(icap);
! 419: return (-2);
! 420: }
! 421: r_end = record + newsize;
! 422: rp = record + pos;
! 423: tcstart = record + tcpos;
! 424: tcend = record + tcposend;
! 425: }
! 426:
! 427: /*
! 428: * Insert tc'ed record into our record.
! 429: */
! 430: s = tcstart + newilen;
! 431: bcopy(tcend, s, (size_t)(rp - tcend));
! 432: bcopy(newicap, tcstart, (size_t)newilen);
! 433: rp += diff;
! 434: free(icap);
! 435:
! 436: /*
! 437: * Start scan on `,' so next igetcap works properly
! 438: * (igetcap always skips first field).
! 439: */
! 440: scan = s-1;
! 441: }
! 442:
! 443: }
! 444: /*
! 445: * Close file (if we opened it), give back any extra memory, and
! 446: * return capability, length and success.
! 447: */
! 448: if (myfd)
! 449: (void)close(fd);
! 450: *len = rp - record - 1; /* don't count NUL */
! 451: if (r_end > rp)
! 452: if ((record =
! 453: realloc(record, (size_t)(rp - record))) == NULL) {
! 454: errno = ENOMEM;
! 455: return (-2);
! 456: }
! 457:
! 458: *cap = record;
! 459: if (tc_not_resolved)
! 460: return (1);
! 461: return (0);
! 462: }
! 463:
! 464: /*
! 465: * Cgetmatch will return 0 if name is one of the names of the capability
! 466: * record buf, -1 if not.
! 467: */
! 468: static int
! 469: igetmatch(buf, name)
! 470: char *buf, *name;
! 471: {
! 472: register char *np, *bp;
! 473:
! 474: /*
! 475: * Start search at beginning of record.
! 476: */
! 477: bp = buf;
! 478: for (;;) {
! 479: /*
! 480: * Try to match a record name.
! 481: */
! 482: np = name;
! 483: for (;;)
! 484: if (*np == '\0')
! 485: if (*bp == '|' || *bp == ',' || *bp == '\0')
! 486: return (0);
! 487: else
! 488: break;
! 489: else
! 490: if (*bp++ != *np++)
! 491: break;
! 492:
! 493: /*
! 494: * Match failed, skip to next name in record.
! 495: */
! 496: bp--; /* a '|' or ',' may have stopped the match */
! 497: for (;;)
! 498: if (*bp == '\0' || *bp == ',')
! 499: return (-1); /* match failed totally */
! 500: else
! 501: if (*bp++ == '|')
! 502: break; /* found next name */
! 503: }
! 504: }
! 505:
! 506: static FILE *pfp;
! 507: static int slash;
! 508: static char **dbp;
! 509:
! 510: static int
! 511: igetclose()
! 512: {
! 513: if (pfp != NULL) {
! 514: (void)fclose(pfp);
! 515: pfp = NULL;
! 516: }
! 517: dbp = NULL;
! 518: slash = 0;
! 519: return(0);
! 520: }
! 521:
! 522: /*
! 523: * Cgetnext() gets either the first or next entry in the logical database
! 524: * specified by db_array. It returns 0 upon completion of the database, 1
! 525: * upon returning an entry with more remaining, and -1 if an error occurs.
! 526: */
! 527: int
! 528: igetnext(bp, db_array)
! 529: register char **bp;
! 530: char **db_array;
! 531: {
! 532: size_t len;
! 533: int status, done;
! 534: char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
! 535: u_int dummy;
! 536:
! 537: if (dbp == NULL)
! 538: dbp = db_array;
! 539:
! 540: if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
! 541: (void)igetclose();
! 542: return (-1);
! 543: }
! 544: for(;;) {
! 545: line = fgetln(pfp, &len);
! 546: if (line == NULL && pfp) {
! 547: (void)fclose(pfp);
! 548: if (ferror(pfp)) {
! 549: (void)igetclose();
! 550: return (-1);
! 551: } else {
! 552: if (*++dbp == NULL) {
! 553: (void)igetclose();
! 554: return (0);
! 555: } else if ((pfp =
! 556: fopen(*dbp, "r")) == NULL) {
! 557: (void)igetclose();
! 558: return (-1);
! 559: } else
! 560: continue;
! 561: }
! 562: } else
! 563: line[len - 1] = '\0';
! 564: if (len == 1) {
! 565: slash = 0;
! 566: continue;
! 567: }
! 568: if (isspace(*line) ||
! 569: *line == ',' || *line == '#' || slash) {
! 570: if (line[len - 2] == '\\')
! 571: slash = 1;
! 572: else
! 573: slash = 0;
! 574: continue;
! 575: }
! 576: if (line[len - 2] == '\\')
! 577: slash = 1;
! 578: else
! 579: slash = 0;
! 580:
! 581: /*
! 582: * Line points to a name line.
! 583: */
! 584: done = 0;
! 585: np = nbuf;
! 586: for (;;) {
! 587: for (cp = line; *cp != '\0'; cp++) {
! 588: if (*cp == ',') {
! 589: *np++ = ',';
! 590: done = 1;
! 591: break;
! 592: }
! 593: *np++ = *cp;
! 594: }
! 595: if (done) {
! 596: *np = '\0';
! 597: break;
! 598: } else { /* name field extends beyond the line */
! 599: line = fgetln(pfp, &len);
! 600: if (line == NULL && pfp) {
! 601: (void)fclose(pfp);
! 602: if (ferror(pfp)) {
! 603: (void)igetclose();
! 604: return (-1);
! 605: }
! 606: } else
! 607: line[len - 1] = '\0';
! 608: }
! 609: }
! 610: rp = buf;
! 611: for(cp = nbuf; *cp != NULL; cp++)
! 612: if (*cp == '|' || *cp == ',')
! 613: break;
! 614: else
! 615: *rp++ = *cp;
! 616:
! 617: *rp = '\0';
! 618: status = getent(bp, &dummy, db_array, -1, buf, 0);
! 619: if (status == -2 || status == -3)
! 620: (void)igetclose();
! 621:
! 622: return (status + 1);
! 623: }
! 624: /* NOTREACHED */
! 625: }