1 /*- 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Casey Leedom of Lawrence Livermore National Laboratory. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #if defined(LIBC_SCCS) && !defined(lint) 38 static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94"; 39 #endif /* LIBC_SCCS and not lint */ 40 41 #include <sys/types.h> 42 43 #include <ctype.h> 44 #include <db.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <limits.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <unistd.h> 52 53 #define BFRAG 1024 54 #define BSIZE 1024 55 #define ESC ('[' & 037) /* ASCII ESC */ 56 #define MAX_RECURSION 32 /* maximum getent recursion */ 57 #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 58 59 #define RECOK (char)0 60 #define TCERR (char)1 61 #define SHADOW (char)2 62 63 static size_t topreclen; /* toprec length */ 64 static char *toprec; /* Additional record specified by cgetset() */ 65 static int gottoprec; /* Flag indicating retrieval of toprecord */ 66 67 static int cdbget __P((DB *, char **, char *)); 68 static int getent __P((char **, u_int *, char **, int, char *, int, char *)); 69 static int nfcmp __P((char *, char *)); 70 71 /* 72 * Cgetset() allows the addition of a user specified buffer to be added 73 * to the database array, in effect "pushing" the buffer on top of the 74 * virtual database. 0 is returned on success, -1 on failure. 75 */ 76 int 77 cgetset(ent) 78 char *ent; 79 { 80 if (ent == NULL) { 81 if (toprec) 82 free(toprec); 83 toprec = NULL; 84 topreclen = 0; 85 return (0); 86 } 87 topreclen = strlen(ent); 88 if ((toprec = malloc (topreclen + 1)) == NULL) { 89 errno = ENOMEM; 90 return (-1); 91 } 92 gottoprec = 0; 93 (void)strcpy(toprec, ent); 94 return (0); 95 } 96 97 /* 98 * Cgetcap searches the capability record buf for the capability cap with 99 * type `type'. A pointer to the value of cap is returned on success, NULL 100 * if the requested capability couldn't be found. 101 * 102 * Specifying a type of ':' means that nothing should follow cap (:cap:). 103 * In this case a pointer to the terminating ':' or NUL will be returned if 104 * cap is found. 105 * 106 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 107 * return NULL. 108 */ 109 char * 110 cgetcap(buf, cap, type) 111 char *buf, *cap; 112 int type; 113 { 114 register char *bp, *cp; 115 116 bp = buf; 117 for (;;) { 118 /* 119 * Skip past the current capability field - it's either the 120 * name field if this is the first time through the loop, or 121 * the remainder of a field whose name failed to match cap. 122 */ 123 for (;;) 124 if (*bp == '\0') 125 return (NULL); 126 else 127 if (*bp++ == ':') 128 break; 129 130 /* 131 * Try to match (cap, type) in buf. 132 */ 133 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 134 continue; 135 if (*cp != '\0') 136 continue; 137 if (*bp == '@') 138 return (NULL); 139 if (type == ':') { 140 if (*bp != '\0' && *bp != ':') 141 continue; 142 return(bp); 143 } 144 if (*bp != type) 145 continue; 146 bp++; 147 return (*bp == '@' ? NULL : bp); 148 } 149 /* NOTREACHED */ 150 } 151 152 /* 153 * Cgetent extracts the capability record name from the NULL terminated file 154 * array db_array and returns a pointer to a malloc'd copy of it in buf. 155 * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 156 * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 157 * -1 if the requested record couldn't be found, -2 if a system error was 158 * encountered (couldn't open/read a file, etc.), and -3 if a potential 159 * reference loop is detected. 160 */ 161 int 162 cgetent(buf, db_array, name) 163 char **buf, **db_array, *name; 164 { 165 u_int dummy; 166 167 return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 168 } 169 170 /* 171 * Getent implements the functions of cgetent. If fd is non-negative, 172 * *db_array has already been opened and fd is the open file descriptor. We 173 * do this to save time and avoid using up file descriptors for tc= 174 * recursions. 175 * 176 * Getent returns the same success/failure codes as cgetent. On success, a 177 * pointer to a malloc'ed capability record with all tc= capabilities fully 178 * expanded and its length (not including trailing ASCII NUL) are left in 179 * *cap and *len. 180 * 181 * Basic algorithm: 182 * + Allocate memory incrementally as needed in chunks of size BFRAG 183 * for capability buffer. 184 * + Recurse for each tc=name and interpolate result. Stop when all 185 * names interpolated, a name can't be found, or depth exceeds 186 * MAX_RECURSION. 187 */ 188 static int 189 getent(cap, len, db_array, fd, name, depth, nfield) 190 char **cap, **db_array, *name, *nfield; 191 u_int *len; 192 int fd, depth; 193 { 194 DB *capdbp; 195 DBT key, data; 196 register char *r_end, *rp, **db_p; 197 int myfd, eof, foundit, retval, clen; 198 char *record, *cbuf; 199 int tc_not_resolved; 200 char pbuf[_POSIX_PATH_MAX]; 201 202 /* 203 * Return with ``loop detected'' error if we've recursed more than 204 * MAX_RECURSION times. 205 */ 206 if (depth > MAX_RECURSION) 207 return (-3); 208 209 /* 210 * Check if we have a top record from cgetset(). 211 */ 212 if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 213 if ((record = malloc (topreclen + BFRAG)) == NULL) { 214 errno = ENOMEM; 215 return (-2); 216 } 217 (void)strcpy(record, toprec); 218 myfd = 0; 219 db_p = db_array; 220 rp = record + topreclen + 1; 221 r_end = rp + BFRAG; 222 goto tc_exp; 223 } 224 /* 225 * Allocate first chunk of memory. 226 */ 227 if ((record = malloc(BFRAG)) == NULL) { 228 errno = ENOMEM; 229 return (-2); 230 } 231 r_end = record + BFRAG; 232 foundit = 0; 233 /* 234 * Loop through database array until finding the record. 235 */ 236 237 for (db_p = db_array; *db_p != NULL; db_p++) { 238 eof = 0; 239 240 /* 241 * Open database if not already open. 242 */ 243 244 if (fd >= 0) { 245 (void)lseek(fd, (off_t)0, L_SET); 246 myfd = 0; 247 } else { 248 (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 249 if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 250 != NULL) { 251 free(record); 252 retval = cdbget(capdbp, &record, name); 253 if (retval < 0) { 254 /* no record available */ 255 (void)capdbp->close(capdbp); 256 return (retval); 257 } 258 /* save the data; close frees it */ 259 clen = strlen(record); 260 cbuf = malloc(clen + 1); 261 memcpy(cbuf, record, clen + 1); 262 if (capdbp->close(capdbp) < 0) { 263 free(cbuf); 264 return (-2); 265 } 266 *len = clen; 267 *cap = cbuf; 268 return (retval); 269 } else { 270 fd = open(*db_p, O_RDONLY, 0); 271 if (fd < 0) { 272 /* No error on unfound file. */ 273 if (errno == ENOENT) 274 continue; 275 free(record); 276 return (-2); 277 } 278 myfd = 1; 279 } 280 } 281 /* 282 * Find the requested capability record ... 283 */ 284 { 285 char buf[BUFSIZ]; 286 register char *b_end, *bp; 287 register int c; 288 289 /* 290 * Loop invariants: 291 * There is always room for one more character in record. 292 * R_end always points just past end of record. 293 * Rp always points just past last character in record. 294 * B_end always points just past last character in buf. 295 * Bp always points at next character in buf. 296 */ 297 b_end = buf; 298 bp = buf; 299 for (;;) { 300 301 /* 302 * Read in a line implementing (\, newline) 303 * line continuation. 304 */ 305 rp = record; 306 for (;;) { 307 if (bp >= b_end) { 308 int n; 309 310 n = read(fd, buf, sizeof(buf)); 311 if (n <= 0) { 312 if (myfd) 313 (void)close(fd); 314 if (n < 0) { 315 free(record); 316 return (-2); 317 } else { 318 fd = -1; 319 eof = 1; 320 break; 321 } 322 } 323 b_end = buf+n; 324 bp = buf; 325 } 326 327 c = *bp++; 328 if (c == '\n') { 329 if (rp > record && *(rp-1) == '\\') { 330 rp--; 331 continue; 332 } else 333 break; 334 } 335 *rp++ = c; 336 337 /* 338 * Enforce loop invariant: if no room 339 * left in record buffer, try to get 340 * some more. 341 */ 342 if (rp >= r_end) { 343 u_int pos; 344 size_t newsize; 345 346 pos = rp - record; 347 newsize = r_end - record + BFRAG; 348 record = realloc(record, newsize); 349 if (record == NULL) { 350 errno = ENOMEM; 351 if (myfd) 352 (void)close(fd); 353 return (-2); 354 } 355 r_end = record + newsize; 356 rp = record + pos; 357 } 358 } 359 /* loop invariant let's us do this */ 360 *rp++ = '\0'; 361 362 /* 363 * If encountered eof check next file. 364 */ 365 if (eof) 366 break; 367 368 /* 369 * Toss blank lines and comments. 370 */ 371 if (*record == '\0' || *record == '#') 372 continue; 373 374 /* 375 * See if this is the record we want ... 376 */ 377 if (cgetmatch(record, name) == 0) { 378 if (nfield == NULL || !nfcmp(nfield, record)) { 379 foundit = 1; 380 break; /* found it! */ 381 } 382 } 383 } 384 } 385 if (foundit) 386 break; 387 } 388 389 if (!foundit) 390 return (-1); 391 392 /* 393 * Got the capability record, but now we have to expand all tc=name 394 * references in it ... 395 */ 396 tc_exp: { 397 register char *newicap, *s; 398 register int newilen; 399 u_int ilen; 400 int diff, iret, tclen; 401 char *icap, *scan, *tc, *tcstart, *tcend; 402 403 /* 404 * Loop invariants: 405 * There is room for one more character in record. 406 * R_end points just past end of record. 407 * Rp points just past last character in record. 408 * Scan points at remainder of record that needs to be 409 * scanned for tc=name constructs. 410 */ 411 scan = record; 412 tc_not_resolved = 0; 413 for (;;) { 414 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 415 break; 416 417 /* 418 * Find end of tc=name and stomp on the trailing `:' 419 * (if present) so we can use it to call ourselves. 420 */ 421 s = tc; 422 for (;;) 423 if (*s == '\0') 424 break; 425 else 426 if (*s++ == ':') { 427 *(s - 1) = '\0'; 428 break; 429 } 430 tcstart = tc - 3; 431 tclen = s - tcstart; 432 tcend = s; 433 434 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 435 NULL); 436 newicap = icap; /* Put into a register. */ 437 newilen = ilen; 438 if (iret != 0) { 439 /* an error */ 440 if (iret < -1) { 441 if (myfd) 442 (void)close(fd); 443 free(record); 444 return (iret); 445 } 446 if (iret == 1) 447 tc_not_resolved = 1; 448 /* couldn't resolve tc */ 449 if (iret == -1) { 450 *(s - 1) = ':'; 451 scan = s - 1; 452 tc_not_resolved = 1; 453 continue; 454 455 } 456 } 457 /* not interested in name field of tc'ed record */ 458 s = newicap; 459 for (;;) 460 if (*s == '\0') 461 break; 462 else 463 if (*s++ == ':') 464 break; 465 newilen -= s - newicap; 466 newicap = s; 467 468 /* make sure interpolated record is `:'-terminated */ 469 s += newilen; 470 if (*(s-1) != ':') { 471 *s = ':'; /* overwrite NUL with : */ 472 newilen++; 473 } 474 475 /* 476 * Make sure there's enough room to insert the 477 * new record. 478 */ 479 diff = newilen - tclen; 480 if (diff >= r_end - rp) { 481 u_int pos, tcpos, tcposend; 482 size_t newsize; 483 484 pos = rp - record; 485 newsize = r_end - record + diff + BFRAG; 486 tcpos = tcstart - record; 487 tcposend = tcend - record; 488 record = realloc(record, newsize); 489 if (record == NULL) { 490 errno = ENOMEM; 491 if (myfd) 492 (void)close(fd); 493 free(icap); 494 return (-2); 495 } 496 r_end = record + newsize; 497 rp = record + pos; 498 tcstart = record + tcpos; 499 tcend = record + tcposend; 500 } 501 502 /* 503 * Insert tc'ed record into our record. 504 */ 505 s = tcstart + newilen; 506 bcopy(tcend, s, rp - tcend); 507 bcopy(newicap, tcstart, newilen); 508 rp += diff; 509 free(icap); 510 511 /* 512 * Start scan on `:' so next cgetcap works properly 513 * (cgetcap always skips first field). 514 */ 515 scan = s-1; 516 } 517 518 } 519 /* 520 * Close file (if we opened it), give back any extra memory, and 521 * return capability, length and success. 522 */ 523 if (myfd) 524 (void)close(fd); 525 *len = rp - record - 1; /* don't count NUL */ 526 if (r_end > rp) 527 if ((record = 528 realloc(record, (size_t)(rp - record))) == NULL) { 529 errno = ENOMEM; 530 return (-2); 531 } 532 533 *cap = record; 534 if (tc_not_resolved) 535 return (1); 536 return (0); 537 } 538 539 static int 540 cdbget(capdbp, bp, name) 541 DB *capdbp; 542 char **bp, *name; 543 { 544 DBT key, data; 545 char *buf; 546 int st; 547 548 key.data = name; 549 key.size = strlen(name); 550 551 for (;;) { 552 /* Get the reference. */ 553 switch(capdbp->get(capdbp, &key, &data, 0)) { 554 case -1: 555 return (-2); 556 case 1: 557 return (-1); 558 } 559 560 /* If not an index to another record, leave. */ 561 if (((char *)data.data)[0] != SHADOW) 562 break; 563 564 key.data = (char *)data.data + 1; 565 key.size = data.size - 1; 566 } 567 568 *bp = (char *)data.data + 1; 569 return (((char *)(data.data))[0] == TCERR ? 1 : 0); 570 } 571 572 /* 573 * Cgetmatch will return 0 if name is one of the names of the capability 574 * record buf, -1 if not. 575 */ 576 int 577 cgetmatch(buf, name) 578 char *buf, *name; 579 { 580 register char *np, *bp; 581 582 /* 583 * Start search at beginning of record. 584 */ 585 bp = buf; 586 for (;;) { 587 /* 588 * Try to match a record name. 589 */ 590 np = name; 591 for (;;) 592 if (*np == '\0') 593 if (*bp == '|' || *bp == ':' || *bp == '\0') 594 return (0); 595 else 596 break; 597 else 598 if (*bp++ != *np++) 599 break; 600 601 /* 602 * Match failed, skip to next name in record. 603 */ 604 bp--; /* a '|' or ':' may have stopped the match */ 605 for (;;) 606 if (*bp == '\0' || *bp == ':') 607 return (-1); /* match failed totally */ 608 else 609 if (*bp++ == '|') 610 break; /* found next name */ 611 } 612 } 613 614 615 616 617 618 int 619 cgetfirst(buf, db_array) 620 char **buf, **db_array; 621 { 622 (void)cgetclose(); 623 return (cgetnext(buf, db_array)); 624 } 625 626 static FILE *pfp; 627 static int slash; 628 static char **dbp; 629 630 int 631 cgetclose() 632 { 633 if (pfp != NULL) { 634 (void)fclose(pfp); 635 pfp = NULL; 636 } 637 dbp = NULL; 638 gottoprec = 0; 639 slash = 0; 640 return(0); 641 } 642 643 /* 644 * Cgetnext() gets either the first or next entry in the logical database 645 * specified by db_array. It returns 0 upon completion of the database, 1 646 * upon returning an entry with more remaining, and -1 if an error occurs. 647 */ 648 int 649 cgetnext(bp, db_array) 650 register char **bp; 651 char **db_array; 652 { 653 size_t len; 654 int status, i, done; 655 char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 656 u_int dummy; 657 658 if (dbp == NULL) 659 dbp = db_array; 660 661 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 662 (void)cgetclose(); 663 return (-1); 664 } 665 for(;;) { 666 if (toprec && !gottoprec) { 667 gottoprec = 1; 668 line = toprec; 669 } else { 670 line = fgetln(pfp, &len); 671 if (line == NULL && pfp) { 672 (void)fclose(pfp); 673 if (ferror(pfp)) { 674 (void)cgetclose(); 675 return (-1); 676 } else { 677 if (*++dbp == NULL) { 678 (void)cgetclose(); 679 return (0); 680 } else if ((pfp = 681 fopen(*dbp, "r")) == NULL) { 682 (void)cgetclose(); 683 return (-1); 684 } else 685 continue; 686 } 687 } else 688 line[len - 1] = '\0'; 689 if (len == 1) { 690 slash = 0; 691 continue; 692 } 693 if (isspace(*line) || 694 *line == ':' || *line == '#' || slash) { 695 if (line[len - 2] == '\\') 696 slash = 1; 697 else 698 slash = 0; 699 continue; 700 } 701 if (line[len - 2] == '\\') 702 slash = 1; 703 else 704 slash = 0; 705 } 706 707 708 /* 709 * Line points to a name line. 710 */ 711 i = 0; 712 done = 0; 713 np = nbuf; 714 for (;;) { 715 for (cp = line; *cp != '\0'; cp++) { 716 if (*cp == ':') { 717 *np++ = ':'; 718 done = 1; 719 break; 720 } 721 if (*cp == '\\') 722 break; 723 *np++ = *cp; 724 } 725 if (done) { 726 *np = '\0'; 727 break; 728 } else { /* name field extends beyond the line */ 729 line = fgetln(pfp, &len); 730 if (line == NULL && pfp) { 731 (void)fclose(pfp); 732 if (ferror(pfp)) { 733 (void)cgetclose(); 734 return (-1); 735 } 736 } else 737 line[len - 1] = '\0'; 738 } 739 } 740 rp = buf; 741 for(cp = nbuf; *cp != NULL; cp++) 742 if (*cp == '|' || *cp == ':') 743 break; 744 else 745 *rp++ = *cp; 746 747 *rp = '\0'; 748 /* 749 * XXX 750 * Last argument of getent here should be nbuf if we want true 751 * sequential access in the case of duplicates. 752 * With NULL, getent will return the first entry found 753 * rather than the duplicate entry record. This is a 754 * matter of semantics that should be resolved. 755 */ 756 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 757 if (status == -2 || status == -3) 758 (void)cgetclose(); 759 760 return (status + 1); 761 } 762 /* NOTREACHED */ 763 } 764 765 /* 766 * Cgetstr retrieves the value of the string capability cap from the 767 * capability record pointed to by buf. A pointer to a decoded, NUL 768 * terminated, malloc'd copy of the string is returned in the char * 769 * pointed to by str. The length of the string not including the trailing 770 * NUL is returned on success, -1 if the requested string capability 771 * couldn't be found, -2 if a system error was encountered (storage 772 * allocation failure). 773 */ 774 int 775 cgetstr(buf, cap, str) 776 char *buf, *cap; 777 char **str; 778 { 779 register u_int m_room; 780 register char *bp, *mp; 781 int len; 782 char *mem; 783 784 /* 785 * Find string capability cap 786 */ 787 bp = cgetcap(buf, cap, '='); 788 if (bp == NULL) 789 return (-1); 790 791 /* 792 * Conversion / storage allocation loop ... Allocate memory in 793 * chunks SFRAG in size. 794 */ 795 if ((mem = malloc(SFRAG)) == NULL) { 796 errno = ENOMEM; 797 return (-2); /* couldn't even allocate the first fragment */ 798 } 799 m_room = SFRAG; 800 mp = mem; 801 802 while (*bp != ':' && *bp != '\0') { 803 /* 804 * Loop invariants: 805 * There is always room for one more character in mem. 806 * Mp always points just past last character in mem. 807 * Bp always points at next character in buf. 808 */ 809 if (*bp == '^') { 810 bp++; 811 if (*bp == ':' || *bp == '\0') 812 break; /* drop unfinished escape */ 813 *mp++ = *bp++ & 037; 814 } else if (*bp == '\\') { 815 bp++; 816 if (*bp == ':' || *bp == '\0') 817 break; /* drop unfinished escape */ 818 if ('0' <= *bp && *bp <= '7') { 819 register int n, i; 820 821 n = 0; 822 i = 3; /* maximum of three octal digits */ 823 do { 824 n = n * 8 + (*bp++ - '0'); 825 } while (--i && '0' <= *bp && *bp <= '7'); 826 *mp++ = n; 827 } 828 else switch (*bp++) { 829 case 'b': case 'B': 830 *mp++ = '\b'; 831 break; 832 case 't': case 'T': 833 *mp++ = '\t'; 834 break; 835 case 'n': case 'N': 836 *mp++ = '\n'; 837 break; 838 case 'f': case 'F': 839 *mp++ = '\f'; 840 break; 841 case 'r': case 'R': 842 *mp++ = '\r'; 843 break; 844 case 'e': case 'E': 845 *mp++ = ESC; 846 break; 847 case 'c': case 'C': 848 *mp++ = ':'; 849 break; 850 default: 851 /* 852 * Catches '\', '^', and 853 * everything else. 854 */ 855 *mp++ = *(bp-1); 856 break; 857 } 858 } else 859 *mp++ = *bp++; 860 m_room--; 861 862 /* 863 * Enforce loop invariant: if no room left in current 864 * buffer, try to get some more. 865 */ 866 if (m_room == 0) { 867 size_t size = mp - mem; 868 869 if ((mem = realloc(mem, size + SFRAG)) == NULL) 870 return (-2); 871 m_room = SFRAG; 872 mp = mem + size; 873 } 874 } 875 *mp++ = '\0'; /* loop invariant let's us do this */ 876 m_room--; 877 len = mp - mem - 1; 878 879 /* 880 * Give back any extra memory and return value and success. 881 */ 882 if (m_room != 0) 883 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 884 return (-2); 885 *str = mem; 886 return (len); 887 } 888 889 /* 890 * Cgetustr retrieves the value of the string capability cap from the 891 * capability record pointed to by buf. The difference between cgetustr() 892 * and cgetstr() is that cgetustr does not decode escapes but rather treats 893 * all characters literally. A pointer to a NUL terminated malloc'd 894 * copy of the string is returned in the char pointed to by str. The 895 * length of the string not including the trailing NUL is returned on success, 896 * -1 if the requested string capability couldn't be found, -2 if a system 897 * error was encountered (storage allocation failure). 898 */ 899 int 900 cgetustr(buf, cap, str) 901 char *buf, *cap, **str; 902 { 903 register u_int m_room; 904 register char *bp, *mp; 905 int len; 906 char *mem; 907 908 /* 909 * Find string capability cap 910 */ 911 if ((bp = cgetcap(buf, cap, '=')) == NULL) 912 return (-1); 913 914 /* 915 * Conversion / storage allocation loop ... Allocate memory in 916 * chunks SFRAG in size. 917 */ 918 if ((mem = malloc(SFRAG)) == NULL) { 919 errno = ENOMEM; 920 return (-2); /* couldn't even allocate the first fragment */ 921 } 922 m_room = SFRAG; 923 mp = mem; 924 925 while (*bp != ':' && *bp != '\0') { 926 /* 927 * Loop invariants: 928 * There is always room for one more character in mem. 929 * Mp always points just past last character in mem. 930 * Bp always points at next character in buf. 931 */ 932 *mp++ = *bp++; 933 m_room--; 934 935 /* 936 * Enforce loop invariant: if no room left in current 937 * buffer, try to get some more. 938 */ 939 if (m_room == 0) { 940 size_t size = mp - mem; 941 942 if ((mem = realloc(mem, size + SFRAG)) == NULL) 943 return (-2); 944 m_room = SFRAG; 945 mp = mem + size; 946 } 947 } 948 *mp++ = '\0'; /* loop invariant let's us do this */ 949 m_room--; 950 len = mp - mem - 1; 951 952 /* 953 * Give back any extra memory and return value and success. 954 */ 955 if (m_room != 0) 956 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 957 return (-2); 958 *str = mem; 959 return (len); 960 } 961 962 /* 963 * Cgetnum retrieves the value of the numeric capability cap from the 964 * capability record pointed to by buf. The numeric value is returned in 965 * the long pointed to by num. 0 is returned on success, -1 if the requested 966 * numeric capability couldn't be found. 967 */ 968 int 969 cgetnum(buf, cap, num) 970 char *buf, *cap; 971 long *num; 972 { 973 register long n; 974 register int base, digit; 975 register char *bp; 976 977 /* 978 * Find numeric capability cap 979 */ 980 bp = cgetcap(buf, cap, '#'); 981 if (bp == NULL) 982 return (-1); 983 984 /* 985 * Look at value and determine numeric base: 986 * 0x... or 0X... hexadecimal, 987 * else 0... octal, 988 * else decimal. 989 */ 990 if (*bp == '0') { 991 bp++; 992 if (*bp == 'x' || *bp == 'X') { 993 bp++; 994 base = 16; 995 } else 996 base = 8; 997 } else 998 base = 10; 999 1000 /* 1001 * Conversion loop ... 1002 */ 1003 n = 0; 1004 for (;;) { 1005 if ('0' <= *bp && *bp <= '9') 1006 digit = *bp - '0'; 1007 else if ('a' <= *bp && *bp <= 'f') 1008 digit = 10 + *bp - 'a'; 1009 else if ('A' <= *bp && *bp <= 'F') 1010 digit = 10 + *bp - 'A'; 1011 else 1012 break; 1013 1014 if (digit >= base) 1015 break; 1016 1017 n = n * base + digit; 1018 bp++; 1019 } 1020 1021 /* 1022 * Return value and success. 1023 */ 1024 *num = n; 1025 return (0); 1026 } 1027 1028 1029 /* 1030 * Compare name field of record. 1031 */ 1032 static int 1033 nfcmp(nf, rec) 1034 char *nf, *rec; 1035 { 1036 char *cp, tmp; 1037 int ret; 1038 1039 for (cp = rec; *cp != ':'; cp++) 1040 ; 1041 1042 tmp = *(cp + 1); 1043 *(cp + 1) = '\0'; 1044 ret = strcmp(nf, rec); 1045 *(cp + 1) = tmp; 1046 1047 return (ret); 1048 } 1049