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