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