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