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 2018 Nexenta Systems, Inc.
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(3S)).
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
vsnprintf(char * buf,size_t buflen,const char * fmt,va_list aargs)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 if (sign && (int64_t)ul < 0)
255 ul = -ul;
256 else
257 sign = 0;
258
259 if (c == 'b') {
260 bs = va_arg(args, char *);
261 base = *bs++;
262 }
263
264 /*
265 * Fill in the number string buffer and calculate the
266 * number string length.
267 */
268 tmp = ul;
269 sp = numbuf;
270 do {
271 *sp++ = digits[tmp % base];
272 numwidth++;
273 } while ((tmp /= base) != 0);
274
275 /*
276 * Reduce the total field width by precision or the number
277 * string length depending on which one is bigger, and sign.
278 */
279 if (prec >= numwidth)
280 width -= prec;
281 else
282 width -= numwidth;
283 width -= sign;
284
285 /* Add the sign if width is '0'-padded */
286 if (sign && pad == '0')
287 ADDCHAR('-');
288
289 /* If not left-aligned, add the width padding */
290 if (!left_align) {
291 while (width-- > 0)
292 ADDCHAR(pad);
293 }
294
295 /* Add the sign if width is NOT '0'-padded */
296 if (sign && pad != '0')
297 ADDCHAR('-');
298
299 /* Add the precision '0'-padding */
300 while (prec-- > numwidth)
301 ADDCHAR('0');
302
303 /* Print out the number */
304 while (sp > numbuf) {
305 sp--;
306 ADDCHAR(*sp);
307 }
308
309 /* Add left-alignment padding */
310 while (width-- > 0)
311 ADDCHAR(' ');
312
313 if (c == 'b' && ul != 0) {
314 int any = 0;
315 c = *bs++;
316 while (c != 0) {
317 if (ul & (1 << (c - 1))) {
318 if (any++ == 0)
319 ADDCHAR('<');
320 while ((c = *bs++) >= 32)
321 ADDCHAR(c);
322 ADDCHAR(',');
323 } else {
324 while ((c = *bs++) >= 32)
325 continue;
326 }
327 }
328 if (any) {
329 bufp--;
330 ADDCHAR('>');
331 }
332 }
333 }
334 if (bufp - buf < buflen)
335 bufp[0] = c;
336 else if (buflen != 0)
337 buf[buflen - 1] = c;
338
339 va_end(args);
340
341 return (bufp - buf);
342 }
343
344 /*PRINTFLIKE3*/
345 size_t
snprintf(char * buf,size_t buflen,const char * fmt,...)346 snprintf(char *buf, size_t buflen, const char *fmt, ...)
347 {
348 va_list args;
349
350 va_start(args, fmt);
351 buflen = vsnprintf(buf, buflen, fmt, args);
352 va_end(args);
353
354 return (buflen);
355 }
356
357 #if defined(_BOOT) && defined(__sparc)
358 /*
359 * The sprintf() and vsprintf() routines aren't shared with the kernel because
360 * the DDI mandates that they return the buffer rather than its length.
361 */
362 /*PRINTFLIKE2*/
363 int
sprintf(char * buf,const char * fmt,...)364 sprintf(char *buf, const char *fmt, ...)
365 {
366 va_list args;
367
368 va_start(args, fmt);
369 (void) vsnprintf(buf, INT_MAX, fmt, args);
370 va_end(args);
371
372 return (strlen(buf));
373 }
374
375 int
vsprintf(char * buf,const char * fmt,va_list args)376 vsprintf(char *buf, const char *fmt, va_list args)
377 {
378 (void) vsnprintf(buf, INT_MAX, fmt, args);
379 return (strlen(buf));
380 }
381 #endif /* _BOOT && __sparc */
382
383 #endif /* !_KMDB && (!_BOOT || __sparc) */
384
385 char *
strcat(char * s1,const char * s2)386 strcat(char *s1, const char *s2)
387 {
388 char *os1 = s1;
389
390 while (*s1++ != '\0')
391 ;
392 s1--;
393 while ((*s1++ = *s2++) != '\0')
394 ;
395 return (os1);
396 }
397
398 char *
strchr(const char * sp,int c)399 strchr(const char *sp, int c)
400 {
401 do {
402 if (*sp == (char)c)
403 return ((char *)sp);
404 } while (*sp++);
405 return (NULL);
406 }
407
408 int
strcmp(const char * s1,const char * s2)409 strcmp(const char *s1, const char *s2)
410 {
411 while (*s1 == *s2++)
412 if (*s1++ == '\0')
413 return (0);
414 return (*(unsigned char *)s1 - *(unsigned char *)--s2);
415 }
416
417 int
strncmp(const char * s1,const char * s2,size_t n)418 strncmp(const char *s1, const char *s2, size_t n)
419 {
420 if (s1 == s2)
421 return (0);
422 n++;
423 while (--n != 0 && *s1 == *s2++)
424 if (*s1++ == '\0')
425 return (0);
426 return ((n == 0) ? 0 : *(unsigned char *)s1 - *(unsigned char *)--s2);
427 }
428
429 static const char charmap[] = {
430 '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
431 '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
432 '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
433 '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
434 '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
435 '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
436 '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
437 '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
438 '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
439 '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
440 '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
441 '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
442 '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
443 '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
444 '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
445 '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
446 '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
447 '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
448 '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
449 '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
450 '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
451 '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
452 '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
453 '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
454 '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307',
455 '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317',
456 '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327',
457 '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337',
458 '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
459 '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
460 '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
461 '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
462 };
463
464 int
strcasecmp(const char * s1,const char * s2)465 strcasecmp(const char *s1, const char *s2)
466 {
467 const unsigned char *cm = (const unsigned char *)charmap;
468 const unsigned char *us1 = (const unsigned char *)s1;
469 const unsigned char *us2 = (const unsigned char *)s2;
470
471 while (cm[*us1] == cm[*us2++])
472 if (*us1++ == '\0')
473 return (0);
474 return (cm[*us1] - cm[*(us2 - 1)]);
475 }
476
477 int
strncasecmp(const char * s1,const char * s2,size_t n)478 strncasecmp(const char *s1, const char *s2, size_t n)
479 {
480 const unsigned char *cm = (const unsigned char *)charmap;
481 const unsigned char *us1 = (const unsigned char *)s1;
482 const unsigned char *us2 = (const unsigned char *)s2;
483
484 while (n != 0 && cm[*us1] == cm[*us2++]) {
485 if (*us1++ == '\0')
486 return (0);
487 n--;
488 }
489 return (n == 0 ? 0 : cm[*us1] - cm[*(us2 - 1)]);
490 }
491
492 char *
strcpy(char * s1,const char * s2)493 strcpy(char *s1, const char *s2)
494 {
495 char *os1 = s1;
496
497 while ((*s1++ = *s2++) != '\0')
498 ;
499 return (os1);
500 }
501
502 char *
strncpy(char * s1,const char * s2,size_t n)503 strncpy(char *s1, const char *s2, size_t n)
504 {
505 char *os1 = s1;
506
507 n++;
508 while (--n != 0 && (*s1++ = *s2++) != '\0')
509 ;
510 if (n != 0)
511 while (--n != 0)
512 *s1++ = '\0';
513 return (os1);
514 }
515
516 char *
strrchr(const char * sp,int c)517 strrchr(const char *sp, int c)
518 {
519 char *r = NULL;
520
521 do {
522 if (*sp == (char)c)
523 r = (char *)sp;
524 } while (*sp++);
525
526 return (r);
527 }
528
529 char *
strstr(const char * as1,const char * as2)530 strstr(const char *as1, const char *as2)
531 {
532 const char *s1, *s2;
533 const char *tptr;
534 char c;
535
536 s1 = as1;
537 s2 = as2;
538
539 if (s2 == NULL || *s2 == '\0')
540 return ((char *)s1);
541 c = *s2;
542
543 while (*s1)
544 if (*s1++ == c) {
545 tptr = s1;
546 while ((c = *++s2) == *s1++ && c)
547 ;
548 if (c == 0)
549 return ((char *)tptr - 1);
550 s1 = tptr;
551 s2 = as2;
552 c = *s2;
553 }
554
555 return (NULL);
556 }
557
558 char *
strpbrk(const char * string,const char * brkset)559 strpbrk(const char *string, const char *brkset)
560 {
561 const char *p;
562
563 do {
564 for (p = brkset; *p != '\0' && *p != *string; ++p)
565 ;
566 if (*p != '\0')
567 return ((char *)string);
568 } while (*string++);
569
570 return (NULL);
571 }
572
573 char *
strncat(char * s1,const char * s2,size_t n)574 strncat(char *s1, const char *s2, size_t n)
575 {
576 char *os1 = s1;
577
578 n++;
579 while (*s1++ != '\0')
580 ;
581 --s1;
582 while ((*s1++ = *s2++) != '\0') {
583 if (--n == 0) {
584 s1[-1] = '\0';
585 break;
586 }
587 }
588 return (os1);
589 }
590
591 #if defined(_BOOT) || defined(_KMDB)
592 #define bcopy(src, dst, n) (void) memcpy((dst), (src), (n))
593 #endif
594
595 size_t
strlcat(char * dst,const char * src,size_t dstsize)596 strlcat(char *dst, const char *src, size_t dstsize)
597 {
598 char *df = dst;
599 size_t left = dstsize;
600 size_t l1;
601 size_t l2 = strlen(src);
602 size_t copied;
603
604 while (left-- != 0 && *df != '\0')
605 df++;
606 /*LINTED: possible ptrdiff_t overflow*/
607 l1 = (size_t)(df - dst);
608 if (dstsize == l1)
609 return (l1 + l2);
610
611 copied = l1 + l2 >= dstsize ? dstsize - l1 - 1 : l2;
612 bcopy(src, dst + l1, copied);
613 dst[l1+copied] = '\0';
614 return (l1 + l2);
615 }
616
617 size_t
strlcpy(char * dst,const char * src,size_t len)618 strlcpy(char *dst, const char *src, size_t len)
619 {
620 size_t slen = strlen(src);
621 size_t copied;
622
623 if (len == 0)
624 return (slen);
625
626 if (slen >= len)
627 copied = len - 1;
628 else
629 copied = slen;
630 bcopy(src, dst, copied);
631 dst[copied] = '\0';
632 return (slen);
633 }
634
635 size_t
strspn(const char * string,const char * charset)636 strspn(const char *string, const char *charset)
637 {
638 const char *p, *q;
639
640 for (q = string; *q != '\0'; ++q) {
641 for (p = charset; *p != '\0' && *p != *q; ++p)
642 ;
643 if (*p == '\0')
644 break;
645 }
646
647 /*LINTED: possible ptrdiff_t overflow*/
648 return ((size_t)(q - string));
649 }
650
651 size_t
strcspn(const char * string,const char * charset)652 strcspn(const char *string, const char *charset)
653 {
654 const char *p, *q;
655
656 for (q = string; *q != '\0'; ++q) {
657 for (p = charset; *p != '\0' && *p != *q; ++p)
658 ;
659 if (*p != '\0')
660 break;
661 }
662
663 /*LINTED E_PTRDIFF_OVERFLOW*/
664 return ((size_t)(q - string));
665 }
666
667 /*
668 * strsep
669 *
670 * The strsep() function locates, in the string referenced by *stringp, the
671 * first occurrence of any character in the string delim (or the terminating
672 * `\0' character) and replaces it with a `\0'. The location of the next
673 * character after the delimiter character (or NULL, if the end of the
674 * string was reached) is stored in *stringp. The original value of
675 * *stringp is returned.
676 *
677 * If *stringp is initially NULL, strsep() returns NULL.
678 *
679 * NOTE: This instance is left for in-kernel use. Libraries and programs
680 * should use strsep from libc.
681 */
682 char *
strsep(char ** stringp,const char * delim)683 strsep(char **stringp, const char *delim)
684 {
685 char *s;
686 const char *spanp;
687 int c, sc;
688 char *tok;
689
690 if ((s = *stringp) == NULL)
691 return (NULL);
692
693 for (tok = s; ; ) {
694 c = *s++;
695 spanp = delim;
696 do {
697 if ((sc = *spanp++) == c) {
698 if (c == 0)
699 s = NULL;
700 else
701 s[-1] = 0;
702 *stringp = s;
703 return (tok);
704 }
705 } while (sc != 0);
706 }
707 /* NOTREACHED */
708 }
709
710 /*
711 * Unless mentioned otherwise, all of the routines below should be added to
712 * the Solaris DDI as necessary. For now, only provide them to standalone.
713 */
714 #if defined(_BOOT) || defined(_KMDB)
715 char *
strtok(char * string,const char * sepset)716 strtok(char *string, const char *sepset)
717 {
718 char *p, *q, *r;
719 static char *savept;
720
721 /*
722 * Set `p' to our current location in the string.
723 */
724 p = (string == NULL) ? savept : string;
725 if (p == NULL)
726 return (NULL);
727
728 /*
729 * Skip leading separators; bail if no tokens remain.
730 */
731 q = p + strspn(p, sepset);
732 if (*q == '\0')
733 return (NULL);
734
735 /*
736 * Mark the end of the token and set `savept' for the next iteration.
737 */
738 if ((r = strpbrk(q, sepset)) == NULL)
739 savept = NULL;
740 else {
741 *r = '\0';
742 savept = ++r;
743 }
744
745 return (q);
746 }
747
748 /*
749 * The strlen() routine isn't shared with the kernel because it has its own
750 * hand-tuned assembly version.
751 */
752 size_t
strlen(const char * s)753 strlen(const char *s)
754 {
755 size_t n = 0;
756
757 while (*s++)
758 n++;
759 return (n);
760 }
761
762 #endif /* _BOOT || _KMDB */
763
764 /*
765 * Returns the number of non-NULL bytes in string argument,
766 * but not more than maxlen. Does not look past str + maxlen.
767 */
768 size_t
strnlen(const char * s,size_t maxlen)769 strnlen(const char *s, size_t maxlen)
770 {
771 size_t n = 0;
772
773 while (maxlen != 0 && *s != 0) {
774 s++;
775 maxlen--;
776 n++;
777 }
778
779 return (n);
780 }
781
782
783 #ifdef _KERNEL
784 /*
785 * Check for a valid C identifier:
786 * a letter or underscore, followed by
787 * zero or more letters, digits and underscores.
788 */
789
790 #define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
791
792 #define IS_ALPHA(c) \
793 (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
794
795 int
strident_valid(const char * id)796 strident_valid(const char *id)
797 {
798 int c = *id++;
799
800 if (!IS_ALPHA(c) && c != '_')
801 return (0);
802 while ((c = *id++) != 0) {
803 if (!IS_ALPHA(c) && !IS_DIGIT(c) && c != '_')
804 return (0);
805 }
806 return (1);
807 }
808
809 /*
810 * Convert a string into a valid C identifier by replacing invalid
811 * characters with '_'. Also makes sure the string is nul-terminated
812 * and takes up at most n bytes.
813 */
814 void
strident_canon(char * s,size_t n)815 strident_canon(char *s, size_t n)
816 {
817 char c;
818 char *end = s + n - 1;
819
820 ASSERT(n > 0);
821
822 if ((c = *s) == 0)
823 return;
824
825 if (!IS_ALPHA(c) && c != '_')
826 *s = '_';
827
828 while (s < end && ((c = *(++s)) != 0)) {
829 if (!IS_ALPHA(c) && !IS_DIGIT(c) && c != '_')
830 *s = '_';
831 }
832 *s = 0;
833 }
834
835 #endif /* _KERNEL */
836