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.11 (Berkeley) 09/13/92"; 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 } 663 if (isspace(*line) || *line == ':' || *line == '#' 664 || len == 0 || slash) { 665 if (len > 0 && line[len - 1] == '\\') 666 slash = 1; 667 else 668 slash = 0; 669 continue; 670 } 671 if (len > 0 && line[len - 1] == '\\') 672 slash = 1; 673 else 674 slash = 0; 675 } 676 677 678 /* 679 * Line points to a name line. 680 */ 681 i = 0; 682 done = 0; 683 np = nbuf; 684 for (;;) { 685 for (cp = line; *cp != '\0'; cp++) { 686 if (*cp == ':') { 687 *np++ = ':'; 688 done = 1; 689 break; 690 } 691 if (*cp == '\\') 692 break; 693 *np++ = *cp; 694 } 695 if (done) { 696 *np = '\0'; 697 break; 698 } else { /* name field extends beyond the line */ 699 line = fgetline(pfp, &len); 700 if (line == NULL && pfp) { 701 (void)fclose(pfp); 702 if (ferror(pfp)) { 703 (void)cgetclose(); 704 return (-1); 705 } 706 } 707 } 708 } 709 rp = buf; 710 for(cp = nbuf; *cp != NULL; cp++) 711 if (*cp == '|' || *cp == ':') 712 break; 713 else 714 *rp++ = *cp; 715 716 *rp = '\0'; 717 /* 718 * XXX 719 * Last argument of getent here should be nbuf if we want true 720 * sequential access in the case of duplicates. 721 * With NULL, getent will return the first entry found 722 * rather than the duplicate entry record. This is a 723 * matter of semantics that should be resolved. 724 */ 725 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 726 if (status == -2 || status == -3) 727 (void)cgetclose(); 728 729 return (status + 1); 730 } 731 /* NOTREACHED */ 732 } 733 734 /* 735 * Cgetstr retrieves the value of the string capability cap from the 736 * capability record pointed to by buf. A pointer to a decoded, NUL 737 * terminated, malloc'd copy of the string is returned in the char * 738 * pointed to by str. The length of the string not including the trailing 739 * NUL is returned on success, -1 if the requested string capability 740 * couldn't be found, -2 if a system error was encountered (storage 741 * allocation failure). 742 */ 743 int 744 cgetstr(buf, cap, str) 745 char *buf, *cap; 746 char **str; 747 { 748 register u_int m_room; 749 register char *bp, *mp; 750 int len; 751 char *mem; 752 753 /* 754 * Find string capability cap 755 */ 756 bp = cgetcap(buf, cap, '='); 757 if (bp == NULL) 758 return (-1); 759 760 /* 761 * Conversion / storage allocation loop ... Allocate memory in 762 * chunks SFRAG in size. 763 */ 764 if ((mem = malloc(SFRAG)) == NULL) { 765 errno = ENOMEM; 766 return (-2); /* couldn't even allocate the first fragment */ 767 } 768 m_room = SFRAG; 769 mp = mem; 770 771 while (*bp != ':' && *bp != '\0') { 772 /* 773 * Loop invariants: 774 * There is always room for one more character in mem. 775 * Mp always points just past last character in mem. 776 * Bp always points at next character in buf. 777 */ 778 if (*bp == '^') { 779 bp++; 780 if (*bp == ':' || *bp == '\0') 781 break; /* drop unfinished escape */ 782 *mp++ = *bp++ & 037; 783 } else if (*bp == '\\') { 784 bp++; 785 if (*bp == ':' || *bp == '\0') 786 break; /* drop unfinished escape */ 787 if ('0' <= *bp && *bp <= '7') { 788 register int n, i; 789 790 n = 0; 791 i = 3; /* maximum of three octal digits */ 792 do { 793 n = n * 8 + (*bp++ - '0'); 794 } while (--i && '0' <= *bp && *bp <= '7'); 795 *mp++ = n; 796 } 797 else switch (*bp++) { 798 case 'b': case 'B': 799 *mp++ = '\b'; 800 break; 801 case 't': case 'T': 802 *mp++ = '\t'; 803 break; 804 case 'n': case 'N': 805 *mp++ = '\n'; 806 break; 807 case 'f': case 'F': 808 *mp++ = '\f'; 809 break; 810 case 'r': case 'R': 811 *mp++ = '\r'; 812 break; 813 case 'e': case 'E': 814 *mp++ = ESC; 815 break; 816 case 'c': case 'C': 817 *mp++ = ':'; 818 break; 819 default: 820 /* 821 * Catches '\', '^', and 822 * everything else. 823 */ 824 *mp++ = *(bp-1); 825 break; 826 } 827 } else 828 *mp++ = *bp++; 829 m_room--; 830 831 /* 832 * Enforce loop invariant: if no room left in current 833 * buffer, try to get some more. 834 */ 835 if (m_room == 0) { 836 size_t size = mp - mem; 837 838 if ((mem = realloc(mem, size + SFRAG)) == NULL) 839 return (-2); 840 m_room = SFRAG; 841 mp = mem + size; 842 } 843 } 844 *mp++ = '\0'; /* loop invariant let's us do this */ 845 m_room--; 846 len = mp - mem - 1; 847 848 /* 849 * Give back any extra memory and return value and success. 850 */ 851 if (m_room != 0) 852 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 853 return (-2); 854 *str = mem; 855 return (len); 856 } 857 858 /* 859 * Cgetustr retrieves the value of the string capability cap from the 860 * capability record pointed to by buf. The difference between cgetustr() 861 * and cgetstr() is that cgetustr does not decode escapes but rather treats 862 * all characters literally. A pointer to a NUL terminated malloc'd 863 * copy of the string is returned in the char pointed to by str. The 864 * length of the string not including the trailing NUL is returned on success, 865 * -1 if the requested string capability couldn't be found, -2 if a system 866 * error was encountered (storage allocation failure). 867 */ 868 int 869 cgetustr(buf, cap, str) 870 char *buf, *cap, **str; 871 { 872 register u_int m_room; 873 register char *bp, *mp; 874 int len; 875 char *mem; 876 877 /* 878 * Find string capability cap 879 */ 880 if ((bp = cgetcap(buf, cap, '=')) == NULL) 881 return (-1); 882 883 /* 884 * Conversion / storage allocation loop ... Allocate memory in 885 * chunks SFRAG in size. 886 */ 887 if ((mem = malloc(SFRAG)) == NULL) { 888 errno = ENOMEM; 889 return (-2); /* couldn't even allocate the first fragment */ 890 } 891 m_room = SFRAG; 892 mp = mem; 893 894 while (*bp != ':' && *bp != '\0') { 895 /* 896 * Loop invariants: 897 * There is always room for one more character in mem. 898 * Mp always points just past last character in mem. 899 * Bp always points at next character in buf. 900 */ 901 *mp++ = *bp++; 902 m_room--; 903 904 /* 905 * Enforce loop invariant: if no room left in current 906 * buffer, try to get some more. 907 */ 908 if (m_room == 0) { 909 size_t size = mp - mem; 910 911 if ((mem = realloc(mem, size + SFRAG)) == NULL) 912 return (-2); 913 m_room = SFRAG; 914 mp = mem + size; 915 } 916 } 917 *mp++ = '\0'; /* loop invariant let's us do this */ 918 m_room--; 919 len = mp - mem - 1; 920 921 /* 922 * Give back any extra memory and return value and success. 923 */ 924 if (m_room != 0) 925 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 926 return (-2); 927 *str = mem; 928 return (len); 929 } 930 931 /* 932 * Cgetnum retrieves the value of the numeric capability cap from the 933 * capability record pointed to by buf. The numeric value is returned in 934 * the long pointed to by num. 0 is returned on success, -1 if the requested 935 * numeric capability couldn't be found. 936 */ 937 int 938 cgetnum(buf, cap, num) 939 char *buf, *cap; 940 long *num; 941 { 942 register long n; 943 register int base, digit; 944 register char *bp; 945 946 /* 947 * Find numeric capability cap 948 */ 949 bp = cgetcap(buf, cap, '#'); 950 if (bp == NULL) 951 return (-1); 952 953 /* 954 * Look at value and determine numeric base: 955 * 0x... or 0X... hexadecimal, 956 * else 0... octal, 957 * else decimal. 958 */ 959 if (*bp == '0') { 960 bp++; 961 if (*bp == 'x' || *bp == 'X') { 962 bp++; 963 base = 16; 964 } else 965 base = 8; 966 } else 967 base = 10; 968 969 /* 970 * Conversion loop ... 971 */ 972 n = 0; 973 for (;;) { 974 if ('0' <= *bp && *bp <= '9') 975 digit = *bp - '0'; 976 else if ('a' <= *bp && *bp <= 'f') 977 digit = 10 + *bp - 'a'; 978 else if ('A' <= *bp && *bp <= 'F') 979 digit = 10 + *bp - 'A'; 980 else 981 break; 982 983 if (digit >= base) 984 break; 985 986 n = n * base + digit; 987 bp++; 988 } 989 990 /* 991 * Return value and success. 992 */ 993 *num = n; 994 return (0); 995 } 996 997 998 /* 999 * Compare name field of record. 1000 */ 1001 static int 1002 nfcmp(nf, rec) 1003 char *nf, *rec; 1004 { 1005 char *cp, tmp; 1006 int ret; 1007 1008 for (cp = rec; *cp != ':'; cp++) 1009 ; 1010 1011 tmp = *(cp + 1); 1012 *(cp + 1) = '\0'; 1013 ret = strcmp(nf, rec); 1014 *(cp + 1) = tmp; 1015 1016 return (ret); 1017 } 1018