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