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