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