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