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