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