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