1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * Copyright 2014 Joyent, Inc. All rights reserved. 25 */ 26 27 /* 28 * Copyright (c) 2016 by Delphix. All rights reserved. 29 * Copyright 2022 Tintri by DDN, Inc. All rights reserved. 30 */ 31 32 /* 33 * Implementations of the functions described in vsnprintf(3C) and string(3C), 34 * for use by the kernel, the standalone, and kmdb. Unless otherwise specified, 35 * these functions match the section 3C manpages. 36 */ 37 38 #include <sys/types.h> 39 #include <sys/null.h> 40 #include <sys/varargs.h> 41 42 #if defined(_KERNEL) 43 #include <sys/systm.h> 44 #include <sys/debug.h> 45 #elif !defined(_BOOT) 46 #include <string.h> 47 #endif 48 49 #include "memcpy.h" 50 #include "string.h" 51 52 /* 53 * We don't need these for x86 boot or kmdb. 54 */ 55 #if !defined(_KMDB) && (!defined(_BOOT) || defined(__sparc)) 56 57 #define ADDCHAR(c) if (bufp++ - buf < buflen) bufp[-1] = (c) 58 59 /* 60 * Given a buffer 'buf' of size 'buflen', render as much of the string 61 * described by <fmt, args> as possible. The string will always be 62 * null-terminated, so the maximum string length is 'buflen - 1'. 63 * Returns the number of bytes that would be necessary to render the 64 * entire string, not including null terminator (just like vsnprintf(3C)). 65 * To determine buffer size in advance, use vsnprintf(NULL, 0, fmt, args) + 1. 66 * 67 * There is no support for floating point, and the C locale is assumed. 68 */ 69 size_t 70 vsnprintf(char *buf, size_t buflen, const char *fmt, va_list aargs) 71 { 72 uint64_t ul, tmp; 73 char *bufp = buf; /* current buffer pointer */ 74 char c, pad; 75 int width, base, sign, num; 76 int prec, h_count, l_count, dot_count; 77 int pad_count, transfer_count, left_align; 78 char *digits, *sp, *bs; 79 char numbuf[65]; /* sufficient for a 64-bit binary value */ 80 int numwidth; 81 va_list args; 82 83 ul = 0; 84 bs = NULL; 85 /* 86 * Make a copy so that all our callers don't have to make a copy 87 */ 88 va_copy(args, aargs); 89 90 if ((ssize_t)buflen < 0) 91 buflen = 0; 92 93 while ((c = *fmt++) != '\0') { 94 if (c != '%') { 95 ADDCHAR(c); 96 continue; 97 } 98 99 width = prec = numwidth = 0; 100 left_align = base = sign = 0; 101 h_count = l_count = dot_count = 0; 102 pad = ' '; 103 digits = "0123456789abcdef"; 104 next_fmt: 105 if ((c = *fmt++) == '\0') 106 break; 107 108 if (c >= 'A' && c <= 'Z') { 109 c += 'a' - 'A'; 110 digits = "0123456789ABCDEF"; 111 } 112 113 switch (c) { 114 case '-': 115 left_align++; 116 goto next_fmt; 117 case '0': 118 if (dot_count == 0) 119 pad = '0'; 120 /*FALLTHROUGH*/ 121 case '1': 122 case '2': 123 case '3': 124 case '4': 125 case '5': 126 case '6': 127 case '7': 128 case '8': 129 case '9': 130 num = 0; 131 for (;;) { 132 num = 10 * num + c - '0'; 133 c = *fmt; 134 if (c < '0' || c > '9') 135 break; 136 else 137 fmt++; 138 } 139 if (dot_count > 0) 140 prec = num; 141 else 142 width = num; 143 144 goto next_fmt; 145 case '.': 146 dot_count++; 147 goto next_fmt; 148 case '*': 149 if (dot_count > 0) 150 prec = (int)va_arg(args, int); 151 else 152 width = (int)va_arg(args, int); 153 goto next_fmt; 154 case 'l': 155 l_count++; 156 goto next_fmt; 157 case 'h': 158 h_count++; 159 goto next_fmt; 160 case 'd': 161 sign = 1; 162 /*FALLTHROUGH*/ 163 case 'u': 164 base = 10; 165 break; 166 case 'p': 167 l_count = 1; 168 /*FALLTHROUGH*/ 169 case 'x': 170 base = 16; 171 break; 172 case 'o': 173 base = 8; 174 break; 175 case 'b': 176 l_count = 0; 177 base = 1; 178 break; 179 case 'c': 180 c = (char)va_arg(args, int); 181 ADDCHAR(c); 182 break; 183 case 's': 184 sp = va_arg(args, char *); 185 if (sp == NULL) { 186 sp = "<null string>"; 187 /* avoid truncation */ 188 prec = strlen(sp); 189 } 190 /* 191 * Handle simple case specially to avoid 192 * performance hit of strlen() 193 */ 194 if (prec == 0 && width == 0) { 195 while ((c = *sp++) != 0) 196 ADDCHAR(c); 197 break; 198 } 199 if (prec > 0) { 200 transfer_count = strnlen(sp, prec); 201 /* widen field if too narrow */ 202 if (prec > width) 203 width = prec; 204 } else 205 transfer_count = strlen(sp); 206 if (width > transfer_count) 207 pad_count = width - transfer_count; 208 else 209 pad_count = 0; 210 while ((!left_align) && (pad_count-- > 0)) 211 ADDCHAR(' '); 212 /* ADDCHAR() evaluates arg at most once */ 213 while (transfer_count-- > 0) 214 ADDCHAR(*sp++); 215 while ((left_align) && (pad_count-- > 0)) 216 ADDCHAR(' '); 217 break; 218 case '%': 219 ADDCHAR('%'); 220 break; 221 } 222 223 if (base == 0) 224 continue; 225 226 if (h_count == 0 && l_count == 0) { 227 if (sign) 228 ul = (int64_t)va_arg(args, int); 229 else 230 ul = (int64_t)va_arg(args, unsigned int); 231 } else if (l_count > 1) { 232 if (sign) 233 ul = (int64_t)va_arg(args, int64_t); 234 else 235 ul = (int64_t)va_arg(args, uint64_t); 236 } else if (l_count > 0) { 237 if (sign) 238 ul = (int64_t)va_arg(args, long); 239 else 240 ul = (int64_t)va_arg(args, unsigned long); 241 } else if (h_count > 1) { 242 if (sign) 243 ul = (int64_t)((char)va_arg(args, int)); 244 else 245 ul = (int64_t)((unsigned char)va_arg(args, 246 int)); 247 } else if (h_count > 0) { 248 if (sign) 249 ul = (int64_t)((short)va_arg(args, int)); 250 else 251 ul = (int64_t)((unsigned short)va_arg(args, 252 int)); 253 } 254 255 if (sign && (int64_t)ul < 0) 256 ul = -ul; 257 else 258 sign = 0; 259 260 if (c == 'b') { 261 bs = va_arg(args, char *); 262 base = *bs++; 263 } 264 265 /* 266 * Fill in the number string buffer and calculate the 267 * number string length. 268 */ 269 tmp = ul; 270 sp = numbuf; 271 do { 272 *sp++ = digits[tmp % base]; 273 numwidth++; 274 } while ((tmp /= base) != 0); 275 276 /* 277 * Reduce the total field width by precision or the number 278 * string length depending on which one is bigger, and sign. 279 */ 280 if (prec >= numwidth) 281 width -= prec; 282 else 283 width -= numwidth; 284 width -= sign; 285 286 /* Add the sign if width is '0'-padded */ 287 if (sign && pad == '0') 288 ADDCHAR('-'); 289 290 /* If not left-aligned, add the width padding */ 291 if (!left_align) { 292 while (width-- > 0) 293 ADDCHAR(pad); 294 } 295 296 /* Add the sign if width is NOT '0'-padded */ 297 if (sign && pad != '0') 298 ADDCHAR('-'); 299 300 /* Add the precision '0'-padding */ 301 while (prec-- > numwidth) 302 ADDCHAR('0'); 303 304 /* Print out the number */ 305 while (sp > numbuf) { 306 sp--; 307 ADDCHAR(*sp); 308 } 309 310 /* Add left-alignment padding */ 311 while (width-- > 0) 312 ADDCHAR(' '); 313 314 if (c == 'b' && ul != 0) { 315 int any = 0; 316 c = *bs++; 317 while (c != 0) { 318 if (ul & (1 << (c - 1))) { 319 if (any++ == 0) 320 ADDCHAR('<'); 321 while ((c = *bs++) >= 32) 322 ADDCHAR(c); 323 ADDCHAR(','); 324 } else { 325 while ((c = *bs++) >= 32) 326 continue; 327 } 328 } 329 if (any) { 330 bufp--; 331 ADDCHAR('>'); 332 } 333 } 334 } 335 if (bufp - buf < buflen) 336 bufp[0] = c; 337 else if (buflen != 0) 338 buf[buflen - 1] = c; 339 340 va_end(args); 341 342 return (bufp - buf); 343 } 344 345 /*PRINTFLIKE3*/ 346 size_t 347 snprintf(char *buf, size_t buflen, const char *fmt, ...) 348 { 349 va_list args; 350 351 va_start(args, fmt); 352 buflen = vsnprintf(buf, buflen, fmt, args); 353 va_end(args); 354 355 return (buflen); 356 } 357 358 #if defined(_BOOT) && defined(__sparc) 359 /* 360 * The sprintf() and vsprintf() routines aren't shared with the kernel because 361 * the DDI mandates that they return the buffer rather than its length. 362 */ 363 /*PRINTFLIKE2*/ 364 int 365 sprintf(char *buf, const char *fmt, ...) 366 { 367 va_list args; 368 369 va_start(args, fmt); 370 (void) vsnprintf(buf, INT_MAX, fmt, args); 371 va_end(args); 372 373 return (strlen(buf)); 374 } 375 376 int 377 vsprintf(char *buf, const char *fmt, va_list args) 378 { 379 (void) vsnprintf(buf, INT_MAX, fmt, args); 380 return (strlen(buf)); 381 } 382 #endif /* _BOOT && __sparc */ 383 384 #endif /* !_KMDB && (!_BOOT || __sparc) */ 385 386 char * 387 strcat(char *s1, const char *s2) 388 { 389 char *os1 = s1; 390 391 while (*s1++ != '\0') 392 ; 393 s1--; 394 while ((*s1++ = *s2++) != '\0') 395 ; 396 return (os1); 397 } 398 399 char * 400 strchr(const char *sp, int c) 401 { 402 do { 403 if (*sp == (char)c) 404 return ((char *)sp); 405 } while (*sp++); 406 return (NULL); 407 } 408 409 int 410 strcmp(const char *s1, const char *s2) 411 { 412 while (*s1 == *s2++) 413 if (*s1++ == '\0') 414 return (0); 415 return (*(unsigned char *)s1 - *(unsigned char *)--s2); 416 } 417 418 int 419 strncmp(const char *s1, const char *s2, size_t n) 420 { 421 if (s1 == s2) 422 return (0); 423 n++; 424 while (--n != 0 && *s1 == *s2++) 425 if (*s1++ == '\0') 426 return (0); 427 return ((n == 0) ? 0 : *(unsigned char *)s1 - *(unsigned char *)--s2); 428 } 429 430 static const char charmap[] = { 431 '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', 432 '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', 433 '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', 434 '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', 435 '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047', 436 '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057', 437 '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', 438 '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077', 439 '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147', 440 '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', 441 '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', 442 '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137', 443 '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147', 444 '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', 445 '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', 446 '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177', 447 '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207', 448 '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217', 449 '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227', 450 '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237', 451 '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247', 452 '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257', 453 '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', 454 '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', 455 '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307', 456 '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317', 457 '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327', 458 '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337', 459 '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347', 460 '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', 461 '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', 462 '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377', 463 }; 464 465 int 466 strcasecmp(const char *s1, const char *s2) 467 { 468 const unsigned char *cm = (const unsigned char *)charmap; 469 const unsigned char *us1 = (const unsigned char *)s1; 470 const unsigned char *us2 = (const unsigned char *)s2; 471 472 while (cm[*us1] == cm[*us2++]) 473 if (*us1++ == '\0') 474 return (0); 475 return (cm[*us1] - cm[*(us2 - 1)]); 476 } 477 478 int 479 strncasecmp(const char *s1, const char *s2, size_t n) 480 { 481 const unsigned char *cm = (const unsigned char *)charmap; 482 const unsigned char *us1 = (const unsigned char *)s1; 483 const unsigned char *us2 = (const unsigned char *)s2; 484 485 while (n != 0 && cm[*us1] == cm[*us2++]) { 486 if (*us1++ == '\0') 487 return (0); 488 n--; 489 } 490 return (n == 0 ? 0 : cm[*us1] - cm[*(us2 - 1)]); 491 } 492 493 char * 494 strcpy(char *s1, const char *s2) 495 { 496 char *os1 = s1; 497 498 while ((*s1++ = *s2++) != '\0') 499 ; 500 return (os1); 501 } 502 503 char * 504 strncpy(char *s1, const char *s2, size_t n) 505 { 506 char *os1 = s1; 507 508 n++; 509 while (--n != 0 && (*s1++ = *s2++) != '\0') 510 ; 511 if (n != 0) 512 while (--n != 0) 513 *s1++ = '\0'; 514 return (os1); 515 } 516 517 char * 518 strrchr(const char *sp, int c) 519 { 520 char *r = NULL; 521 522 do { 523 if (*sp == (char)c) 524 r = (char *)sp; 525 } while (*sp++); 526 527 return (r); 528 } 529 530 char * 531 strstr(const char *as1, const char *as2) 532 { 533 const char *s1, *s2; 534 const char *tptr; 535 char c; 536 537 s1 = as1; 538 s2 = as2; 539 540 if (s2 == NULL || *s2 == '\0') 541 return ((char *)s1); 542 c = *s2; 543 544 while (*s1) 545 if (*s1++ == c) { 546 tptr = s1; 547 while ((c = *++s2) == *s1++ && c) 548 ; 549 if (c == 0) 550 return ((char *)tptr - 1); 551 s1 = tptr; 552 s2 = as2; 553 c = *s2; 554 } 555 556 return (NULL); 557 } 558 559 char * 560 strpbrk(const char *string, const char *brkset) 561 { 562 const char *p; 563 564 do { 565 for (p = brkset; *p != '\0' && *p != *string; ++p) 566 ; 567 if (*p != '\0') 568 return ((char *)string); 569 } while (*string++); 570 571 return (NULL); 572 } 573 574 char * 575 strncat(char *s1, const char *s2, size_t n) 576 { 577 char *os1 = s1; 578 579 n++; 580 while (*s1++ != '\0') 581 ; 582 --s1; 583 while ((*s1++ = *s2++) != '\0') { 584 if (--n == 0) { 585 s1[-1] = '\0'; 586 break; 587 } 588 } 589 return (os1); 590 } 591 592 #if defined(_BOOT) || defined(_KMDB) 593 #define bcopy(src, dst, n) (void) memcpy((dst), (src), (n)) 594 #endif 595 596 size_t 597 strlcat(char *dst, const char *src, size_t dstsize) 598 { 599 char *df = dst; 600 size_t left = dstsize; 601 size_t l1; 602 size_t l2 = strlen(src); 603 size_t copied; 604 605 while (left-- != 0 && *df != '\0') 606 df++; 607 /*LINTED: possible ptrdiff_t overflow*/ 608 l1 = (size_t)(df - dst); 609 if (dstsize == l1) 610 return (l1 + l2); 611 612 copied = l1 + l2 >= dstsize ? dstsize - l1 - 1 : l2; 613 bcopy(src, dst + l1, copied); 614 dst[l1+copied] = '\0'; 615 return (l1 + l2); 616 } 617 618 size_t 619 strlcpy(char *dst, const char *src, size_t len) 620 { 621 size_t slen = strlen(src); 622 size_t copied; 623 624 if (len == 0) 625 return (slen); 626 627 if (slen >= len) 628 copied = len - 1; 629 else 630 copied = slen; 631 bcopy(src, dst, copied); 632 dst[copied] = '\0'; 633 return (slen); 634 } 635 636 size_t 637 strspn(const char *string, const char *charset) 638 { 639 const char *p, *q; 640 641 for (q = string; *q != '\0'; ++q) { 642 for (p = charset; *p != '\0' && *p != *q; ++p) 643 ; 644 if (*p == '\0') 645 break; 646 } 647 648 /*LINTED: possible ptrdiff_t overflow*/ 649 return ((size_t)(q - string)); 650 } 651 652 size_t 653 strcspn(const char *string, const char *charset) 654 { 655 const char *p, *q; 656 657 for (q = string; *q != '\0'; ++q) { 658 for (p = charset; *p != '\0' && *p != *q; ++p) 659 ; 660 if (*p != '\0') 661 break; 662 } 663 664 /*LINTED E_PTRDIFF_OVERFLOW*/ 665 return ((size_t)(q - string)); 666 } 667 668 /* 669 * strsep 670 * 671 * The strsep() function locates, in the string referenced by *stringp, the 672 * first occurrence of any character in the string delim (or the terminating 673 * `\0' character) and replaces it with a `\0'. The location of the next 674 * character after the delimiter character (or NULL, if the end of the 675 * string was reached) is stored in *stringp. The original value of 676 * *stringp is returned. 677 * 678 * If *stringp is initially NULL, strsep() returns NULL. 679 * 680 * NOTE: This instance is left for in-kernel use. Libraries and programs 681 * should use strsep from libc. 682 */ 683 char * 684 strsep(char **stringp, const char *delim) 685 { 686 char *s; 687 const char *spanp; 688 int c, sc; 689 char *tok; 690 691 if ((s = *stringp) == NULL) 692 return (NULL); 693 694 for (tok = s; ; ) { 695 c = *s++; 696 spanp = delim; 697 do { 698 if ((sc = *spanp++) == c) { 699 if (c == 0) 700 s = NULL; 701 else 702 s[-1] = 0; 703 *stringp = s; 704 return (tok); 705 } 706 } while (sc != 0); 707 } 708 /* NOTREACHED */ 709 } 710 711 /* 712 * strtok_r 713 * 714 * uses strpbrk and strspn to break string into tokens on 715 * sequentially subsequent calls. returns NULL when no 716 * non-separator characters remain. 717 * `subsequent' calls are calls with first argument NULL. 718 */ 719 char * 720 strtok_r(char *string, const char *sepset, char **lasts) 721 { 722 char *q, *r; 723 724 /* first or subsequent call */ 725 if (string == NULL) 726 string = *lasts; 727 728 if (string == NULL) /* return if no tokens remaining */ 729 return (NULL); 730 731 q = string + strspn(string, sepset); /* skip leading separators */ 732 733 if (*q == '\0') /* return if no tokens remaining */ 734 return (NULL); 735 736 if ((r = strpbrk(q, sepset)) == NULL) { /* move past token */ 737 *lasts = NULL; /* indicate this is last token */ 738 } else { 739 *r = '\0'; 740 *lasts = r + 1; 741 } 742 return (q); 743 } 744 745 /* 746 * Unless mentioned otherwise, all of the routines below should be added to 747 * the Solaris DDI as necessary. For now, only provide them to standalone. 748 */ 749 #if defined(_BOOT) || defined(_KMDB) 750 char * 751 strtok(char *string, const char *sepset) 752 { 753 char *p, *q, *r; 754 static char *savept; 755 756 /* 757 * Set `p' to our current location in the string. 758 */ 759 p = (string == NULL) ? savept : string; 760 if (p == NULL) 761 return (NULL); 762 763 /* 764 * Skip leading separators; bail if no tokens remain. 765 */ 766 q = p + strspn(p, sepset); 767 if (*q == '\0') 768 return (NULL); 769 770 /* 771 * Mark the end of the token and set `savept' for the next iteration. 772 */ 773 if ((r = strpbrk(q, sepset)) == NULL) 774 savept = NULL; 775 else { 776 *r = '\0'; 777 savept = ++r; 778 } 779 780 return (q); 781 } 782 783 /* 784 * The strlen() routine isn't shared with the kernel because it has its own 785 * hand-tuned assembly version. 786 */ 787 size_t 788 strlen(const char *s) 789 { 790 size_t n = 0; 791 792 while (*s++) 793 n++; 794 return (n); 795 } 796 797 #endif /* _BOOT || _KMDB */ 798 799 /* 800 * Returns the number of non-NULL bytes in string argument, 801 * but not more than maxlen. Does not look past str + maxlen. 802 */ 803 size_t 804 strnlen(const char *s, size_t maxlen) 805 { 806 size_t n = 0; 807 808 while (maxlen != 0 && *s != 0) { 809 s++; 810 maxlen--; 811 n++; 812 } 813 814 return (n); 815 } 816 817 818 #ifdef _KERNEL 819 /* 820 * Check for a valid C identifier: 821 * a letter or underscore, followed by 822 * zero or more letters, digits and underscores. 823 */ 824 825 #define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') 826 827 #define IS_ALPHA(c) \ 828 (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) 829 830 int 831 strident_valid(const char *id) 832 { 833 int c = *id++; 834 835 if (!IS_ALPHA(c) && c != '_') 836 return (0); 837 while ((c = *id++) != 0) { 838 if (!IS_ALPHA(c) && !IS_DIGIT(c) && c != '_') 839 return (0); 840 } 841 return (1); 842 } 843 844 /* 845 * Convert a string into a valid C identifier by replacing invalid 846 * characters with '_'. Also makes sure the string is nul-terminated 847 * and takes up at most n bytes. 848 */ 849 void 850 strident_canon(char *s, size_t n) 851 { 852 char c; 853 char *end = s + n - 1; 854 855 ASSERT(n > 0); 856 857 if ((c = *s) == 0) 858 return; 859 860 if (!IS_ALPHA(c) && c != '_') 861 *s = '_'; 862 863 while (s < end && ((c = *(++s)) != 0)) { 864 if (!IS_ALPHA(c) && !IS_DIGIT(c) && c != '_') 865 *s = '_'; 866 } 867 *s = 0; 868 } 869 870 #endif /* _KERNEL */ 871