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