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