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