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