xref: /illumos-gate/usr/src/common/util/string.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
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
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
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
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
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 *
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 *
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
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
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
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
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 *
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 *
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 *
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 *
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 *
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 *
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
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
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
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
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 *
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 *
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
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
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
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
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