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