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