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