1 /*- 2 * Copyright (c) 1992 The Regents of the University of California. 3 * 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 5.15 (Berkeley) 3/19/93"; 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; 198 char *record; 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 (capdbp->close(capdbp) < 0) 254 return (-2); 255 *len = strlen(record); 256 *cap = malloc(*len + 1); 257 memmove(*cap, record, *len + 1); 258 return (retval); 259 } else { 260 fd = open(*db_p, O_RDONLY, 0); 261 if (fd < 0) { 262 /* No error on unfound file. */ 263 if (errno == ENOENT) 264 continue; 265 free(record); 266 return (-2); 267 } 268 myfd = 1; 269 } 270 } 271 /* 272 * Find the requested capability record ... 273 */ 274 { 275 char buf[BUFSIZ]; 276 register char *b_end, *bp; 277 register int c; 278 279 /* 280 * Loop invariants: 281 * There is always room for one more character in record. 282 * R_end always points just past end of record. 283 * Rp always points just past last character in record. 284 * B_end always points just past last character in buf. 285 * Bp always points at next character in buf. 286 */ 287 b_end = buf; 288 bp = buf; 289 for (;;) { 290 291 /* 292 * Read in a line implementing (\, newline) 293 * line continuation. 294 */ 295 rp = record; 296 for (;;) { 297 if (bp >= b_end) { 298 int n; 299 300 n = read(fd, buf, sizeof(buf)); 301 if (n <= 0) { 302 if (myfd) 303 (void)close(fd); 304 if (n < 0) { 305 free(record); 306 return (-2); 307 } else { 308 fd = -1; 309 eof = 1; 310 break; 311 } 312 } 313 b_end = buf+n; 314 bp = buf; 315 } 316 317 c = *bp++; 318 if (c == '\n') { 319 if (rp > record && *(rp-1) == '\\') { 320 rp--; 321 continue; 322 } else 323 break; 324 } 325 *rp++ = c; 326 327 /* 328 * Enforce loop invariant: if no room 329 * left in record buffer, try to get 330 * some more. 331 */ 332 if (rp >= r_end) { 333 u_int pos; 334 size_t newsize; 335 336 pos = rp - record; 337 newsize = r_end - record + BFRAG; 338 record = realloc(record, newsize); 339 if (record == NULL) { 340 errno = ENOMEM; 341 if (myfd) 342 (void)close(fd); 343 return (-2); 344 } 345 r_end = record + newsize; 346 rp = record + pos; 347 } 348 } 349 /* loop invariant let's us do this */ 350 *rp++ = '\0'; 351 352 /* 353 * If encountered eof check next file. 354 */ 355 if (eof) 356 break; 357 358 /* 359 * Toss blank lines and comments. 360 */ 361 if (*record == '\0' || *record == '#') 362 continue; 363 364 /* 365 * See if this is the record we want ... 366 */ 367 if (cgetmatch(record, name) == 0) { 368 if (nfield == NULL || !nfcmp(nfield, record)) { 369 foundit = 1; 370 break; /* found it! */ 371 } 372 } 373 } 374 } 375 if (foundit) 376 break; 377 } 378 379 if (!foundit) 380 return (-1); 381 382 /* 383 * Got the capability record, but now we have to expand all tc=name 384 * references in it ... 385 */ 386 tc_exp: { 387 register char *newicap, *s; 388 register int newilen; 389 u_int ilen; 390 int diff, iret, tclen; 391 char *icap, *scan, *tc, *tcstart, *tcend; 392 393 /* 394 * Loop invariants: 395 * There is room for one more character in record. 396 * R_end points just past end of record. 397 * Rp points just past last character in record. 398 * Scan points at remainder of record that needs to be 399 * scanned for tc=name constructs. 400 */ 401 scan = record; 402 tc_not_resolved = 0; 403 for (;;) { 404 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 405 break; 406 407 /* 408 * Find end of tc=name and stomp on the trailing `:' 409 * (if present) so we can use it to call ourselves. 410 */ 411 s = tc; 412 for (;;) 413 if (*s == '\0') 414 break; 415 else 416 if (*s++ == ':') { 417 *(s - 1) = '\0'; 418 break; 419 } 420 tcstart = tc - 3; 421 tclen = s - tcstart; 422 tcend = s; 423 424 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 425 NULL); 426 newicap = icap; /* Put into a register. */ 427 newilen = ilen; 428 if (iret != 0) { 429 /* an error */ 430 if (iret < -1) { 431 if (myfd) 432 (void)close(fd); 433 free(record); 434 return (iret); 435 } 436 if (iret == 1) 437 tc_not_resolved = 1; 438 /* couldn't resolve tc */ 439 if (iret == -1) { 440 *(s - 1) = ':'; 441 scan = s - 1; 442 tc_not_resolved = 1; 443 continue; 444 445 } 446 } 447 /* not interested in name field of tc'ed record */ 448 s = newicap; 449 for (;;) 450 if (*s == '\0') 451 break; 452 else 453 if (*s++ == ':') 454 break; 455 newilen -= s - newicap; 456 newicap = s; 457 458 /* make sure interpolated record is `:'-terminated */ 459 s += newilen; 460 if (*(s-1) != ':') { 461 *s = ':'; /* overwrite NUL with : */ 462 newilen++; 463 } 464 465 /* 466 * Make sure there's enough room to insert the 467 * new record. 468 */ 469 diff = newilen - tclen; 470 if (diff >= r_end - rp) { 471 u_int pos, tcpos, tcposend; 472 size_t newsize; 473 474 pos = rp - record; 475 newsize = r_end - record + diff + BFRAG; 476 tcpos = tcstart - record; 477 tcposend = tcend - record; 478 record = realloc(record, newsize); 479 if (record == NULL) { 480 errno = ENOMEM; 481 if (myfd) 482 (void)close(fd); 483 free(icap); 484 return (-2); 485 } 486 r_end = record + newsize; 487 rp = record + pos; 488 tcstart = record + tcpos; 489 tcend = record + tcposend; 490 } 491 492 /* 493 * Insert tc'ed record into our record. 494 */ 495 s = tcstart + newilen; 496 bcopy(tcend, s, rp - tcend); 497 bcopy(newicap, tcstart, newilen); 498 rp += diff; 499 free(icap); 500 501 /* 502 * Start scan on `:' so next cgetcap works properly 503 * (cgetcap always skips first field). 504 */ 505 scan = s-1; 506 } 507 508 } 509 /* 510 * Close file (if we opened it), give back any extra memory, and 511 * return capability, length and success. 512 */ 513 if (myfd) 514 (void)close(fd); 515 *len = rp - record - 1; /* don't count NUL */ 516 if (r_end > rp) 517 if ((record = 518 realloc(record, (size_t)(rp - record))) == NULL) { 519 errno = ENOMEM; 520 return (-2); 521 } 522 523 *cap = record; 524 if (tc_not_resolved) 525 return (1); 526 return (0); 527 } 528 529 static int 530 cdbget(capdbp, bp, name) 531 DB *capdbp; 532 char **bp, *name; 533 { 534 DBT key, data; 535 char *buf; 536 int st; 537 538 key.data = name; 539 key.size = strlen(name); 540 541 for (;;) { 542 /* Get the reference. */ 543 switch(capdbp->get(capdbp, &key, &data, 0)) { 544 case -1: 545 return (-2); 546 case 1: 547 return (-1); 548 } 549 550 /* If not an index to another record, leave. */ 551 if (((char *)data.data)[0] != SHADOW) 552 break; 553 554 key.data = (char *)data.data + 1; 555 key.size = data.size - 1; 556 } 557 558 *bp = (char *)data.data + 1; 559 return (((char *)(data.data))[0] == TCERR ? 1 : 0); 560 } 561 562 /* 563 * Cgetmatch will return 0 if name is one of the names of the capability 564 * record buf, -1 if not. 565 */ 566 int 567 cgetmatch(buf, name) 568 char *buf, *name; 569 { 570 register char *np, *bp; 571 572 /* 573 * Start search at beginning of record. 574 */ 575 bp = buf; 576 for (;;) { 577 /* 578 * Try to match a record name. 579 */ 580 np = name; 581 for (;;) 582 if (*np == '\0') 583 if (*bp == '|' || *bp == ':' || *bp == '\0') 584 return (0); 585 else 586 break; 587 else 588 if (*bp++ != *np++) 589 break; 590 591 /* 592 * Match failed, skip to next name in record. 593 */ 594 bp--; /* a '|' or ':' may have stopped the match */ 595 for (;;) 596 if (*bp == '\0' || *bp == ':') 597 return (-1); /* match failed totally */ 598 else 599 if (*bp++ == '|') 600 break; /* found next name */ 601 } 602 } 603 604 605 606 607 608 int 609 cgetfirst(buf, db_array) 610 char **buf, **db_array; 611 { 612 (void)cgetclose(); 613 return (cgetnext(buf, db_array)); 614 } 615 616 static FILE *pfp; 617 static int slash; 618 static char **dbp; 619 620 int 621 cgetclose() 622 { 623 if (pfp != NULL) { 624 (void)fclose(pfp); 625 pfp = NULL; 626 } 627 dbp = NULL; 628 gottoprec = 0; 629 slash = 0; 630 return(0); 631 } 632 633 /* 634 * Cgetnext() gets either the first or next entry in the logical database 635 * specified by db_array. It returns 0 upon completion of the database, 1 636 * upon returning an entry with more remaining, and -1 if an error occurs. 637 */ 638 int 639 cgetnext(bp, db_array) 640 register char **bp; 641 char **db_array; 642 { 643 size_t len; 644 int status, i, done; 645 char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 646 u_int dummy; 647 648 if (dbp == NULL) 649 dbp = db_array; 650 651 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 652 (void)cgetclose(); 653 return (-1); 654 } 655 for(;;) { 656 if (toprec && !gottoprec) { 657 gottoprec = 1; 658 line = toprec; 659 } else { 660 line = fgetline(pfp, &len); 661 if (line == NULL && pfp) { 662 (void)fclose(pfp); 663 if (ferror(pfp)) { 664 (void)cgetclose(); 665 return (-1); 666 } else { 667 if (*++dbp == NULL) { 668 (void)cgetclose(); 669 return (0); 670 } else if ((pfp = 671 fopen(*dbp, "r")) == NULL) { 672 (void)cgetclose(); 673 return (-1); 674 } else 675 continue; 676 } 677 } 678 if (len == 0) { 679 slash = 0; 680 continue; 681 } 682 if (isspace(*line) || 683 *line == ':' || *line == '#' || slash) { 684 if (line[len - 1] == '\\') 685 slash = 1; 686 else 687 slash = 0; 688 continue; 689 } 690 if (line[len - 1] == '\\') 691 slash = 1; 692 else 693 slash = 0; 694 } 695 696 697 /* 698 * Line points to a name line. 699 */ 700 i = 0; 701 done = 0; 702 np = nbuf; 703 for (;;) { 704 for (cp = line; *cp != '\0'; cp++) { 705 if (*cp == ':') { 706 *np++ = ':'; 707 done = 1; 708 break; 709 } 710 if (*cp == '\\') 711 break; 712 *np++ = *cp; 713 } 714 if (done) { 715 *np = '\0'; 716 break; 717 } else { /* name field extends beyond the line */ 718 line = fgetline(pfp, &len); 719 if (line == NULL && pfp) { 720 (void)fclose(pfp); 721 if (ferror(pfp)) { 722 (void)cgetclose(); 723 return (-1); 724 } 725 } 726 } 727 } 728 rp = buf; 729 for(cp = nbuf; *cp != NULL; cp++) 730 if (*cp == '|' || *cp == ':') 731 break; 732 else 733 *rp++ = *cp; 734 735 *rp = '\0'; 736 /* 737 * XXX 738 * Last argument of getent here should be nbuf if we want true 739 * sequential access in the case of duplicates. 740 * With NULL, getent will return the first entry found 741 * rather than the duplicate entry record. This is a 742 * matter of semantics that should be resolved. 743 */ 744 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 745 if (status == -2 || status == -3) 746 (void)cgetclose(); 747 748 return (status + 1); 749 } 750 /* NOTREACHED */ 751 } 752 753 /* 754 * Cgetstr retrieves the value of the string capability cap from the 755 * capability record pointed to by buf. A pointer to a decoded, NUL 756 * terminated, malloc'd copy of the string is returned in the char * 757 * pointed to by str. The length of the string not including the trailing 758 * NUL is returned on success, -1 if the requested string capability 759 * couldn't be found, -2 if a system error was encountered (storage 760 * allocation failure). 761 */ 762 int 763 cgetstr(buf, cap, str) 764 char *buf, *cap; 765 char **str; 766 { 767 register u_int m_room; 768 register char *bp, *mp; 769 int len; 770 char *mem; 771 772 /* 773 * Find string capability cap 774 */ 775 bp = cgetcap(buf, cap, '='); 776 if (bp == NULL) 777 return (-1); 778 779 /* 780 * Conversion / storage allocation loop ... Allocate memory in 781 * chunks SFRAG in size. 782 */ 783 if ((mem = malloc(SFRAG)) == NULL) { 784 errno = ENOMEM; 785 return (-2); /* couldn't even allocate the first fragment */ 786 } 787 m_room = SFRAG; 788 mp = mem; 789 790 while (*bp != ':' && *bp != '\0') { 791 /* 792 * Loop invariants: 793 * There is always room for one more character in mem. 794 * Mp always points just past last character in mem. 795 * Bp always points at next character in buf. 796 */ 797 if (*bp == '^') { 798 bp++; 799 if (*bp == ':' || *bp == '\0') 800 break; /* drop unfinished escape */ 801 *mp++ = *bp++ & 037; 802 } else if (*bp == '\\') { 803 bp++; 804 if (*bp == ':' || *bp == '\0') 805 break; /* drop unfinished escape */ 806 if ('0' <= *bp && *bp <= '7') { 807 register int n, i; 808 809 n = 0; 810 i = 3; /* maximum of three octal digits */ 811 do { 812 n = n * 8 + (*bp++ - '0'); 813 } while (--i && '0' <= *bp && *bp <= '7'); 814 *mp++ = n; 815 } 816 else switch (*bp++) { 817 case 'b': case 'B': 818 *mp++ = '\b'; 819 break; 820 case 't': case 'T': 821 *mp++ = '\t'; 822 break; 823 case 'n': case 'N': 824 *mp++ = '\n'; 825 break; 826 case 'f': case 'F': 827 *mp++ = '\f'; 828 break; 829 case 'r': case 'R': 830 *mp++ = '\r'; 831 break; 832 case 'e': case 'E': 833 *mp++ = ESC; 834 break; 835 case 'c': case 'C': 836 *mp++ = ':'; 837 break; 838 default: 839 /* 840 * Catches '\', '^', and 841 * everything else. 842 */ 843 *mp++ = *(bp-1); 844 break; 845 } 846 } else 847 *mp++ = *bp++; 848 m_room--; 849 850 /* 851 * Enforce loop invariant: if no room left in current 852 * buffer, try to get some more. 853 */ 854 if (m_room == 0) { 855 size_t size = mp - mem; 856 857 if ((mem = realloc(mem, size + SFRAG)) == NULL) 858 return (-2); 859 m_room = SFRAG; 860 mp = mem + size; 861 } 862 } 863 *mp++ = '\0'; /* loop invariant let's us do this */ 864 m_room--; 865 len = mp - mem - 1; 866 867 /* 868 * Give back any extra memory and return value and success. 869 */ 870 if (m_room != 0) 871 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 872 return (-2); 873 *str = mem; 874 return (len); 875 } 876 877 /* 878 * Cgetustr retrieves the value of the string capability cap from the 879 * capability record pointed to by buf. The difference between cgetustr() 880 * and cgetstr() is that cgetustr does not decode escapes but rather treats 881 * all characters literally. A pointer to a NUL terminated malloc'd 882 * copy of the string is returned in the char pointed to by str. The 883 * length of the string not including the trailing NUL is returned on success, 884 * -1 if the requested string capability couldn't be found, -2 if a system 885 * error was encountered (storage allocation failure). 886 */ 887 int 888 cgetustr(buf, cap, str) 889 char *buf, *cap, **str; 890 { 891 register u_int m_room; 892 register char *bp, *mp; 893 int len; 894 char *mem; 895 896 /* 897 * Find string capability cap 898 */ 899 if ((bp = cgetcap(buf, cap, '=')) == NULL) 900 return (-1); 901 902 /* 903 * Conversion / storage allocation loop ... Allocate memory in 904 * chunks SFRAG in size. 905 */ 906 if ((mem = malloc(SFRAG)) == NULL) { 907 errno = ENOMEM; 908 return (-2); /* couldn't even allocate the first fragment */ 909 } 910 m_room = SFRAG; 911 mp = mem; 912 913 while (*bp != ':' && *bp != '\0') { 914 /* 915 * Loop invariants: 916 * There is always room for one more character in mem. 917 * Mp always points just past last character in mem. 918 * Bp always points at next character in buf. 919 */ 920 *mp++ = *bp++; 921 m_room--; 922 923 /* 924 * Enforce loop invariant: if no room left in current 925 * buffer, try to get some more. 926 */ 927 if (m_room == 0) { 928 size_t size = mp - mem; 929 930 if ((mem = realloc(mem, size + SFRAG)) == NULL) 931 return (-2); 932 m_room = SFRAG; 933 mp = mem + size; 934 } 935 } 936 *mp++ = '\0'; /* loop invariant let's us do this */ 937 m_room--; 938 len = mp - mem - 1; 939 940 /* 941 * Give back any extra memory and return value and success. 942 */ 943 if (m_room != 0) 944 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 945 return (-2); 946 *str = mem; 947 return (len); 948 } 949 950 /* 951 * Cgetnum retrieves the value of the numeric capability cap from the 952 * capability record pointed to by buf. The numeric value is returned in 953 * the long pointed to by num. 0 is returned on success, -1 if the requested 954 * numeric capability couldn't be found. 955 */ 956 int 957 cgetnum(buf, cap, num) 958 char *buf, *cap; 959 long *num; 960 { 961 register long n; 962 register int base, digit; 963 register char *bp; 964 965 /* 966 * Find numeric capability cap 967 */ 968 bp = cgetcap(buf, cap, '#'); 969 if (bp == NULL) 970 return (-1); 971 972 /* 973 * Look at value and determine numeric base: 974 * 0x... or 0X... hexadecimal, 975 * else 0... octal, 976 * else decimal. 977 */ 978 if (*bp == '0') { 979 bp++; 980 if (*bp == 'x' || *bp == 'X') { 981 bp++; 982 base = 16; 983 } else 984 base = 8; 985 } else 986 base = 10; 987 988 /* 989 * Conversion loop ... 990 */ 991 n = 0; 992 for (;;) { 993 if ('0' <= *bp && *bp <= '9') 994 digit = *bp - '0'; 995 else if ('a' <= *bp && *bp <= 'f') 996 digit = 10 + *bp - 'a'; 997 else if ('A' <= *bp && *bp <= 'F') 998 digit = 10 + *bp - 'A'; 999 else 1000 break; 1001 1002 if (digit >= base) 1003 break; 1004 1005 n = n * base + digit; 1006 bp++; 1007 } 1008 1009 /* 1010 * Return value and success. 1011 */ 1012 *num = n; 1013 return (0); 1014 } 1015 1016 1017 /* 1018 * Compare name field of record. 1019 */ 1020 static int 1021 nfcmp(nf, rec) 1022 char *nf, *rec; 1023 { 1024 char *cp, tmp; 1025 int ret; 1026 1027 for (cp = rec; *cp != ':'; cp++) 1028 ; 1029 1030 tmp = *(cp + 1); 1031 *(cp + 1) = '\0'; 1032 ret = strcmp(nf, rec); 1033 *(cp + 1) = tmp; 1034 1035 return (ret); 1036 } 1037