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 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * $FreeBSD: src/lib/libc/gen/getcap.c,v 1.11.2.2 2001/01/15 06:48:09 gad Exp $ 37 * $DragonFly: src/lib/libc/gen/getcap.c,v 1.7 2005/11/19 22:32:53 swildner Exp $ 38 * 39 * @(#)getcap.c 8.3 (Berkeley) 3/25/94 40 */ 41 42 #include "namespace.h" 43 #include <sys/types.h> 44 45 #include <ctype.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <limits.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include "un-namespace.h" 54 55 #include <db.h> 56 57 #define BFRAG 1024 58 #define BSIZE 1024 59 #define ESC ('[' & 037) /* ASCII ESC */ 60 #define MAX_RECURSION 32 /* maximum getent recursion */ 61 #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 62 63 #define RECOK (char)0 64 #define TCERR (char)1 65 #define SHADOW (char)2 66 67 static size_t topreclen; /* toprec length */ 68 static char *toprec; /* Additional record specified by cgetset() */ 69 static int gottoprec; /* Flag indicating retrieval of toprecord */ 70 71 static int cdbget (DB *, char **, char *); 72 static int getent (char **, u_int *, char **, int, char *, int, char *); 73 static int nfcmp (char *, char *); 74 75 /* 76 * Cgetset() allows the addition of a user specified buffer to be added 77 * to the database array, in effect "pushing" the buffer on top of the 78 * virtual database. 0 is returned on success, -1 on failure. 79 */ 80 int 81 cgetset(char *ent) 82 { 83 if (ent == NULL) { 84 if (toprec) 85 free(toprec); 86 toprec = NULL; 87 topreclen = 0; 88 return (0); 89 } 90 topreclen = strlen(ent); 91 if ((toprec = malloc (topreclen + 1)) == NULL) { 92 errno = ENOMEM; 93 return (-1); 94 } 95 gottoprec = 0; 96 strcpy(toprec, ent); 97 return (0); 98 } 99 100 /* 101 * Cgetcap searches the capability record buf for the capability cap with 102 * type `type'. A pointer to the value of cap is returned on success, NULL 103 * if the requested capability couldn't be found. 104 * 105 * Specifying a type of ':' means that nothing should follow cap (:cap:). 106 * In this case a pointer to the terminating ':' or NUL will be returned if 107 * cap is found. 108 * 109 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 110 * return NULL. 111 */ 112 char * 113 cgetcap(char *buf, char *cap, int type) 114 { 115 char *bp, *cp; 116 117 bp = buf; 118 for (;;) { 119 /* 120 * Skip past the current capability field - it's either the 121 * name field if this is the first time through the loop, or 122 * the remainder of a field whose name failed to match cap. 123 */ 124 for (;;) 125 if (*bp == '\0') 126 return (NULL); 127 else 128 if (*bp++ == ':') 129 break; 130 131 /* 132 * Try to match (cap, type) in buf. 133 */ 134 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 135 continue; 136 if (*cp != '\0') 137 continue; 138 if (*bp == '@') 139 return (NULL); 140 if (type == ':') { 141 if (*bp != '\0' && *bp != ':') 142 continue; 143 return(bp); 144 } 145 if (*bp != type) 146 continue; 147 bp++; 148 return (*bp == '@' ? NULL : bp); 149 } 150 /* NOTREACHED */ 151 } 152 153 /* 154 * Cgetent extracts the capability record name from the NULL terminated file 155 * array db_array and returns a pointer to a malloc'd copy of it in buf. 156 * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 157 * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 158 * -1 if the requested record couldn't be found, -2 if a system error was 159 * encountered (couldn't open/read a file, etc.), and -3 if a potential 160 * reference loop is detected. 161 */ 162 int 163 cgetent(char **buf, char **db_array, char *name) 164 { 165 u_int dummy; 166 167 return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 168 } 169 170 /* 171 * Getent implements the functions of cgetent. If fd is non-negative, 172 * *db_array has already been opened and fd is the open file descriptor. We 173 * do this to save time and avoid using up file descriptors for tc= 174 * recursions. 175 * 176 * Getent returns the same success/failure codes as cgetent. On success, a 177 * pointer to a malloc'ed capability record with all tc= capabilities fully 178 * expanded and its length (not including trailing ASCII NUL) are left in 179 * *cap and *len. 180 * 181 * Basic algorithm: 182 * + Allocate memory incrementally as needed in chunks of size BFRAG 183 * for capability buffer. 184 * + Recurse for each tc=name and interpolate result. Stop when all 185 * names interpolated, a name can't be found, or depth exceeds 186 * MAX_RECURSION. 187 */ 188 static int 189 getent(char **cap, u_int *len, char **db_array, int fd, char *name, int depth, 190 char *nfield) 191 { 192 DB *capdbp; 193 char *r_end, *rp, **db_p; 194 int myfd, eof, foundit, retval, clen; 195 char *record, *cbuf; 196 int tc_not_resolved; 197 char pbuf[_POSIX_PATH_MAX]; 198 199 rp = NULL; 200 myfd = 0; 201 202 /* 203 * Return with ``loop detected'' error if we've recursed more than 204 * MAX_RECURSION times. 205 */ 206 if (depth > MAX_RECURSION) 207 return (-3); 208 209 /* 210 * Check if we have a top record from cgetset(). 211 */ 212 if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 213 if ((record = malloc (topreclen + BFRAG)) == NULL) { 214 errno = ENOMEM; 215 return (-2); 216 } 217 strcpy(record, toprec); 218 myfd = 0; 219 db_p = db_array; 220 rp = record + topreclen + 1; 221 r_end = rp + BFRAG; 222 goto tc_exp; 223 } 224 /* 225 * Allocate first chunk of memory. 226 */ 227 if ((record = malloc(BFRAG)) == NULL) { 228 errno = ENOMEM; 229 return (-2); 230 } 231 r_end = record + BFRAG; 232 foundit = 0; 233 /* 234 * Loop through database array until finding the record. 235 */ 236 237 for (db_p = db_array; *db_p != NULL; db_p++) { 238 eof = 0; 239 240 /* 241 * Open database if not already open. 242 */ 243 244 if (fd >= 0) { 245 lseek(fd, (off_t)0, SEEK_SET); 246 myfd = 0; 247 } else { 248 snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 249 if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 250 != NULL) { 251 free(record); 252 retval = cdbget(capdbp, &record, name); 253 if (retval < 0) { 254 /* no record available */ 255 capdbp->close(capdbp); 256 return (retval); 257 } 258 /* save the data; close frees it */ 259 clen = strlen(record); 260 cbuf = malloc(clen + 1); 261 memcpy(cbuf, record, clen + 1); 262 if (capdbp->close(capdbp) < 0) { 263 free(cbuf); 264 return (-2); 265 } 266 *len = clen; 267 *cap = cbuf; 268 return (retval); 269 } else { 270 fd = _open(*db_p, O_RDONLY, 0); 271 if (fd < 0) 272 continue; 273 myfd = 1; 274 } 275 } 276 /* 277 * Find the requested capability record ... 278 */ 279 { 280 char buf[BUFSIZ]; 281 char *b_end, *bp; 282 int c; 283 284 /* 285 * Loop invariants: 286 * There is always room for one more character in record. 287 * R_end always points just past end of record. 288 * Rp always points just past last character in record. 289 * B_end always points just past last character in buf. 290 * Bp always points at next character in buf. 291 */ 292 b_end = buf; 293 bp = buf; 294 for (;;) { 295 296 /* 297 * Read in a line implementing (\, newline) 298 * line continuation. 299 */ 300 rp = record; 301 for (;;) { 302 if (bp >= b_end) { 303 int n; 304 305 n = _read(fd, buf, sizeof(buf)); 306 if (n <= 0) { 307 if (myfd) 308 _close(fd); 309 if (n < 0) { 310 free(record); 311 return (-2); 312 } else { 313 fd = -1; 314 eof = 1; 315 break; 316 } 317 } 318 b_end = buf+n; 319 bp = buf; 320 } 321 322 c = *bp++; 323 if (c == '\n') { 324 if (rp > record && *(rp-1) == '\\') { 325 rp--; 326 continue; 327 } else 328 break; 329 } 330 *rp++ = c; 331 332 /* 333 * Enforce loop invariant: if no room 334 * left in record buffer, try to get 335 * some more. 336 */ 337 if (rp >= r_end) { 338 u_int pos; 339 size_t newsize; 340 341 pos = rp - record; 342 newsize = r_end - record + BFRAG; 343 record = reallocf(record, newsize); 344 if (record == NULL) { 345 errno = ENOMEM; 346 if (myfd) 347 _close(fd); 348 return (-2); 349 } 350 r_end = record + newsize; 351 rp = record + pos; 352 } 353 } 354 /* loop invariant let's us do this */ 355 *rp++ = '\0'; 356 357 /* 358 * If encountered eof check next file. 359 */ 360 if (eof) 361 break; 362 363 /* 364 * Toss blank lines and comments. 365 */ 366 if (*record == '\0' || *record == '#') 367 continue; 368 369 /* 370 * See if this is the record we want ... 371 */ 372 if (cgetmatch(record, name) == 0) { 373 if (nfield == NULL || !nfcmp(nfield, record)) { 374 foundit = 1; 375 break; /* found it! */ 376 } 377 } 378 } 379 } 380 if (foundit) 381 break; 382 } 383 384 if (!foundit) { 385 free(record); 386 return (-1); 387 } 388 389 /* 390 * Got the capability record, but now we have to expand all tc=name 391 * references in it ... 392 */ 393 tc_exp: { 394 char *newicap, *s; 395 int newilen; 396 u_int ilen; 397 int diff, iret, tclen; 398 char *icap, *scan, *tc, *tcstart, *tcend; 399 400 /* 401 * Loop invariants: 402 * There is room for one more character in record. 403 * R_end points just past end of record. 404 * Rp points just past last character in record. 405 * Scan points at remainder of record that needs to be 406 * scanned for tc=name constructs. 407 */ 408 scan = record; 409 tc_not_resolved = 0; 410 for (;;) { 411 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 412 break; 413 414 /* 415 * Find end of tc=name and stomp on the trailing `:' 416 * (if present) so we can use it to call ourselves. 417 */ 418 s = tc; 419 for (;;) 420 if (*s == '\0') 421 break; 422 else 423 if (*s++ == ':') { 424 *(s - 1) = '\0'; 425 break; 426 } 427 tcstart = tc - 3; 428 tclen = s - tcstart; 429 tcend = s; 430 431 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 432 NULL); 433 newicap = icap; /* Put into a register. */ 434 newilen = ilen; 435 if (iret != 0) { 436 /* an error */ 437 if (iret < -1) { 438 if (myfd) 439 _close(fd); 440 free(record); 441 return (iret); 442 } 443 if (iret == 1) 444 tc_not_resolved = 1; 445 /* couldn't resolve tc */ 446 if (iret == -1) { 447 *(s - 1) = ':'; 448 scan = s - 1; 449 tc_not_resolved = 1; 450 continue; 451 452 } 453 } 454 /* not interested in name field of tc'ed record */ 455 s = newicap; 456 for (;;) 457 if (*s == '\0') 458 break; 459 else 460 if (*s++ == ':') 461 break; 462 newilen -= s - newicap; 463 newicap = s; 464 465 /* make sure interpolated record is `:'-terminated */ 466 s += newilen; 467 if (*(s-1) != ':') { 468 *s = ':'; /* overwrite NUL with : */ 469 newilen++; 470 } 471 472 /* 473 * Make sure there's enough room to insert the 474 * new record. 475 */ 476 diff = newilen - tclen; 477 if (diff >= r_end - rp) { 478 u_int pos, tcpos, tcposend; 479 size_t newsize; 480 481 pos = rp - record; 482 newsize = r_end - record + diff + BFRAG; 483 tcpos = tcstart - record; 484 tcposend = tcend - record; 485 record = reallocf(record, newsize); 486 if (record == NULL) { 487 errno = ENOMEM; 488 if (myfd) 489 _close(fd); 490 free(icap); 491 return (-2); 492 } 493 r_end = record + newsize; 494 rp = record + pos; 495 tcstart = record + tcpos; 496 tcend = record + tcposend; 497 } 498 499 /* 500 * Insert tc'ed record into our record. 501 */ 502 s = tcstart + newilen; 503 bcopy(tcend, s, rp - tcend); 504 bcopy(newicap, tcstart, newilen); 505 rp += diff; 506 free(icap); 507 508 /* 509 * Start scan on `:' so next cgetcap works properly 510 * (cgetcap always skips first field). 511 */ 512 scan = s-1; 513 } 514 515 } 516 /* 517 * Close file (if we opened it), give back any extra memory, and 518 * return capability, length and success. 519 */ 520 if (myfd) 521 _close(fd); 522 *len = rp - record - 1; /* don't count NUL */ 523 if (r_end > rp) 524 if ((record = 525 reallocf(record, (size_t)(rp - record))) == NULL) { 526 errno = ENOMEM; 527 return (-2); 528 } 529 530 *cap = record; 531 if (tc_not_resolved) 532 return (1); 533 return (0); 534 } 535 536 static int 537 cdbget(DB *capdbp, char **bp, char *name) 538 { 539 DBT key, data; 540 541 key.data = name; 542 key.size = strlen(name); 543 544 for (;;) { 545 /* Get the reference. */ 546 switch(capdbp->get(capdbp, &key, &data, 0)) { 547 case -1: 548 return (-2); 549 case 1: 550 return (-1); 551 } 552 553 /* If not an index to another record, leave. */ 554 if (((char *)data.data)[0] != SHADOW) 555 break; 556 557 key.data = (char *)data.data + 1; 558 key.size = data.size - 1; 559 } 560 561 *bp = (char *)data.data + 1; 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(char *buf, char *name) 571 { 572 char *np, *bp; 573 574 /* 575 * Start search at beginning of record. 576 */ 577 bp = buf; 578 for (;;) { 579 /* 580 * Try to match a record name. 581 */ 582 np = name; 583 for (;;) 584 if (*np == '\0') 585 if (*bp == '|' || *bp == ':' || *bp == '\0') 586 return (0); 587 else 588 break; 589 else 590 if (*bp++ != *np++) 591 break; 592 593 /* 594 * Match failed, skip to next name in record. 595 */ 596 bp--; /* a '|' or ':' may have stopped the match */ 597 for (;;) 598 if (*bp == '\0' || *bp == ':') 599 return (-1); /* match failed totally */ 600 else 601 if (*bp++ == '|') 602 break; /* found next name */ 603 } 604 } 605 606 607 608 609 610 int 611 cgetfirst(char **buf, char **db_array) 612 { 613 cgetclose(); 614 return (cgetnext(buf, db_array)); 615 } 616 617 static FILE *pfp; 618 static int slash; 619 static char **dbp; 620 621 int 622 cgetclose(void) 623 { 624 if (pfp != NULL) { 625 fclose(pfp); 626 pfp = NULL; 627 } 628 dbp = NULL; 629 gottoprec = 0; 630 slash = 0; 631 return(0); 632 } 633 634 /* 635 * Cgetnext() gets either the first or next entry in the logical database 636 * specified by db_array. It returns 0 upon completion of the database, 1 637 * upon returning an entry with more remaining, and -1 if an error occurs. 638 */ 639 int 640 cgetnext(char **bp, char **db_array) 641 { 642 size_t len; 643 int done, hadreaderr, i, savederrno, status; 644 char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 645 u_int dummy; 646 647 savederrno = 0; 648 649 if (dbp == NULL) 650 dbp = db_array; 651 652 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 653 cgetclose(); 654 return (-1); 655 } 656 for(;;) { 657 if (toprec && !gottoprec) { 658 gottoprec = 1; 659 line = toprec; 660 } else { 661 line = fgetln(pfp, &len); 662 if (line == NULL && pfp) { 663 hadreaderr = ferror(pfp); 664 if (hadreaderr) 665 savederrno = errno; 666 fclose(pfp); 667 pfp = NULL; 668 if (hadreaderr) { 669 cgetclose(); 670 errno = savederrno; 671 return (-1); 672 } else { 673 if (*++dbp == NULL) { 674 cgetclose(); 675 return (0); 676 } else if ((pfp = 677 fopen(*dbp, "r")) == NULL) { 678 cgetclose(); 679 return (-1); 680 } else 681 continue; 682 } 683 } else 684 line[len - 1] = '\0'; 685 if (len == 1) { 686 slash = 0; 687 continue; 688 } 689 if (isspace((unsigned char)*line) || 690 *line == ':' || *line == '#' || slash) { 691 if (line[len - 2] == '\\') 692 slash = 1; 693 else 694 slash = 0; 695 continue; 696 } 697 if (line[len - 2] == '\\') 698 slash = 1; 699 else 700 slash = 0; 701 } 702 703 704 /* 705 * Line points to a name line. 706 */ 707 i = 0; 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 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, 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, 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, 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