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 <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 62 static size_t topreclen; /* toprec length */ 63 static char *toprec; /* Additional record specified by cgetset() */ 64 static int gottoprec; /* Flag indicating retrieval of toprecord */ 65 66 static int cdbget __P((DB *, char **, char *)); 67 static int getent __P((char **, u_int *, char **, int, char *, int, char *)); 68 static int nfcmp __P((char *, char *)); 69 70 /* 71 * Cgetset() allows the addition of a user specified buffer to be added 72 * to the database array, in effect "pushing" the buffer on top of the 73 * virtual database. 0 is returned on success, -1 on failure. 74 */ 75 int 76 cgetset(ent) 77 char *ent; 78 { 79 if (ent == NULL) { 80 if (toprec) 81 free(toprec); 82 toprec = NULL; 83 topreclen = 0; 84 return (0); 85 } 86 topreclen = strlen(ent); 87 if ((toprec = malloc (topreclen + 1)) == NULL) { 88 errno = ENOMEM; 89 return (-1); 90 } 91 gottoprec = 0; 92 (void)strcpy(toprec, ent); 93 return (0); 94 } 95 96 /* 97 * Cgetcap searches the capability record buf for the capability cap with 98 * type `type'. A pointer to the value of cap is returned on success, NULL 99 * if the requested capability couldn't be found. 100 * 101 * Specifying a type of ':' means that nothing should follow cap (:cap:). 102 * In this case a pointer to the terminating ':' or NUL will be returned if 103 * cap is found. 104 * 105 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 106 * return NULL. 107 */ 108 char * 109 cgetcap(buf, cap, type) 110 char *buf, *cap; 111 int type; 112 { 113 register char *bp, *cp; 114 115 bp = buf; 116 for (;;) { 117 /* 118 * Skip past the current capability field - it's either the 119 * name field if this is the first time through the loop, or 120 * the remainder of a field whose name failed to match cap. 121 */ 122 for (;;) 123 if (*bp == '\0') 124 return (NULL); 125 else 126 if (*bp++ == ':') 127 break; 128 129 /* 130 * Try to match (cap, type) in buf. 131 */ 132 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 133 continue; 134 if (*cp != '\0') 135 continue; 136 if (*bp == '@') 137 return (NULL); 138 if (type == ':') { 139 if (*bp != '\0' && *bp != ':') 140 continue; 141 return(bp); 142 } 143 if (*bp != type) 144 continue; 145 bp++; 146 return (*bp == '@' ? NULL : bp); 147 } 148 /* NOTREACHED */ 149 } 150 151 /* 152 * Cgetent extracts the capability record name from the NULL terminated file 153 * array db_array and returns a pointer to a malloc'd copy of it in buf. 154 * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 155 * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 156 * -1 if the requested record couldn't be found, -2 if a system error was 157 * encountered (couldn't open/read a file, etc.), and -3 if a potential 158 * reference loop is detected. 159 */ 160 int 161 cgetent(buf, db_array, name) 162 char **buf, **db_array, *name; 163 { 164 u_int dummy; 165 166 return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 167 } 168 169 /* 170 * Getent implements the functions of cgetent. If fd is non-negative, 171 * *db_array has already been opened and fd is the open file descriptor. We 172 * do this to save time and avoid using up file descriptors for tc= 173 * recursions. 174 * 175 * Getent returns the same success/failure codes as cgetent. On success, a 176 * pointer to a malloc'ed capability record with all tc= capabilities fully 177 * expanded and its length (not including trailing ASCII NUL) are left in 178 * *cap and *len. 179 * 180 * Basic algorithm: 181 * + Allocate memory incrementally as needed in chunks of size BFRAG 182 * for capability buffer. 183 * + Recurse for each tc=name and interpolate result. Stop when all 184 * names interpolated, a name can't be found, or depth exceeds 185 * MAX_RECURSION. 186 */ 187 static int 188 getent(cap, len, db_array, fd, name, depth, nfield) 189 char **cap, **db_array, *name, *nfield; 190 u_int *len; 191 int fd, depth; 192 { 193 DB *capdbp; 194 DBT key, data; 195 register char *r_end, *rp, **db_p; 196 int myfd, eof, foundit, retval; 197 char *record; 198 int tc_not_resolved; 199 char pbuf[_POSIX_PATH_MAX]; 200 201 /* 202 * Return with ``loop detected'' error if we've recursed more than 203 * MAX_RECURSION times. 204 */ 205 if (depth > MAX_RECURSION) 206 return (-3); 207 208 /* 209 * Check if we have a top record from cgetset(). 210 */ 211 if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 212 if ((record = malloc (topreclen + BFRAG)) == NULL) { 213 errno = ENOMEM; 214 return (-2); 215 } 216 (void)strcpy(record, toprec); 217 myfd = 0; 218 db_p = db_array; 219 rp = record + topreclen + 1; 220 r_end = rp + BFRAG; 221 goto tc_exp; 222 } 223 /* 224 * Allocate first chunk of memory. 225 */ 226 if ((record = malloc(BFRAG)) == NULL) { 227 errno = ENOMEM; 228 return (-2); 229 } 230 r_end = record + BFRAG; 231 foundit = 0; 232 /* 233 * Loop through database array until finding the record. 234 */ 235 236 for (db_p = db_array; *db_p != NULL; db_p++) { 237 eof = 0; 238 239 /* 240 * Open database if not already open. 241 */ 242 243 if (fd >= 0) { 244 (void)lseek(fd, 0L, L_SET); 245 myfd = 0; 246 } else { 247 sprintf(pbuf, "%s.db", *db_p); 248 if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 249 != NULL) { 250 free(record); 251 retval = cdbget(capdbp, &record, name); 252 if (capdbp->close(capdbp) < 0) 253 return (-2); 254 *cap = malloc (strlen(record) + 1); 255 strcpy(*cap, record); 256 return (retval); 257 } else { 258 fd = open(*db_p, O_RDONLY, 0); 259 if (fd < 0) { 260 /* No error on unfound file. */ 261 if (errno == ENOENT) 262 continue; 263 free(record); 264 return (-2); 265 } 266 myfd = 1; 267 } 268 } 269 /* 270 * Find the requested capability record ... 271 */ 272 { 273 char buf[BUFSIZ]; 274 register char *b_end, *bp; 275 register int c; 276 277 /* 278 * Loop invariants: 279 * There is always room for one more character in record. 280 * R_end always points just past end of record. 281 * Rp always points just past last character in record. 282 * B_end always points just past last character in buf. 283 * Bp always points at next character in buf. 284 */ 285 b_end = buf; 286 bp = buf; 287 for (;;) { 288 289 /* 290 * Read in a line implementing (\, newline) 291 * line continuation. 292 */ 293 rp = record; 294 for (;;) { 295 if (bp >= b_end) { 296 int n; 297 298 n = read(fd, buf, sizeof(buf)); 299 if (n <= 0) { 300 if (myfd) 301 (void)close(fd); 302 if (n < 0) { 303 free(record); 304 return (-2); 305 } else { 306 fd = -1; 307 eof = 1; 308 break; 309 } 310 } 311 b_end = buf+n; 312 bp = buf; 313 } 314 315 c = *bp++; 316 if (c == '\n') { 317 if (rp > record && *(rp-1) == '\\') { 318 rp--; 319 continue; 320 } else 321 break; 322 } 323 *rp++ = c; 324 325 /* 326 * Enforce loop invariant: if no room 327 * left in record buffer, try to get 328 * some more. 329 */ 330 if (rp >= r_end) { 331 u_int pos; 332 size_t newsize; 333 334 pos = rp - record; 335 newsize = r_end - record + BFRAG; 336 record = realloc(record, newsize); 337 if (record == NULL) { 338 errno = ENOMEM; 339 if (myfd) 340 (void)close(fd); 341 return (-2); 342 } 343 r_end = record + newsize; 344 rp = record + pos; 345 } 346 } 347 /* loop invariant let's us do this */ 348 *rp++ = '\0'; 349 350 /* 351 * If encountered eof check next file. 352 */ 353 if (eof) 354 break; 355 356 /* 357 * Toss blank lines and comments. 358 */ 359 if (*record == '\0' || *record == '#') 360 continue; 361 362 /* 363 * See if this is the record we want ... 364 */ 365 if (cgetmatch(record, name) == 0) { 366 if (nfield == NULL || !nfcmp(nfield, record)) { 367 foundit = 1; 368 break; /* found it! */ 369 } 370 } 371 } 372 } 373 if (foundit) 374 break; 375 } 376 377 if (!foundit) 378 return (-1); 379 380 /* 381 * Got the capability record, but now we have to expand all tc=name 382 * references in it ... 383 */ 384 tc_exp: { 385 register char *newicap, *s; 386 register int newilen; 387 u_int ilen; 388 int diff, iret, tclen; 389 char *icap, *scan, *tc, *tcstart, *tcend; 390 391 /* 392 * Loop invariants: 393 * There is room for one more character in record. 394 * R_end points just past end of record. 395 * Rp points just past last character in record. 396 * Scan points at remainder of record that needs to be 397 * scanned for tc=name constructs. 398 */ 399 scan = record; 400 tc_not_resolved = 0; 401 for (;;) { 402 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 403 break; 404 405 /* 406 * Find end of tc=name and stomp on the trailing `:' 407 * (if present) so we can use it to call ourselves. 408 */ 409 s = tc; 410 for (;;) 411 if (*s == '\0') 412 break; 413 else 414 if (*s++ == ':') { 415 *(s - 1) = '\0'; 416 break; 417 } 418 tcstart = tc - 3; 419 tclen = s - tcstart; 420 tcend = s; 421 422 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 423 NULL); 424 newicap = icap; /* Put into a register. */ 425 newilen = ilen; 426 if (iret != 0) { 427 /* an error */ 428 if (iret < -1) { 429 if (myfd) 430 (void)close(fd); 431 free(record); 432 return (iret); 433 } 434 if (iret == 1) 435 tc_not_resolved = 1; 436 /* couldn't resolve tc */ 437 if (iret == -1) { 438 *(s - 1) = ':'; 439 scan = s - 1; 440 tc_not_resolved = 1; 441 continue; 442 443 } 444 } 445 /* not interested in name field of tc'ed record */ 446 s = newicap; 447 for (;;) 448 if (*s == '\0') 449 break; 450 else 451 if (*s++ == ':') 452 break; 453 newilen -= s - newicap; 454 newicap = s; 455 456 /* make sure interpolated record is `:'-terminated */ 457 s += newilen; 458 if (*(s-1) != ':') { 459 *s = ':'; /* overwrite NUL with : */ 460 newilen++; 461 } 462 463 /* 464 * Make sure there's enough room to insert the 465 * new record. 466 */ 467 diff = newilen - tclen; 468 if (diff >= r_end - rp) { 469 u_int pos, tcpos, tcposend; 470 size_t newsize; 471 472 pos = rp - record; 473 newsize = r_end - record + diff + BFRAG; 474 tcpos = tcstart - record; 475 tcposend = tcend - record; 476 record = realloc(record, newsize); 477 if (record == NULL) { 478 errno = ENOMEM; 479 if (myfd) 480 (void)close(fd); 481 free(icap); 482 return (-2); 483 } 484 r_end = record + newsize; 485 rp = record + pos; 486 tcstart = record + tcpos; 487 tcend = record + tcposend; 488 } 489 490 /* 491 * Insert tc'ed record into our record. 492 */ 493 s = tcstart + newilen; 494 bcopy(tcend, s, rp - tcend); 495 bcopy(newicap, tcstart, newilen); 496 rp += diff; 497 free(icap); 498 499 /* 500 * Start scan on `:' so next cgetcap works properly 501 * (cgetcap always skips first field). 502 */ 503 scan = s-1; 504 } 505 506 } 507 /* 508 * Close file (if we opened it), give back any extra memory, and 509 * return capability, length and success. 510 */ 511 if (myfd) 512 (void)close(fd); 513 *len = rp - record - 1; /* don't count NUL */ 514 if (r_end > rp) 515 if ((record = 516 realloc(record, (size_t)(rp - record))) == NULL) { 517 errno = ENOMEM; 518 return (-2); 519 } 520 521 *cap = record; 522 if (tc_not_resolved) 523 return (1); 524 return (0); 525 } 526 527 static int 528 cdbget(capdbp, bp, name) 529 DB *capdbp; 530 char **bp, *name; 531 { 532 DBT key, data; 533 char *buf; 534 int st; 535 536 key.data = name; 537 key.size = strlen(name) + 1; 538 539 540 /* 541 * Get the reference. 542 */ 543 if ((st = capdbp->get(capdbp, &key, &data, 0)) < 0) 544 return(-2); 545 if (st == 1) 546 return(-1); 547 548 if ((buf = malloc(data.size - 1)) == NULL) 549 return(-2); 550 551 strcpy(buf, (char *)(data.data)); 552 553 key.data = buf; 554 key.size = data.size; 555 556 /* 557 * Get the record. 558 */ 559 if (capdbp->get(capdbp, &key, &data, 0) < 0) { 560 free(buf); 561 return(-2); 562 } 563 free(buf); 564 *bp = &((char *)(data.data))[1]; 565 if (((char *)(data.data))[0] == TCERR) 566 return 1; 567 else 568 return 0; 569 } 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 = fgetline(pfp, &len); 671 if (line == NULL && pfp) { 672 (void)fclose(pfp); 673 if (ferror(pfp)) { 674 (void)cgetclose(); 675 return (-1); 676 } else { 677 dbp++; 678 if (*dbp == NULL) { 679 (void)cgetclose(); 680 return (0); 681 } else if ((pfp = fopen(*dbp, "r")) == 682 NULL) { 683 (void)cgetclose(); 684 return (-1); 685 } else 686 continue; 687 } 688 } 689 if (isspace(*line) || *line == ':' || *line == '#' 690 || len == 0 || slash) { 691 if (len > 0 && line[len - 1] == '\\') 692 slash = 1; 693 else 694 slash = 0; 695 continue; 696 } 697 if (len > 0 && line[len - 1] == '\\') 698 slash = 1; 699 else 700 slash = 0; 701 } 702 703 704 /* 705 * Line points to a name line. 706 */ 707 i = 0; 708 done = 0; 709 np = nbuf; 710 for (;;) { 711 for (cp = line; *cp != '\0'; cp++) { 712 if (*cp == ':') { 713 *np++ = ':'; 714 done = 1; 715 break; 716 } 717 if (*cp == '\\') 718 break; 719 *np++ = *cp; 720 } 721 if (done) { 722 *np = '\0'; 723 break; 724 } else { /* name field extends beyond the line */ 725 line = fgetline(pfp, &len); 726 if (line == NULL && pfp) { 727 (void)fclose(pfp); 728 if (ferror(pfp)) { 729 (void)cgetclose(); 730 return (-1); 731 } 732 } 733 } 734 } 735 rp = buf; 736 for(cp = nbuf; *cp != NULL; cp++) 737 if (*cp == '|' || *cp == ':') 738 break; 739 else 740 *rp++ = *cp; 741 742 *rp = '\0'; 743 /* 744 * XXX 745 * Last argument of getent here should be nbuf if we want true 746 * sequential access in the case of duplicates. 747 * With NULL, getent will return the first entry found 748 * rather than the duplicate entry record. This is a 749 * matter of semantics that should be resolved. 750 */ 751 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 752 if (status == -2 || status == -3) 753 (void)cgetclose(); 754 755 return (status + 1); 756 } 757 /* NOTREACHED */ 758 } 759 760 /* 761 * Cgetstr retrieves the value of the string capability cap from the 762 * capability record pointed to by buf. A pointer to a decoded, NUL 763 * terminated, malloc'd copy of the string is returned in the char * 764 * pointed to by str. The length of the string not including the trailing 765 * NUL is returned on success, -1 if the requested string capability 766 * couldn't be found, -2 if a system error was encountered (storage 767 * allocation failure). 768 */ 769 int 770 cgetstr(buf, cap, str) 771 char *buf, *cap; 772 char **str; 773 { 774 register u_int m_room; 775 register char *bp, *mp; 776 int len; 777 char *mem; 778 779 /* 780 * Find string capability cap 781 */ 782 bp = cgetcap(buf, cap, '='); 783 if (bp == NULL) 784 return (-1); 785 786 /* 787 * Conversion / storage allocation loop ... Allocate memory in 788 * chunks SFRAG in size. 789 */ 790 if ((mem = malloc(SFRAG)) == NULL) { 791 errno = ENOMEM; 792 return (-2); /* couldn't even allocate the first fragment */ 793 } 794 m_room = SFRAG; 795 mp = mem; 796 797 while (*bp != ':' && *bp != '\0') { 798 /* 799 * Loop invariants: 800 * There is always room for one more character in mem. 801 * Mp always points just past last character in mem. 802 * Bp always points at next character in buf. 803 */ 804 if (*bp == '^') { 805 bp++; 806 if (*bp == ':' || *bp == '\0') 807 break; /* drop unfinished escape */ 808 *mp++ = *bp++ & 037; 809 } else if (*bp == '\\') { 810 bp++; 811 if (*bp == ':' || *bp == '\0') 812 break; /* drop unfinished escape */ 813 if ('0' <= *bp && *bp <= '7') { 814 register int n, i; 815 816 n = 0; 817 i = 3; /* maximum of three octal digits */ 818 do { 819 n = n * 8 + (*bp++ - '0'); 820 } while (--i && '0' <= *bp && *bp <= '7'); 821 *mp++ = n; 822 } 823 else switch (*bp++) { 824 case 'b': case 'B': 825 *mp++ = '\b'; 826 break; 827 case 't': case 'T': 828 *mp++ = '\t'; 829 break; 830 case 'n': case 'N': 831 *mp++ = '\n'; 832 break; 833 case 'f': case 'F': 834 *mp++ = '\f'; 835 break; 836 case 'r': case 'R': 837 *mp++ = '\r'; 838 break; 839 case 'e': case 'E': 840 *mp++ = ESC; 841 break; 842 case 'c': case 'C': 843 *mp++ = ':'; 844 break; 845 default: 846 /* 847 * Catches '\', '^', and 848 * everything else. 849 */ 850 *mp++ = *(bp-1); 851 break; 852 } 853 } else 854 *mp++ = *bp++; 855 m_room--; 856 857 /* 858 * Enforce loop invariant: if no room left in current 859 * buffer, try to get some more. 860 */ 861 if (m_room == 0) { 862 size_t size = mp - mem; 863 864 if ((mem = realloc(mem, size + SFRAG)) == NULL) 865 return (-2); 866 m_room = SFRAG; 867 mp = mem + size; 868 } 869 } 870 *mp++ = '\0'; /* loop invariant let's us do this */ 871 m_room--; 872 len = mp - mem - 1; 873 874 /* 875 * Give back any extra memory and return value and success. 876 */ 877 if (m_room != 0) 878 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 879 return (-2); 880 *str = mem; 881 return (len); 882 } 883 884 /* 885 * Cgetustr retrieves the value of the string capability cap from the 886 * capability record pointed to by buf. The difference between cgetustr() 887 * and cgetstr() is that cgetustr does not decode escapes but rather treats 888 * all characters literally. A pointer to a NUL terminated malloc'd 889 * copy of the string is returned in the char pointed to by str. The 890 * length of the string not including the trailing NUL is returned on success, 891 * -1 if the requested string capability couldn't be found, -2 if a system 892 * error was encountered (storage allocation failure). 893 */ 894 int 895 cgetustr(buf, cap, str) 896 char *buf, *cap, **str; 897 { 898 register u_int m_room; 899 register char *bp, *mp; 900 int len; 901 char *mem; 902 903 /* 904 * Find string capability cap 905 */ 906 if ((bp = cgetcap(buf, cap, '=')) == NULL) 907 return (-1); 908 909 /* 910 * Conversion / storage allocation loop ... Allocate memory in 911 * chunks SFRAG in size. 912 */ 913 if ((mem = malloc(SFRAG)) == NULL) { 914 errno = ENOMEM; 915 return (-2); /* couldn't even allocate the first fragment */ 916 } 917 m_room = SFRAG; 918 mp = mem; 919 920 while (*bp != ':' && *bp != '\0') { 921 /* 922 * Loop invariants: 923 * There is always room for one more character in mem. 924 * Mp always points just past last character in mem. 925 * Bp always points at next character in buf. 926 */ 927 *mp++ = *bp++; 928 m_room--; 929 930 /* 931 * Enforce loop invariant: if no room left in current 932 * buffer, try to get some more. 933 */ 934 if (m_room == 0) { 935 size_t size = mp - mem; 936 937 if ((mem = realloc(mem, size + SFRAG)) == NULL) 938 return (-2); 939 m_room = SFRAG; 940 mp = mem + size; 941 } 942 } 943 *mp++ = '\0'; /* loop invariant let's us do this */ 944 m_room--; 945 len = mp - mem - 1; 946 947 /* 948 * Give back any extra memory and return value and success. 949 */ 950 if (m_room != 0) 951 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 952 return (-2); 953 *str = mem; 954 return (len); 955 } 956 957 /* 958 * Cgetnum retrieves the value of the numeric capability cap from the 959 * capability record pointed to by buf. The numeric value is returned in 960 * the long pointed to by num. 0 is returned on success, -1 if the requested 961 * numeric capability couldn't be found. 962 */ 963 int 964 cgetnum(buf, cap, num) 965 char *buf, *cap; 966 long *num; 967 { 968 register long n; 969 register int base, digit; 970 register char *bp; 971 972 /* 973 * Find numeric capability cap 974 */ 975 bp = cgetcap(buf, cap, '#'); 976 if (bp == NULL) 977 return (-1); 978 979 /* 980 * Look at value and determine numeric base: 981 * 0x... or 0X... hexadecimal, 982 * else 0... octal, 983 * else decimal. 984 */ 985 if (*bp == '0') { 986 bp++; 987 if (*bp == 'x' || *bp == 'X') { 988 bp++; 989 base = 16; 990 } else 991 base = 8; 992 } else 993 base = 10; 994 995 /* 996 * Conversion loop ... 997 */ 998 n = 0; 999 for (;;) { 1000 if ('0' <= *bp && *bp <= '9') 1001 digit = *bp - '0'; 1002 else 1003 if ( ('a' <= *bp && *bp <= 'f') 1004 || ('A' <= *bp && *bp <= 'F')) 1005 digit = 10 + *bp - 'a'; 1006 else 1007 break; 1008 1009 if (digit >= base) 1010 break; 1011 1012 n = n * base + digit; 1013 bp++; 1014 } 1015 1016 /* 1017 * Return value and success. 1018 */ 1019 *num = n; 1020 return (0); 1021 } 1022 1023 1024 /* 1025 * Compare name field of record. 1026 */ 1027 static int 1028 nfcmp(nf, rec) 1029 char *nf, *rec; 1030 { 1031 char *cp, tmp; 1032 int ret; 1033 1034 for (cp = rec; *cp != ':'; cp++) 1035 ; 1036 1037 tmp = *(cp + 1); 1038 *(cp + 1) = '\0'; 1039 ret = strcmp(nf, rec); 1040 *(cp + 1) = tmp; 1041 1042 return (ret); 1043 } 1044