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