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