1 /*- 2 * Copyright (c) 1992 The Regents of the University of California. 3 * 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 sccsid[] = "@(#)getcap.c 5.1 (Berkeley) 8/6/92"; 39 #endif /* LIBC_SCCS and not lint */ 40 41 #include <sys/types.h> 42 43 #include <ctype.h> 44 #include <errno.h> 45 #include <errno.h> 46 #include <fcntl.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <unistd.h> 51 52 #define BFRAG 1024 53 #define BSIZE 80 54 #define ESC ('[' & 037) /* ASCII ESC */ 55 #define MAX_RECURSION 32 /* maximum getent recursion */ 56 #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 57 58 static size_t topreclen; /* toprec length */ 59 static char *toprec; /* Additional record specified by cgetset() */ 60 static int gottoprec; /* Flag indicating retrieval of toprecord */ 61 62 static int getent __P((char **, u_int *, char **, int, char *, int)); 63 64 /* 65 * Cgetset() allows the addition of a user specified buffer to be added 66 * to the database array, in effect "pushing" the buffer on top of the 67 * virtual database. 0 is returned on success, -1 on failure. 68 */ 69 int 70 cgetset(ent) 71 char *ent; 72 { 73 if (ent == NULL) { 74 if (toprec) 75 free(toprec); 76 toprec = NULL; 77 topreclen = 0; 78 return (0); 79 } 80 topreclen = strlen(ent); 81 if ((toprec = malloc (topreclen + 1)) == NULL) { 82 errno = ENOMEM; 83 return (-1); 84 } 85 gottoprec = 0; 86 (void)strcpy(toprec, ent); 87 return (0); 88 } 89 90 /* 91 * Cgetcap searches the capability record buf for the capability cap with 92 * type `type'. A pointer to the value of cap is returned on success, NULL 93 * if the requested capability couldn't be found. 94 * 95 * Specifying a type of ':' means that nothing should follow cap (:cap:). 96 * In this case a pointer to the terminating ':' or NUL will be returned if 97 * cap is found. 98 * 99 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 100 * return NULL. 101 */ 102 char * 103 cgetcap(buf, cap, type) 104 char *buf, *cap; 105 int type; 106 { 107 register char *bp, *cp; 108 109 bp = buf; 110 for (;;) { 111 /* 112 * Skip past the current capability field - it's either the 113 * name field if this is the first time through the loop, or 114 * the remainder of a field whose name failed to match cap. 115 */ 116 for (;;) 117 if (*bp == '\0') 118 return (NULL); 119 else 120 if (*bp++ == ':') 121 break; 122 123 /* 124 * Try to match (cap, type) in buf. 125 */ 126 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 127 continue; 128 if (*cp != '\0') 129 continue; 130 if (*bp == '@') 131 return (NULL); 132 if (type == ':') { 133 if (*bp != '\0' && *bp != ':') 134 continue; 135 return(bp); 136 } 137 if (*bp != type) 138 continue; 139 bp++; 140 return (*bp == '@' ? NULL : bp); 141 } 142 /* NOTREACHED */ 143 } 144 145 /* 146 * Cgetent extracts the capability record name from the NULL terminated file 147 * array db_array and returns a pointer to a malloc'd copy of it in buf. 148 * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 149 * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 150 * -1 if the requested record couldn't be found, -2 if a system error was 151 * encountered (couldn't open/read a file, etc.), and -3 if a potential 152 * reference loop is detected. 153 */ 154 int 155 cgetent(buf, db_array, name) 156 char **buf, **db_array, *name; 157 { 158 u_int dummy; 159 160 return (getent(buf, &dummy, db_array, -1, name, 0)); 161 } 162 163 /* 164 * Getent implements the functions of cgetent. If fd is non-negative, 165 * *db_array has already been opened and fd is the open file descriptor. We 166 * do this to save time and avoid using up file descriptors for tc= 167 * recursions. 168 * 169 * Getent returns the same success/failure codes as cgetent. On success, a 170 * pointer to a malloc'ed capability record with all tc= capabilities fully 171 * expanded and its length (not including trailing ASCII NUL) are left in 172 * *cap and *len. 173 * 174 * Basic algorithm: 175 * + Allocate memory incrementally as needed in chunks of size BFRAG 176 * for capability buffer. 177 * + Recurse for each tc=name and interpolate result. Stop when all 178 * names interpolated, a name can't be found, or depth exceeds 179 * MAX_RECURSION. 180 */ 181 static int 182 getent(cap, len, db_array, fd, name, depth) 183 char **cap, **db_array, *name; 184 u_int *len; 185 int fd, depth; 186 { 187 register char *r_end, *rp, **db_p; 188 int myfd, eof, foundit; 189 char *record; 190 191 /* 192 * Return with ``loop detected'' error if we've recursed more than 193 * MAX_RECURSION times. 194 */ 195 if (depth > MAX_RECURSION) 196 return (-3); 197 198 /* 199 * Check if we have a top record from cgetset(). 200 */ 201 if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 202 if ((record = malloc (topreclen + BFRAG)) == NULL) { 203 errno = ENOMEM; 204 return (-2); 205 } 206 (void)strcpy(record, toprec); 207 myfd = 0; 208 db_p = db_array; 209 rp = record + topreclen + 1; 210 r_end = rp + BFRAG; 211 goto tc_exp; 212 } 213 /* 214 * Allocate first chunk of memory. 215 */ 216 if ((record = malloc(BFRAG)) == NULL) { 217 errno = ENOMEM; 218 return (-2); 219 } 220 r_end = record + BFRAG; 221 foundit = 0; 222 223 /* 224 * Loop through database array until finding the record. 225 */ 226 227 for (db_p = db_array; *db_p != NULL; db_p++) { 228 eof = 0; 229 230 /* 231 * Open database if not already open. 232 */ 233 if (fd >= 0) { 234 (void)lseek(fd, 0L, L_SET); 235 myfd = 0; 236 } else { 237 fd = open(*db_p, O_RDONLY, 0); 238 if (fd < 0) { 239 free(record); 240 return (-2); 241 } 242 myfd = 1; 243 } 244 245 /* 246 * Find the requested capability record ... 247 */ 248 { 249 char buf[BUFSIZ]; 250 register char *b_end, *bp; 251 register int c; 252 253 /* 254 * Loop invariants: 255 * There is always room for one more character in record. 256 * R_end always points just past end of record. 257 * Rp always points just past last character in record. 258 * B_end always points just past last character in buf. 259 * Bp always points at next character in buf. 260 */ 261 b_end = buf; 262 bp = buf; 263 for (;;) { 264 265 /* 266 * Read in a line implementing (\, newline) 267 * line continuation. 268 */ 269 rp = record; 270 for (;;) { 271 if (bp >= b_end) { 272 int n; 273 274 n = read(fd, buf, sizeof(buf)); 275 if (n <= 0) { 276 if (myfd) 277 (void)close(fd); 278 if (n < 0) { 279 free(record); 280 return (-2); 281 } else { 282 fd = -1; 283 eof = 1; 284 break; 285 } 286 } 287 b_end = buf+n; 288 bp = buf; 289 } 290 291 c = *bp++; 292 if (c == '\n') { 293 if (rp > record && *(rp-1) == '\\') { 294 rp--; 295 continue; 296 } else 297 break; 298 } 299 *rp++ = c; 300 301 /* 302 * Enforce loop invariant: if no room 303 * left in record buffer, try to get 304 * some more. 305 */ 306 if (rp >= r_end) { 307 u_int pos; 308 size_t newsize; 309 310 pos = rp - record; 311 newsize = r_end - record + BFRAG; 312 record = realloc(record, newsize); 313 if (record == NULL) { 314 errno = ENOMEM; 315 if (myfd) 316 (void)close(fd); 317 return (-2); 318 } 319 r_end = record + newsize; 320 rp = record + pos; 321 } 322 } 323 /* loop invariant let's us do this */ 324 *rp++ = '\0'; 325 326 /* 327 * If encountered eof check next file. 328 */ 329 if (eof) 330 break; 331 332 /* 333 * Toss blank lines and comments. 334 */ 335 if (*record == '\0' || *record == '#') 336 continue; 337 338 /* 339 * See if this is the record we want ... 340 */ 341 if (cgetmatch(record, name) == 0) { 342 foundit = 1; 343 break; /* found it! */ 344 } 345 } 346 } 347 if (foundit) 348 break; 349 } 350 351 if (!foundit) 352 return (-1); 353 354 /* 355 * Got the capability record, but now we have to expand all tc=name 356 * references in it ... 357 */ 358 tc_exp: { 359 register char *newicap, *s; 360 register int newilen; 361 u_int ilen; 362 int diff, iret, tclen; 363 char *icap, *scan, *tc, *tcstart, *tcend; 364 365 /* 366 * Loop invariants: 367 * There is room for one more character in record. 368 * R_end points just past end of record. 369 * Rp points just past last character in record. 370 * Scan points at remainder of record that needs to be 371 * scanned for tc=name constructs. 372 */ 373 scan = record; 374 for (;;) { 375 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 376 break; 377 378 /* 379 * Find end of tc=name and stomp on the trailing `:' 380 * (if present) so we can use it to call ourselves. 381 */ 382 s = tc; 383 for (;;) 384 if (*s == '\0') 385 break; 386 else 387 if (*s++ == ':') { 388 *(s-1) = '\0'; 389 break; 390 } 391 tcstart = tc - 3; 392 tclen = s - tcstart; 393 tcend = s; 394 395 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1); 396 newicap = icap; /* Put into a register. */ 397 newilen = ilen; 398 if (iret != 0) { 399 /* an error or couldn't resolve tc= */ 400 if (myfd) 401 (void)close(fd); 402 free(record); 403 return (iret); 404 } 405 406 /* not interested in name field of tc'ed record */ 407 s = newicap; 408 for (;;) 409 if (*s == '\0') 410 break; 411 else 412 if (*s++ == ':') 413 break; 414 newilen -= s - newicap; 415 newicap = s; 416 417 /* make sure interpolated record is `:'-terminated */ 418 s += newilen; 419 if (*(s-1) != ':') { 420 *s = ':'; /* overwrite NUL with : */ 421 newilen++; 422 } 423 424 /* 425 * Make sure there's enough room to insert the 426 * new record. 427 */ 428 diff = newilen - tclen; 429 if (diff >= r_end - rp) { 430 u_int pos, tcpos, tcposend; 431 size_t newsize; 432 433 pos = rp - record; 434 newsize = r_end - record + diff + BFRAG; 435 tcpos = tcstart - record; 436 tcposend = tcend - record; 437 record = realloc(record, newsize); 438 if (record == NULL) { 439 errno = ENOMEM; 440 if (myfd) 441 (void)close(fd); 442 free(icap); 443 return (-2); 444 } 445 r_end = record + newsize; 446 rp = record + pos; 447 tcstart = record + tcpos; 448 tcend = record + tcposend; 449 } 450 451 /* 452 * Insert tc'ed record into our record. 453 */ 454 s = tcstart + newilen; 455 bcopy(tcend, s, rp - tcend); 456 bcopy(newicap, tcstart, newilen); 457 rp += diff; 458 free(icap); 459 460 /* 461 * Start scan on `:' so next cgetcap works properly 462 * (cgetcap always skips first field). 463 */ 464 scan = s-1; 465 } 466 } 467 468 /* 469 * Close file (if we opened it), give back any extra memory, and 470 * return capability, length and success. 471 */ 472 if (myfd) 473 (void)close(fd); 474 *len = rp - record - 1; /* don't count NUL */ 475 if (r_end > rp) 476 record = realloc(record, (size_t)(rp - record)); 477 *cap = record; 478 return (0); 479 } 480 481 /* 482 * Cgetmatch will return 0 if name is one of the names of the capability 483 * record buf, -1 if not. 484 */ 485 int 486 cgetmatch(buf, name) 487 char *buf, *name; 488 { 489 register char *np, *bp; 490 491 /* 492 * Start search at beginning of record. 493 */ 494 bp = buf; 495 for (;;) { 496 /* 497 * Try to match a record name. 498 */ 499 np = name; 500 for (;;) 501 if (*np == '\0') 502 if (*bp == '|' || *bp == ':' || *bp == '\0') 503 return (0); 504 else 505 break; 506 else 507 if (*bp++ != *np++) 508 break; 509 510 /* 511 * Match failed, skip to next name in record. 512 */ 513 bp--; /* a '|' or ':' may have stopped the match */ 514 for (;;) 515 if (*bp == '\0' || *bp == ':') 516 return (-1); /* match failed totally */ 517 else 518 if (*bp++ == '|') 519 break; /* found next name */ 520 } 521 } 522 523 524 525 526 527 int 528 cgetfirst(buf, db_array) 529 char **buf, **db_array; 530 { 531 (void)cgetclose(); 532 return (cgetnext(buf, db_array)); 533 } 534 535 static FILE *pfp; 536 static int slash; 537 static char **dbp; 538 539 int 540 cgetclose() 541 { 542 if (pfp != NULL) { 543 (void)fclose(pfp); 544 pfp = NULL; 545 } 546 dbp = NULL; 547 gottoprec = 0; 548 slash = 0; 549 return(0); 550 } 551 552 /* 553 * Cgetnext() gets either the first or next entry in the logical database 554 * specified by db_array. It returns 0 upon completion of the database, 1 555 * upon returning an entry with more remaining, and -1 if an error occurs. 556 */ 557 int 558 cgetnext(bp, db_array) 559 register char **bp; 560 char **db_array; 561 { 562 size_t len; 563 int status; 564 char *cp, *line, *rp, buf[BSIZE]; 565 u_int dummy; 566 567 if (dbp == NULL) 568 dbp = db_array; 569 570 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 571 (void)cgetclose(); 572 return (-1); 573 } 574 for(;;) { 575 if (toprec && !gottoprec) { 576 gottoprec = 1; 577 line = toprec; 578 } else { 579 line = fgetline(pfp, &len); 580 if (line == NULL && pfp) { 581 (void)fclose(pfp); 582 if (ferror(pfp)) { 583 (void)cgetclose(); 584 return (-1); 585 } else { 586 dbp++; 587 if (*dbp == NULL) { 588 (void)cgetclose(); 589 return (0); 590 } else if ((pfp = fopen(*dbp, "r")) == 591 NULL) { 592 (void)cgetclose(); 593 return (-1); 594 } else 595 continue; 596 } 597 } 598 if (isspace(*line) || *line == ':' || *line == '#' 599 || len == 0 || slash) { 600 if (len > 0 && line[len - 1] == '\\') 601 slash = 1; 602 else 603 slash = 0; 604 continue; 605 } 606 if (len > 0 && line[len - 1] == '\\') 607 slash = 1; 608 else 609 slash = 0; 610 } 611 /* line points to a name line */ 612 613 rp = buf; 614 for(cp = line; *cp != NULL; cp++) 615 if (*cp == '|' || *cp == ':') 616 break; 617 else 618 *rp++ = *cp; 619 620 *rp = '\0'; 621 status = getent(bp, &dummy, db_array, -1, buf, 0); 622 if (status == 0) 623 return (1); 624 if (status == -2 || status == -3) { 625 (void)cgetclose(); 626 return (status + 1); 627 } 628 } 629 /* NOTREACHED */ 630 } 631 632 /* 633 * Cgetstr retrieves the value of the string capability cap from the 634 * capability record pointed to by buf. A pointer to a decoded, NUL 635 * terminated, malloc'd copy of the string is returned in the char * 636 * pointed to by str. The length of the string not including the trailing 637 * NUL is returned on success, -1 if the requested string capability 638 * couldn't be found, -2 if a system error was encountered (storage 639 * allocation failure). 640 */ 641 int 642 cgetstr(buf, cap, str) 643 char *buf, *cap; 644 char **str; 645 { 646 register u_int m_room; 647 register char *bp, *mp; 648 int len; 649 char *mem; 650 651 /* 652 * Find string capability cap 653 */ 654 bp = cgetcap(buf, cap, '='); 655 if (bp == NULL) 656 return (-1); 657 658 /* 659 * Conversion / storage allocation loop ... Allocate memory in 660 * chunks SFRAG in size. 661 */ 662 if ((mem = malloc(SFRAG)) == NULL) { 663 errno = ENOMEM; 664 return (-2); /* couldn't even allocate the first fragment */ 665 } 666 m_room = SFRAG; 667 mp = mem; 668 669 while (*bp != ':' && *bp != '\0') { 670 /* 671 * Loop invariants: 672 * There is always room for one more character in mem. 673 * Mp always points just past last character in mem. 674 * Bp always points at next character in buf. 675 */ 676 if (*bp == '^') { 677 bp++; 678 if (*bp == ':' || *bp == '\0') 679 break; /* drop unfinished escape */ 680 *mp++ = *bp++ & 037; 681 } else if (*bp == '\\') { 682 bp++; 683 if (*bp == ':' || *bp == '\0') 684 break; /* drop unfinished escape */ 685 if ('0' <= *bp && *bp <= '7') { 686 register int n, i; 687 688 n = 0; 689 i = 3; /* maximum of three octal digits */ 690 do { 691 n = n * 8 + (*bp++ - '0'); 692 } while (--i && '0' <= *bp && *bp <= '7'); 693 *mp++ = n; 694 } 695 else switch (*bp++) { 696 case 'b': case 'B': 697 *mp++ = '\b'; 698 break; 699 case 't': case 'T': 700 *mp++ = '\t'; 701 break; 702 case 'n': case 'N': 703 *mp++ = '\n'; 704 break; 705 case 'f': case 'F': 706 *mp++ = '\f'; 707 break; 708 case 'r': case 'R': 709 *mp++ = '\r'; 710 break; 711 case 'e': case 'E': 712 *mp++ = ESC; 713 break; 714 case 'c': case 'C': 715 *mp++ = ':'; 716 break; 717 default: 718 /* 719 * Catches '\', '^', and 720 * everything else. 721 */ 722 *mp++ = *(bp-1); 723 break; 724 } 725 } else 726 *mp++ = *bp++; 727 m_room--; 728 729 /* 730 * Enforce loop invariant: if no room left in current 731 * buffer, try to get some more. 732 */ 733 if (m_room == 0) { 734 size_t size = mp - mem; 735 736 if ((mem = realloc(mem, size + SFRAG)) == NULL) 737 return (-2); 738 m_room = SFRAG; 739 mp = mem + size; 740 } 741 } 742 *mp++ = '\0'; /* loop invariant let's us do this */ 743 m_room--; 744 len = mp - mem - 1; 745 746 /* 747 * Give back any extra memory and return value and success. 748 */ 749 if (m_room != 0) 750 mem = realloc(mem, (size_t)(mp - mem)); 751 *str = mem; 752 return (len); 753 } 754 755 /* 756 * Cgetustr retrieves the value of the string capability cap from the 757 * capability record pointed to by buf. The difference between cgetustr() 758 * and cgetstr() is that cgetustr does not decode escapes but rather treats 759 * all characters literally. A pointer to a NUL terminated malloc'd 760 * copy of the string is returned in the char pointed to by str. The 761 * length of the string not including the trailing NUL is returned on success, 762 * -1 if the requested string capability couldn't be found, -2 if a system 763 * error was encountered (storage allocation failure). 764 */ 765 int 766 cgetustr(buf, cap, str) 767 char *buf, *cap, **str; 768 { 769 register u_int m_room; 770 register char *bp, *mp; 771 int len; 772 char *mem; 773 774 /* 775 * Find string capability cap 776 */ 777 if ((bp = cgetcap(buf, cap, '=')) == NULL) 778 return (-1); 779 780 /* 781 * Conversion / storage allocation loop ... Allocate memory in 782 * chunks SFRAG in size. 783 */ 784 if ((mem = malloc(SFRAG)) == NULL) { 785 errno = ENOMEM; 786 return (-2); /* couldn't even allocate the first fragment */ 787 } 788 m_room = SFRAG; 789 mp = mem; 790 791 while (*bp != ':' && *bp != '\0') { 792 /* 793 * Loop invariants: 794 * There is always room for one more character in mem. 795 * Mp always points just past last character in mem. 796 * Bp always points at next character in buf. 797 */ 798 *mp++ = *bp++; 799 m_room--; 800 801 /* 802 * Enforce loop invariant: if no room left in current 803 * buffer, try to get some more. 804 */ 805 if (m_room == 0) { 806 size_t size = mp - mem; 807 808 if ((mem = realloc(mem, size + SFRAG)) == NULL) 809 return (-2); 810 m_room = SFRAG; 811 mp = mem + size; 812 } 813 } 814 *mp++ = '\0'; /* loop invariant let's us do this */ 815 m_room--; 816 len = mp - mem - 1; 817 818 /* 819 * Give back any extra memory and return value and success. 820 */ 821 if (m_room != 0) 822 mem = realloc(mem, (size_t)(mp - mem)); 823 *str = mem; 824 return (len); 825 } 826 827 /* 828 * Cgetnum retrieves the value of the numeric capability cap from the 829 * capability record pointed to by buf. The numeric value is returned in 830 * the long pointed to by num. 0 is returned on success, -1 if the requested 831 * numeric capability couldn't be found. 832 */ 833 int 834 cgetnum(buf, cap, num) 835 char *buf, *cap; 836 long *num; 837 { 838 register long n; 839 register int base, digit; 840 register char *bp; 841 842 /* 843 * Find numeric capability cap 844 */ 845 bp = cgetcap(buf, cap, '#'); 846 if (bp == NULL) 847 return (-1); 848 849 /* 850 * Look at value and determine numeric base: 851 * 0x... or 0X... hexadecimal, 852 * else 0... octal, 853 * else decimal. 854 */ 855 if (*bp == '0') { 856 bp++; 857 if (*bp == 'x' || *bp == 'X') { 858 bp++; 859 base = 16; 860 } else 861 base = 8; 862 } else 863 base = 10; 864 865 /* 866 * Conversion loop ... 867 */ 868 n = 0; 869 for (;;) { 870 if ('0' <= *bp && *bp <= '9') 871 digit = *bp - '0'; 872 else 873 if ( ('a' <= *bp && *bp <= 'f') 874 || ('A' <= *bp && *bp <= 'F')) 875 digit = 10 + *bp - 'a'; 876 else 877 break; 878 879 if (digit >= base) 880 break; 881 882 n = n * base + digit; 883 bp++; 884 } 885 886 /* 887 * Return value and success. 888 */ 889 *num = n; 890 return (0); 891 } 892