1 /* $NetBSD: getcap.c,v 1.15 1997/08/25 19:31:45 kleink 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.15 1997/08/25 19:31:45 kleink 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 **, u_int *, 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 register 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 u_int 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 u_int *len; 212 int fd, depth; 213 { 214 DB *capdbp; 215 register char *r_end, *rp = NULL, **db_p; /* pacify gcc */ 216 int myfd = 0, eof, foundit, retval, clen; 217 char *record, *cbuf; 218 int tc_not_resolved; 219 char pbuf[_POSIX_PATH_MAX]; 220 221 /* 222 * Return with ``loop detected'' error if we've recursed more than 223 * MAX_RECURSION times. 224 */ 225 if (depth > MAX_RECURSION) 226 return (-3); 227 228 /* 229 * Check if we have a top record from cgetset(). 230 */ 231 if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 232 if ((record = malloc (topreclen + BFRAG)) == NULL) { 233 errno = ENOMEM; 234 return (-2); 235 } 236 (void)strcpy(record, toprec); /* XXX: strcpy is safe */ 237 db_p = db_array; 238 rp = record + topreclen + 1; 239 r_end = rp + BFRAG; 240 goto tc_exp; 241 } 242 /* 243 * Allocate first chunk of memory. 244 */ 245 if ((record = malloc(BFRAG)) == NULL) { 246 errno = ENOMEM; 247 return (-2); 248 } 249 r_end = record + BFRAG; 250 foundit = 0; 251 /* 252 * Loop through database array until finding the record. 253 */ 254 255 for (db_p = db_array; *db_p != NULL; db_p++) { 256 eof = 0; 257 258 /* 259 * Open database if not already open. 260 */ 261 262 if (fd >= 0) { 263 (void)lseek(fd, (off_t)0, SEEK_SET); 264 } else { 265 (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 266 if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 267 != NULL) { 268 free(record); 269 retval = cdbget(capdbp, &record, name); 270 if (retval < 0) { 271 /* no record available */ 272 (void)capdbp->close(capdbp); 273 return (retval); 274 } 275 /* save the data; close frees it */ 276 clen = strlen(record); 277 cbuf = malloc(clen + 1); 278 memcpy(cbuf, record, clen + 1); 279 if (capdbp->close(capdbp) < 0) { 280 free(cbuf); 281 return (-2); 282 } 283 *len = clen; 284 *cap = cbuf; 285 return (retval); 286 } else { 287 fd = open(*db_p, O_RDONLY, 0); 288 if (fd < 0) { 289 /* No error on unfound file. */ 290 continue; 291 } 292 myfd = 1; 293 } 294 } 295 /* 296 * Find the requested capability record ... 297 */ 298 { 299 char buf[BUFSIZ]; 300 register char *b_end, *bp; 301 register int c; 302 303 /* 304 * Loop invariants: 305 * There is always room for one more character in record. 306 * R_end always points just past end of record. 307 * Rp always points just past last character in record. 308 * B_end always points just past last character in buf. 309 * Bp always points at next character in buf. 310 */ 311 b_end = buf; 312 bp = buf; 313 for (;;) { 314 315 /* 316 * Read in a line implementing (\, newline) 317 * line continuation. 318 */ 319 rp = record; 320 for (;;) { 321 if (bp >= b_end) { 322 int n; 323 324 n = read(fd, buf, sizeof(buf)); 325 if (n <= 0) { 326 if (myfd) 327 (void)close(fd); 328 if (n < 0) { 329 free(record); 330 return (-2); 331 } else { 332 fd = -1; 333 eof = 1; 334 break; 335 } 336 } 337 b_end = buf+n; 338 bp = buf; 339 } 340 341 c = *bp++; 342 if (c == '\n') { 343 if (rp > record && *(rp-1) == '\\') { 344 rp--; 345 continue; 346 } else 347 break; 348 } 349 *rp++ = c; 350 351 /* 352 * Enforce loop invariant: if no room 353 * left in record buffer, try to get 354 * some more. 355 */ 356 if (rp >= r_end) { 357 u_int pos; 358 size_t newsize; 359 360 pos = rp - record; 361 newsize = r_end - record + BFRAG; 362 record = realloc(record, newsize); 363 if (record == NULL) { 364 errno = ENOMEM; 365 if (myfd) 366 (void)close(fd); 367 return (-2); 368 } 369 r_end = record + newsize; 370 rp = record + pos; 371 } 372 } 373 /* loop invariant let's us do this */ 374 *rp++ = '\0'; 375 376 /* 377 * If encountered eof check next file. 378 */ 379 if (eof) 380 break; 381 382 /* 383 * Toss blank lines and comments. 384 */ 385 if (*record == '\0' || *record == '#') 386 continue; 387 388 /* 389 * See if this is the record we want ... 390 */ 391 if (cgetmatch(record, name) == 0) { 392 if (nfield == NULL || !nfcmp(nfield, record)) { 393 foundit = 1; 394 break; /* found it! */ 395 } 396 } 397 } 398 } 399 if (foundit) 400 break; 401 } 402 403 if (!foundit) 404 return (-1); 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 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, 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 register 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 627 628 629 630 int 631 cgetfirst(buf, db_array) 632 char **buf, **db_array; 633 { 634 (void)cgetclose(); 635 return (cgetnext(buf, db_array)); 636 } 637 638 static FILE *pfp; 639 static int slash; 640 static char **dbp; 641 642 int 643 cgetclose() 644 { 645 if (pfp != NULL) { 646 (void)fclose(pfp); 647 pfp = NULL; 648 } 649 dbp = NULL; 650 gottoprec = 0; 651 slash = 0; 652 return(0); 653 } 654 655 /* 656 * Cgetnext() gets either the first or next entry in the logical database 657 * specified by db_array. It returns 0 upon completion of the database, 1 658 * upon returning an entry with more remaining, and -1 if an error occurs. 659 */ 660 int 661 cgetnext(bp, db_array) 662 register char **bp; 663 char **db_array; 664 { 665 size_t len; 666 int status, i, done; 667 char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 668 u_int dummy; 669 670 if (dbp == NULL) 671 dbp = db_array; 672 673 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 674 (void)cgetclose(); 675 return (-1); 676 } 677 for(;;) { 678 if (toprec && !gottoprec) { 679 gottoprec = 1; 680 line = toprec; 681 } else { 682 line = fgetln(pfp, &len); 683 if (line == NULL && pfp) { 684 (void)fclose(pfp); 685 if (ferror(pfp)) { 686 (void)cgetclose(); 687 return (-1); 688 } else { 689 if (*++dbp == NULL) { 690 (void)cgetclose(); 691 return (0); 692 } else if ((pfp = 693 fopen(*dbp, "r")) == NULL) { 694 (void)cgetclose(); 695 return (-1); 696 } else 697 continue; 698 } 699 } else 700 line[len - 1] = '\0'; 701 if (len == 1) { 702 slash = 0; 703 continue; 704 } 705 if (isspace(*line) || 706 *line == ':' || *line == '#' || slash) { 707 if (line[len - 2] == '\\') 708 slash = 1; 709 else 710 slash = 0; 711 continue; 712 } 713 if (line[len - 2] == '\\') 714 slash = 1; 715 else 716 slash = 0; 717 } 718 719 720 /* 721 * Line points to a name line. 722 */ 723 i = 0; 724 done = 0; 725 np = nbuf; 726 for (;;) { 727 for (cp = line; *cp != '\0'; cp++) { 728 if (*cp == ':') { 729 *np++ = ':'; 730 done = 1; 731 break; 732 } 733 if (*cp == '\\') 734 break; 735 *np++ = *cp; 736 } 737 if (done) { 738 *np = '\0'; 739 break; 740 } else { /* name field extends beyond the line */ 741 line = fgetln(pfp, &len); 742 if (line == NULL && pfp) { 743 (void)fclose(pfp); 744 if (ferror(pfp)) { 745 (void)cgetclose(); 746 return (-1); 747 } 748 } else 749 line[len - 1] = '\0'; 750 } 751 } 752 rp = buf; 753 for(cp = nbuf; *cp != '\0'; cp++) 754 if (*cp == '|' || *cp == ':') 755 break; 756 else 757 *rp++ = *cp; 758 759 *rp = '\0'; 760 /* 761 * XXX 762 * Last argument of getent here should be nbuf if we want true 763 * sequential access in the case of duplicates. 764 * With NULL, getent will return the first entry found 765 * rather than the duplicate entry record. This is a 766 * matter of semantics that should be resolved. 767 */ 768 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 769 if (status == -2 || status == -3) 770 (void)cgetclose(); 771 772 return (status + 1); 773 } 774 /* NOTREACHED */ 775 } 776 777 /* 778 * Cgetstr retrieves the value of the string capability cap from the 779 * capability record pointed to by buf. A pointer to a decoded, NUL 780 * terminated, malloc'd copy of the string is returned in the char * 781 * pointed to by str. The length of the string not including the trailing 782 * NUL is returned on success, -1 if the requested string capability 783 * couldn't be found, -2 if a system error was encountered (storage 784 * allocation failure). 785 */ 786 int 787 cgetstr(buf, cap, str) 788 char *buf, *cap; 789 char **str; 790 { 791 register u_int m_room; 792 register char *bp, *mp; 793 int len; 794 char *mem; 795 796 /* 797 * Find string capability cap 798 */ 799 bp = cgetcap(buf, cap, '='); 800 if (bp == NULL) 801 return (-1); 802 803 /* 804 * Conversion / storage allocation loop ... Allocate memory in 805 * chunks SFRAG in size. 806 */ 807 if ((mem = malloc(SFRAG)) == NULL) { 808 errno = ENOMEM; 809 return (-2); /* couldn't even allocate the first fragment */ 810 } 811 m_room = SFRAG; 812 mp = mem; 813 814 while (*bp != ':' && *bp != '\0') { 815 /* 816 * Loop invariants: 817 * There is always room for one more character in mem. 818 * Mp always points just past last character in mem. 819 * Bp always points at next character in buf. 820 */ 821 if (*bp == '^') { 822 bp++; 823 if (*bp == ':' || *bp == '\0') 824 break; /* drop unfinished escape */ 825 *mp++ = *bp++ & 037; 826 } else if (*bp == '\\') { 827 bp++; 828 if (*bp == ':' || *bp == '\0') 829 break; /* drop unfinished escape */ 830 if ('0' <= *bp && *bp <= '7') { 831 register int n, i; 832 833 n = 0; 834 i = 3; /* maximum of three octal digits */ 835 do { 836 n = n * 8 + (*bp++ - '0'); 837 } while (--i && '0' <= *bp && *bp <= '7'); 838 *mp++ = n; 839 } 840 else switch (*bp++) { 841 case 'b': case 'B': 842 *mp++ = '\b'; 843 break; 844 case 't': case 'T': 845 *mp++ = '\t'; 846 break; 847 case 'n': case 'N': 848 *mp++ = '\n'; 849 break; 850 case 'f': case 'F': 851 *mp++ = '\f'; 852 break; 853 case 'r': case 'R': 854 *mp++ = '\r'; 855 break; 856 case 'e': case 'E': 857 *mp++ = ESC; 858 break; 859 case 'c': case 'C': 860 *mp++ = ':'; 861 break; 862 default: 863 /* 864 * Catches '\', '^', and 865 * everything else. 866 */ 867 *mp++ = *(bp-1); 868 break; 869 } 870 } else 871 *mp++ = *bp++; 872 m_room--; 873 874 /* 875 * Enforce loop invariant: if no room left in current 876 * buffer, try to get some more. 877 */ 878 if (m_room == 0) { 879 size_t size = mp - mem; 880 881 if ((mem = realloc(mem, size + SFRAG)) == NULL) 882 return (-2); 883 m_room = SFRAG; 884 mp = mem + size; 885 } 886 } 887 *mp++ = '\0'; /* loop invariant let's us do this */ 888 m_room--; 889 len = mp - mem - 1; 890 891 /* 892 * Give back any extra memory and return value and success. 893 */ 894 if (m_room != 0) 895 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 896 return (-2); 897 *str = mem; 898 return (len); 899 } 900 901 /* 902 * Cgetustr retrieves the value of the string capability cap from the 903 * capability record pointed to by buf. The difference between cgetustr() 904 * and cgetstr() is that cgetustr does not decode escapes but rather treats 905 * all characters literally. A pointer to a NUL terminated malloc'd 906 * copy of the string is returned in the char pointed to by str. The 907 * length of the string not including the trailing NUL is returned on success, 908 * -1 if the requested string capability couldn't be found, -2 if a system 909 * error was encountered (storage allocation failure). 910 */ 911 int 912 cgetustr(buf, cap, str) 913 char *buf, *cap, **str; 914 { 915 register u_int m_room; 916 register char *bp, *mp; 917 int len; 918 char *mem; 919 920 /* 921 * Find string capability cap 922 */ 923 if ((bp = cgetcap(buf, cap, '=')) == NULL) 924 return (-1); 925 926 /* 927 * Conversion / storage allocation loop ... Allocate memory in 928 * chunks SFRAG in size. 929 */ 930 if ((mem = malloc(SFRAG)) == NULL) { 931 errno = ENOMEM; 932 return (-2); /* couldn't even allocate the first fragment */ 933 } 934 m_room = SFRAG; 935 mp = mem; 936 937 while (*bp != ':' && *bp != '\0') { 938 /* 939 * Loop invariants: 940 * There is always room for one more character in mem. 941 * Mp always points just past last character in mem. 942 * Bp always points at next character in buf. 943 */ 944 *mp++ = *bp++; 945 m_room--; 946 947 /* 948 * Enforce loop invariant: if no room left in current 949 * buffer, try to get some more. 950 */ 951 if (m_room == 0) { 952 size_t size = mp - mem; 953 954 if ((mem = realloc(mem, size + SFRAG)) == NULL) 955 return (-2); 956 m_room = SFRAG; 957 mp = mem + size; 958 } 959 } 960 *mp++ = '\0'; /* loop invariant let's us do this */ 961 m_room--; 962 len = mp - mem - 1; 963 964 /* 965 * Give back any extra memory and return value and success. 966 */ 967 if (m_room != 0) 968 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 969 return (-2); 970 *str = mem; 971 return (len); 972 } 973 974 /* 975 * Cgetnum retrieves the value of the numeric capability cap from the 976 * capability record pointed to by buf. The numeric value is returned in 977 * the long pointed to by num. 0 is returned on success, -1 if the requested 978 * numeric capability couldn't be found. 979 */ 980 int 981 cgetnum(buf, cap, num) 982 char *buf, *cap; 983 long *num; 984 { 985 register long n; 986 register int base, digit; 987 register char *bp; 988 989 /* 990 * Find numeric capability cap 991 */ 992 bp = cgetcap(buf, cap, '#'); 993 if (bp == NULL) 994 return (-1); 995 996 /* 997 * Look at value and determine numeric base: 998 * 0x... or 0X... hexadecimal, 999 * else 0... octal, 1000 * else decimal. 1001 */ 1002 if (*bp == '0') { 1003 bp++; 1004 if (*bp == 'x' || *bp == 'X') { 1005 bp++; 1006 base = 16; 1007 } else 1008 base = 8; 1009 } else 1010 base = 10; 1011 1012 /* 1013 * Conversion loop ... 1014 */ 1015 n = 0; 1016 for (;;) { 1017 if ('0' <= *bp && *bp <= '9') 1018 digit = *bp - '0'; 1019 else if ('a' <= *bp && *bp <= 'f') 1020 digit = 10 + *bp - 'a'; 1021 else if ('A' <= *bp && *bp <= 'F') 1022 digit = 10 + *bp - 'A'; 1023 else 1024 break; 1025 1026 if (digit >= base) 1027 break; 1028 1029 n = n * base + digit; 1030 bp++; 1031 } 1032 1033 /* 1034 * Return value and success. 1035 */ 1036 *num = n; 1037 return (0); 1038 } 1039 1040 1041 /* 1042 * Compare name field of record. 1043 */ 1044 static int 1045 nfcmp(nf, rec) 1046 char *nf, *rec; 1047 { 1048 char *cp, tmp; 1049 int ret; 1050 1051 for (cp = rec; *cp != ':'; cp++) 1052 ; 1053 1054 tmp = *(cp + 1); 1055 *(cp + 1) = '\0'; 1056 ret = strcmp(nf, rec); 1057 *(cp + 1) = tmp; 1058 1059 return (ret); 1060 } 1061