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