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