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