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