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