1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 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. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 __SCCSID("@(#)getcap.c 8.3 (Berkeley) 3/25/94"); 37 #include "namespace.h" 38 #include <sys/types.h> 39 40 #include <ctype.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <limits.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 #include "un-namespace.h" 49 50 #include <db.h> 51 52 #define BFRAG 1024 53 #define BSIZE 1024 54 #define ESC ('[' & 037) /* ASCII ESC */ 55 #define MAX_RECURSION 32 /* maximum getent recursion */ 56 #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 57 58 #define RECOK (char)0 59 #define TCERR (char)1 60 #define SHADOW (char)2 61 62 static size_t topreclen; /* toprec length */ 63 static char *toprec; /* Additional record specified by cgetset() */ 64 static int gottoprec; /* Flag indicating retrieval of toprecord */ 65 66 static int cdbget(DB *, char **, const char *); 67 static int getent(char **, u_int *, char **, int, const char *, int, char *); 68 static int nfcmp(char *, char *); 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(const char *ent) 77 { 78 if (ent == NULL) { 79 if (toprec) 80 free(toprec); 81 toprec = NULL; 82 topreclen = 0; 83 return (0); 84 } 85 topreclen = strlen(ent); 86 if ((toprec = malloc (topreclen + 1)) == NULL) { 87 errno = ENOMEM; 88 return (-1); 89 } 90 gottoprec = 0; 91 (void)strcpy(toprec, ent); 92 return (0); 93 } 94 95 /* 96 * Cgetcap searches the capability record buf for the capability cap with 97 * type `type'. A pointer to the value of cap is returned on success, NULL 98 * if the requested capability couldn't be found. 99 * 100 * Specifying a type of ':' means that nothing should follow cap (:cap:). 101 * In this case a pointer to the terminating ':' or NUL will be returned if 102 * cap is found. 103 * 104 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 105 * return NULL. 106 */ 107 char * 108 cgetcap(char *buf, const char *cap, int type) 109 { 110 char *bp; 111 const char *cp; 112 113 bp = buf; 114 for (;;) { 115 /* 116 * Skip past the current capability field - it's either the 117 * name field if this is the first time through the loop, or 118 * the remainder of a field whose name failed to match cap. 119 */ 120 for (;;) 121 if (*bp == '\0') 122 return (NULL); 123 else 124 if (*bp++ == ':') 125 break; 126 127 /* 128 * Try to match (cap, type) in buf. 129 */ 130 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 131 continue; 132 if (*cp != '\0') 133 continue; 134 if (*bp == '@') 135 return (NULL); 136 if (type == ':') { 137 if (*bp != '\0' && *bp != ':') 138 continue; 139 return(bp); 140 } 141 if (*bp != type) 142 continue; 143 bp++; 144 return (*bp == '@' ? NULL : bp); 145 } 146 /* NOTREACHED */ 147 } 148 149 /* 150 * Cgetent extracts the capability record name from the NULL terminated file 151 * array db_array and returns a pointer to a malloc'd copy of it in buf. 152 * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 153 * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 154 * -1 if the requested record couldn't be found, -2 if a system error was 155 * encountered (couldn't open/read a file, etc.), and -3 if a potential 156 * reference loop is detected. 157 */ 158 int 159 cgetent(char **buf, char **db_array, const char *name) 160 { 161 u_int dummy; 162 163 return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 164 } 165 166 /* 167 * Getent implements the functions of cgetent. If fd is non-negative, 168 * *db_array has already been opened and fd is the open file descriptor. We 169 * do this to save time and avoid using up file descriptors for tc= 170 * recursions. 171 * 172 * Getent returns the same success/failure codes as cgetent. On success, a 173 * pointer to a malloc'ed capability record with all tc= capabilities fully 174 * expanded and its length (not including trailing ASCII NUL) are left in 175 * *cap and *len. 176 * 177 * Basic algorithm: 178 * + Allocate memory incrementally as needed in chunks of size BFRAG 179 * for capability buffer. 180 * + Recurse for each tc=name and interpolate result. Stop when all 181 * names interpolated, a name can't be found, or depth exceeds 182 * MAX_RECURSION. 183 */ 184 static int 185 getent(char **cap, u_int *len, char **db_array, int fd, const char *name, 186 int depth, char *nfield) 187 { 188 DB *capdbp; 189 char *r_end, *rp, **db_p; 190 int myfd, eof, foundit, retval; 191 char *record, *cbuf; 192 int tc_not_resolved; 193 char pbuf[_POSIX_PATH_MAX]; 194 195 /* 196 * Return with ``loop detected'' error if we've recursed more than 197 * MAX_RECURSION times. 198 */ 199 if (depth > MAX_RECURSION) 200 return (-3); 201 202 /* 203 * Check if we have a top record from cgetset(). 204 */ 205 if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 206 if ((record = malloc (topreclen + BFRAG)) == NULL) { 207 errno = ENOMEM; 208 return (-2); 209 } 210 (void)strcpy(record, toprec); 211 myfd = 0; 212 db_p = db_array; 213 rp = record + topreclen + 1; 214 r_end = rp + BFRAG; 215 goto tc_exp; 216 } 217 /* 218 * Allocate first chunk of memory. 219 */ 220 if ((record = malloc(BFRAG)) == NULL) { 221 errno = ENOMEM; 222 return (-2); 223 } 224 r_end = record + BFRAG; 225 foundit = 0; 226 /* 227 * Loop through database array until finding the record. 228 */ 229 230 for (db_p = db_array; *db_p != NULL; db_p++) { 231 eof = 0; 232 233 /* 234 * Open database if not already open. 235 */ 236 237 if (fd >= 0) { 238 (void)lseek(fd, (off_t)0, SEEK_SET); 239 myfd = 0; 240 } else { 241 (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 242 if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 243 != NULL) { 244 free(record); 245 retval = cdbget(capdbp, &record, name); 246 if (retval < 0) { 247 /* no record available */ 248 (void)capdbp->close(capdbp); 249 return (retval); 250 } 251 /* save the data; close frees it */ 252 cbuf = strdup(record); 253 if (capdbp->close(capdbp) < 0) { 254 free(cbuf); 255 return (-2); 256 } 257 if (cbuf == NULL) { 258 errno = ENOMEM; 259 return (-2); 260 } 261 *len = strlen(cbuf); 262 *cap = cbuf; 263 return (retval); 264 } else { 265 fd = _open(*db_p, O_RDONLY | O_CLOEXEC, 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, 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, "re")) == 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, "re")) == 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 done = 0; 711 np = nbuf; 712 for (;;) { 713 for (cp = line; *cp != '\0'; cp++) { 714 if (*cp == ':') { 715 *np++ = ':'; 716 done = 1; 717 break; 718 } 719 if (*cp == '\\') 720 break; 721 *np++ = *cp; 722 } 723 if (done) { 724 *np = '\0'; 725 break; 726 } else { /* name field extends beyond the line */ 727 line = fgetln(pfp, &len); 728 if (line == NULL && pfp) { 729 /* Name extends beyond the EOF! */ 730 hadreaderr = ferror(pfp); 731 if (hadreaderr) 732 savederrno = errno; 733 fclose(pfp); 734 pfp = NULL; 735 if (hadreaderr) { 736 cgetclose(); 737 errno = savederrno; 738 return (-1); 739 } else { 740 cgetclose(); 741 return (-1); 742 } 743 } else 744 line[len - 1] = '\0'; 745 } 746 } 747 rp = buf; 748 for(cp = nbuf; *cp != '\0'; cp++) 749 if (*cp == '|' || *cp == ':') 750 break; 751 else 752 *rp++ = *cp; 753 754 *rp = '\0'; 755 /* 756 * XXX 757 * Last argument of getent here should be nbuf if we want true 758 * sequential access in the case of duplicates. 759 * With NULL, getent will return the first entry found 760 * rather than the duplicate entry record. This is a 761 * matter of semantics that should be resolved. 762 */ 763 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 764 if (status == -2 || status == -3) 765 (void)cgetclose(); 766 767 return (status + 1); 768 } 769 /* NOTREACHED */ 770 } 771 772 /* 773 * Cgetstr retrieves the value of the string capability cap from the 774 * capability record pointed to by buf. A pointer to a decoded, NUL 775 * terminated, malloc'd copy of the string is returned in the char * 776 * pointed to by str. The length of the string not including the trailing 777 * NUL is returned on success, -1 if the requested string capability 778 * couldn't be found, -2 if a system error was encountered (storage 779 * allocation failure). 780 */ 781 int 782 cgetstr(char *buf, const char *cap, char **str) 783 { 784 u_int m_room; 785 char *bp, *mp; 786 int len; 787 char *mem; 788 789 /* 790 * Find string capability cap 791 */ 792 bp = cgetcap(buf, cap, '='); 793 if (bp == NULL) 794 return (-1); 795 796 /* 797 * Conversion / storage allocation loop ... Allocate memory in 798 * chunks SFRAG in size. 799 */ 800 if ((mem = malloc(SFRAG)) == NULL) { 801 errno = ENOMEM; 802 return (-2); /* couldn't even allocate the first fragment */ 803 } 804 m_room = SFRAG; 805 mp = mem; 806 807 while (*bp != ':' && *bp != '\0') { 808 /* 809 * Loop invariants: 810 * There is always room for one more character in mem. 811 * Mp always points just past last character in mem. 812 * Bp always points at next character in buf. 813 */ 814 if (*bp == '^') { 815 bp++; 816 if (*bp == ':' || *bp == '\0') 817 break; /* drop unfinished escape */ 818 if (*bp == '?') { 819 *mp++ = '\177'; 820 bp++; 821 } else 822 *mp++ = *bp++ & 037; 823 } else if (*bp == '\\') { 824 bp++; 825 if (*bp == ':' || *bp == '\0') 826 break; /* drop unfinished escape */ 827 if ('0' <= *bp && *bp <= '7') { 828 int n, i; 829 830 n = 0; 831 i = 3; /* maximum of three octal digits */ 832 do { 833 n = n * 8 + (*bp++ - '0'); 834 } while (--i && '0' <= *bp && *bp <= '7'); 835 *mp++ = n; 836 } 837 else switch (*bp++) { 838 case 'b': case 'B': 839 *mp++ = '\b'; 840 break; 841 case 't': case 'T': 842 *mp++ = '\t'; 843 break; 844 case 'n': case 'N': 845 *mp++ = '\n'; 846 break; 847 case 'f': case 'F': 848 *mp++ = '\f'; 849 break; 850 case 'r': case 'R': 851 *mp++ = '\r'; 852 break; 853 case 'e': case 'E': 854 *mp++ = ESC; 855 break; 856 case 'c': case 'C': 857 *mp++ = ':'; 858 break; 859 default: 860 /* 861 * Catches '\', '^', and 862 * everything else. 863 */ 864 *mp++ = *(bp-1); 865 break; 866 } 867 } else 868 *mp++ = *bp++; 869 m_room--; 870 871 /* 872 * Enforce loop invariant: if no room left in current 873 * buffer, try to get some more. 874 */ 875 if (m_room == 0) { 876 size_t size = mp - mem; 877 878 if ((mem = reallocf(mem, size + SFRAG)) == NULL) 879 return (-2); 880 m_room = SFRAG; 881 mp = mem + size; 882 } 883 } 884 *mp++ = '\0'; /* loop invariant let's us do this */ 885 m_room--; 886 len = mp - mem - 1; 887 888 /* 889 * Give back any extra memory and return value and success. 890 */ 891 if (m_room != 0) 892 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL) 893 return (-2); 894 *str = mem; 895 return (len); 896 } 897 898 /* 899 * Cgetustr retrieves the value of the string capability cap from the 900 * capability record pointed to by buf. The difference between cgetustr() 901 * and cgetstr() is that cgetustr does not decode escapes but rather treats 902 * all characters literally. A pointer to a NUL terminated malloc'd 903 * copy of the string is returned in the char pointed to by str. The 904 * length of the string not including the trailing NUL is returned on success, 905 * -1 if the requested string capability couldn't be found, -2 if a system 906 * error was encountered (storage allocation failure). 907 */ 908 int 909 cgetustr(char *buf, const char *cap, char **str) 910 { 911 u_int m_room; 912 char *bp, *mp; 913 int len; 914 char *mem; 915 916 /* 917 * Find string capability cap 918 */ 919 if ((bp = cgetcap(buf, cap, '=')) == NULL) 920 return (-1); 921 922 /* 923 * Conversion / storage allocation loop ... Allocate memory in 924 * chunks SFRAG in size. 925 */ 926 if ((mem = malloc(SFRAG)) == NULL) { 927 errno = ENOMEM; 928 return (-2); /* couldn't even allocate the first fragment */ 929 } 930 m_room = SFRAG; 931 mp = mem; 932 933 while (*bp != ':' && *bp != '\0') { 934 /* 935 * Loop invariants: 936 * There is always room for one more character in mem. 937 * Mp always points just past last character in mem. 938 * Bp always points at next character in buf. 939 */ 940 *mp++ = *bp++; 941 m_room--; 942 943 /* 944 * Enforce loop invariant: if no room left in current 945 * buffer, try to get some more. 946 */ 947 if (m_room == 0) { 948 size_t size = mp - mem; 949 950 if ((mem = reallocf(mem, size + SFRAG)) == NULL) 951 return (-2); 952 m_room = SFRAG; 953 mp = mem + size; 954 } 955 } 956 *mp++ = '\0'; /* loop invariant let's us do this */ 957 m_room--; 958 len = mp - mem - 1; 959 960 /* 961 * Give back any extra memory and return value and success. 962 */ 963 if (m_room != 0) 964 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL) 965 return (-2); 966 *str = mem; 967 return (len); 968 } 969 970 /* 971 * Cgetnum retrieves the value of the numeric capability cap from the 972 * capability record pointed to by buf. The numeric value is returned in 973 * the long pointed to by num. 0 is returned on success, -1 if the requested 974 * numeric capability couldn't be found. 975 */ 976 int 977 cgetnum(char *buf, const char *cap, long *num) 978 { 979 long n; 980 int base, digit; 981 char *bp; 982 983 /* 984 * Find numeric capability cap 985 */ 986 bp = cgetcap(buf, cap, '#'); 987 if (bp == NULL) 988 return (-1); 989 990 /* 991 * Look at value and determine numeric base: 992 * 0x... or 0X... hexadecimal, 993 * else 0... octal, 994 * else decimal. 995 */ 996 if (*bp == '0') { 997 bp++; 998 if (*bp == 'x' || *bp == 'X') { 999 bp++; 1000 base = 16; 1001 } else 1002 base = 8; 1003 } else 1004 base = 10; 1005 1006 /* 1007 * Conversion loop ... 1008 */ 1009 n = 0; 1010 for (;;) { 1011 if ('0' <= *bp && *bp <= '9') 1012 digit = *bp - '0'; 1013 else if ('a' <= *bp && *bp <= 'f') 1014 digit = 10 + *bp - 'a'; 1015 else if ('A' <= *bp && *bp <= 'F') 1016 digit = 10 + *bp - 'A'; 1017 else 1018 break; 1019 1020 if (digit >= base) 1021 break; 1022 1023 n = n * base + digit; 1024 bp++; 1025 } 1026 1027 /* 1028 * Return value and success. 1029 */ 1030 *num = n; 1031 return (0); 1032 } 1033 1034 1035 /* 1036 * Compare name field of record. 1037 */ 1038 static int 1039 nfcmp(char *nf, char *rec) 1040 { 1041 char *cp, tmp; 1042 int ret; 1043 1044 for (cp = rec; *cp != ':'; cp++) 1045 ; 1046 1047 tmp = *(cp + 1); 1048 *(cp + 1) = '\0'; 1049 ret = strcmp(nf, rec); 1050 *(cp + 1) = tmp; 1051 1052 return (ret); 1053 } 1054