xref: /illumos-gate/usr/src/common/util/string.c (revision 584b574a)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5049fa28aSdr  * Common Development and Distribution License (the "License").
6049fa28aSdr  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
227637daddSmyers  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
24237b1f31SPatrick Mooney  * Copyright 2014 Joyent, Inc.  All rights reserved.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
273c5da275SMatthew Ahrens /*
283c5da275SMatthew Ahrens  * Copyright (c) 2016 by Delphix. All rights reserved.
2977889f88SYuri Pankov  * Copyright 2018 Nexenta Systems, Inc.
303c5da275SMatthew Ahrens  */
313c5da275SMatthew Ahrens 
327c478bd9Sstevel@tonic-gate /*
337c478bd9Sstevel@tonic-gate  * Implementations of the functions described in vsnprintf(3C) and string(3C),
347c478bd9Sstevel@tonic-gate  * for use by the kernel, the standalone, and kmdb.  Unless otherwise specified,
357c478bd9Sstevel@tonic-gate  * these functions match the section 3C manpages.
367c478bd9Sstevel@tonic-gate  */
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include <sys/types.h>
394870e0a7SRichard PALO #include <sys/null.h>
407c478bd9Sstevel@tonic-gate #include <sys/varargs.h>
41ae115bc7Smrj 
42ae115bc7Smrj #if defined(_KERNEL)
437c478bd9Sstevel@tonic-gate #include <sys/systm.h>
447c478bd9Sstevel@tonic-gate #include <sys/debug.h>
45ae115bc7Smrj #elif !defined(_BOOT)
46ae115bc7Smrj #include <string.h>
47ae115bc7Smrj #endif
48ae115bc7Smrj 
49ae115bc7Smrj #include "memcpy.h"
50ae115bc7Smrj #include "string.h"
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /*
53ae115bc7Smrj  * We don't need these for x86 boot or kmdb.
547c478bd9Sstevel@tonic-gate  */
55ae115bc7Smrj #if !defined(_KMDB) && (!defined(_BOOT) || defined(__sparc))
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate #define	ADDCHAR(c)	if (bufp++ - buf < buflen) bufp[-1] = (c)
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate /*
607c478bd9Sstevel@tonic-gate  * Given a buffer 'buf' of size 'buflen', render as much of the string
617c478bd9Sstevel@tonic-gate  * described by <fmt, args> as possible.  The string will always be
627c478bd9Sstevel@tonic-gate  * null-terminated, so the maximum string length is 'buflen - 1'.
637c478bd9Sstevel@tonic-gate  * Returns the number of bytes that would be necessary to render the
647c478bd9Sstevel@tonic-gate  * entire string, not including null terminator (just like vsnprintf(3S)).
657c478bd9Sstevel@tonic-gate  * To determine buffer size in advance, use vsnprintf(NULL, 0, fmt, args) + 1.
667c478bd9Sstevel@tonic-gate  *
677c478bd9Sstevel@tonic-gate  * There is no support for floating point, and the C locale is assumed.
687c478bd9Sstevel@tonic-gate  */
697c478bd9Sstevel@tonic-gate size_t
vsnprintf(char * buf,size_t buflen,const char * fmt,va_list aargs)707c478bd9Sstevel@tonic-gate vsnprintf(char *buf, size_t buflen, const char *fmt, va_list aargs)
717c478bd9Sstevel@tonic-gate {
727c478bd9Sstevel@tonic-gate 	uint64_t ul, tmp;
737c478bd9Sstevel@tonic-gate 	char *bufp = buf;	/* current buffer pointer */
7477889f88SYuri Pankov 	char c, pad;
7577889f88SYuri Pankov 	int width, base, sign, num;
76186507a7Smyers 	int prec, h_count, l_count, dot_count;
77186507a7Smyers 	int pad_count, transfer_count, left_align;
787c478bd9Sstevel@tonic-gate 	char *digits, *sp, *bs;
797c478bd9Sstevel@tonic-gate 	char numbuf[65];	/* sufficient for a 64-bit binary value */
8077889f88SYuri Pankov 	int numwidth;
817c478bd9Sstevel@tonic-gate 	va_list args;
827c478bd9Sstevel@tonic-gate 
83*584b574aSToomas Soome 	ul = 0;
84*584b574aSToomas Soome 	bs = NULL;
857c478bd9Sstevel@tonic-gate 	/*
867c478bd9Sstevel@tonic-gate 	 * Make a copy so that all our callers don't have to make a copy
877c478bd9Sstevel@tonic-gate 	 */
887c478bd9Sstevel@tonic-gate 	va_copy(args, aargs);
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	if ((ssize_t)buflen < 0)
917c478bd9Sstevel@tonic-gate 		buflen = 0;
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	while ((c = *fmt++) != '\0') {
947c478bd9Sstevel@tonic-gate 		if (c != '%') {
957c478bd9Sstevel@tonic-gate 			ADDCHAR(c);
967c478bd9Sstevel@tonic-gate 			continue;
977c478bd9Sstevel@tonic-gate 		}
987c478bd9Sstevel@tonic-gate 
9977889f88SYuri Pankov 		width = prec = numwidth = 0;
100186507a7Smyers 		left_align = base = sign = 0;
101186507a7Smyers 		h_count = l_count = dot_count = 0;
102186507a7Smyers 		pad = ' ';
103186507a7Smyers 		digits = "0123456789abcdef";
104186507a7Smyers next_fmt:
1057c478bd9Sstevel@tonic-gate 		if ((c = *fmt++) == '\0')
1067c478bd9Sstevel@tonic-gate 			break;
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate 		if (c >= 'A' && c <= 'Z') {
1097c478bd9Sstevel@tonic-gate 			c += 'a' - 'A';
1107c478bd9Sstevel@tonic-gate 			digits = "0123456789ABCDEF";
1117c478bd9Sstevel@tonic-gate 		}
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 		switch (c) {
114186507a7Smyers 		case '-':
115186507a7Smyers 			left_align++;
116186507a7Smyers 			goto next_fmt;
117186507a7Smyers 		case '0':
118186507a7Smyers 			if (dot_count == 0)
119186507a7Smyers 				pad = '0';
120186507a7Smyers 			/*FALLTHROUGH*/
121186507a7Smyers 		case '1':
122186507a7Smyers 		case '2':
123186507a7Smyers 		case '3':
124186507a7Smyers 		case '4':
125186507a7Smyers 		case '5':
126186507a7Smyers 		case '6':
127186507a7Smyers 		case '7':
128186507a7Smyers 		case '8':
129186507a7Smyers 		case '9':
130186507a7Smyers 			num = 0;
131186507a7Smyers 			for (;;) {
132186507a7Smyers 				num = 10 * num + c - '0';
133186507a7Smyers 				c = *fmt;
134186507a7Smyers 				if (c < '0' || c > '9')
135186507a7Smyers 					break;
136186507a7Smyers 				else
137186507a7Smyers 					fmt++;
138186507a7Smyers 			}
139186507a7Smyers 			if (dot_count > 0)
140186507a7Smyers 				prec = num;
141186507a7Smyers 			else
142186507a7Smyers 				width = num;
143186507a7Smyers 
144186507a7Smyers 			goto next_fmt;
145186507a7Smyers 		case '.':
146186507a7Smyers 			dot_count++;
147186507a7Smyers 			goto next_fmt;
148186507a7Smyers 		case '*':
149049fa28aSdr 			if (dot_count > 0)
150049fa28aSdr 				prec = (int)va_arg(args, int);
151049fa28aSdr 			else
152049fa28aSdr 				width = (int)va_arg(args, int);
153186507a7Smyers 			goto next_fmt;
154186507a7Smyers 		case 'l':
155186507a7Smyers 			l_count++;
156186507a7Smyers 			goto next_fmt;
157186507a7Smyers 		case 'h':
158186507a7Smyers 			h_count++;
159186507a7Smyers 			goto next_fmt;
1607c478bd9Sstevel@tonic-gate 		case 'd':
1617c478bd9Sstevel@tonic-gate 			sign = 1;
1627c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
1637c478bd9Sstevel@tonic-gate 		case 'u':
1647c478bd9Sstevel@tonic-gate 			base = 10;
1657c478bd9Sstevel@tonic-gate 			break;
1667c478bd9Sstevel@tonic-gate 		case 'p':
167186507a7Smyers 			l_count = 1;
1687c478bd9Sstevel@tonic-gate 			/*FALLTHROUGH*/
1697c478bd9Sstevel@tonic-gate 		case 'x':
1707c478bd9Sstevel@tonic-gate 			base = 16;
1717c478bd9Sstevel@tonic-gate 			break;
1727c478bd9Sstevel@tonic-gate 		case 'o':
1737c478bd9Sstevel@tonic-gate 			base = 8;
1747c478bd9Sstevel@tonic-gate 			break;
1757c478bd9Sstevel@tonic-gate 		case 'b':
176186507a7Smyers 			l_count = 0;
1777c478bd9Sstevel@tonic-gate 			base = 1;
1787c478bd9Sstevel@tonic-gate 			break;
1797c478bd9Sstevel@tonic-gate 		case 'c':
180fc77c268Smyers 			c = (char)va_arg(args, int);
181186507a7Smyers 			ADDCHAR(c);
1827c478bd9Sstevel@tonic-gate 			break;
1837c478bd9Sstevel@tonic-gate 		case 's':
1847c478bd9Sstevel@tonic-gate 			sp = va_arg(args, char *);
185186507a7Smyers 			if (sp == NULL) {
1867c478bd9Sstevel@tonic-gate 				sp = "<null string>";
187186507a7Smyers 				/* avoid truncation */
188186507a7Smyers 				prec = strlen(sp);
189186507a7Smyers 			}
190186507a7Smyers 			/*
191186507a7Smyers 			 * Handle simple case specially to avoid
192186507a7Smyers 			 * performance hit of strlen()
193186507a7Smyers 			 */
194186507a7Smyers 			if (prec == 0 && width == 0) {
195186507a7Smyers 				while ((c = *sp++) != 0)
196186507a7Smyers 					ADDCHAR(c);
197186507a7Smyers 				break;
198186507a7Smyers 			}
199186507a7Smyers 			if (prec > 0) {
2007637daddSmyers 				transfer_count = strnlen(sp, prec);
201186507a7Smyers 				/* widen field if too narrow */
202186507a7Smyers 				if (prec > width)
203186507a7Smyers 					width = prec;
2047637daddSmyers 			} else
2057637daddSmyers 				transfer_count = strlen(sp);
206186507a7Smyers 			if (width > transfer_count)
207186507a7Smyers 				pad_count = width - transfer_count;
208186507a7Smyers 			else
209186507a7Smyers 				pad_count = 0;
210186507a7Smyers 			while ((!left_align) && (pad_count-- > 0))
211186507a7Smyers 				ADDCHAR(' ');
212186507a7Smyers 			/* ADDCHAR() evaluates arg at most once */
213186507a7Smyers 			while (transfer_count-- > 0)
214186507a7Smyers 				ADDCHAR(*sp++);
215186507a7Smyers 			while ((left_align) && (pad_count-- > 0))
216186507a7Smyers 				ADDCHAR(' ');
2177c478bd9Sstevel@tonic-gate 			break;
2187c478bd9Sstevel@tonic-gate 		case '%':
2197c478bd9Sstevel@tonic-gate 			ADDCHAR('%');
2207c478bd9Sstevel@tonic-gate 			break;
2217c478bd9Sstevel@tonic-gate 		}
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate 		if (base == 0)
2247c478bd9Sstevel@tonic-gate 			continue;
2257c478bd9Sstevel@tonic-gate 
226186507a7Smyers 		if (h_count == 0 && l_count == 0)
227186507a7Smyers 			if (sign)
228186507a7Smyers 				ul = (int64_t)va_arg(args, int);
229186507a7Smyers 			else
230186507a7Smyers 				ul = (int64_t)va_arg(args, unsigned int);
231186507a7Smyers 		else if (l_count > 1)
232186507a7Smyers 			if (sign)
233186507a7Smyers 				ul = (int64_t)va_arg(args, int64_t);
234186507a7Smyers 			else
235186507a7Smyers 				ul = (int64_t)va_arg(args, uint64_t);
236186507a7Smyers 		else if (l_count > 0)
237186507a7Smyers 			if (sign)
238186507a7Smyers 				ul = (int64_t)va_arg(args, long);
239186507a7Smyers 			else
240186507a7Smyers 				ul = (int64_t)va_arg(args, unsigned long);
241186507a7Smyers 		else if (h_count > 1)
242186507a7Smyers 			if (sign)
243fc77c268Smyers 				ul = (int64_t)((char)va_arg(args, int));
244186507a7Smyers 			else
245fc77c268Smyers 				ul = (int64_t)((unsigned char)va_arg(args,
246fc77c268Smyers 				    int));
247186507a7Smyers 		else if (h_count > 0)
248186507a7Smyers 			if (sign)
249fc77c268Smyers 				ul = (int64_t)((short)va_arg(args, int));
250186507a7Smyers 			else
251fc77c268Smyers 				ul = (int64_t)((unsigned short)va_arg(args,
252fc77c268Smyers 				    int));
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate 		if (sign && (int64_t)ul < 0)
2557c478bd9Sstevel@tonic-gate 			ul = -ul;
2567c478bd9Sstevel@tonic-gate 		else
2577c478bd9Sstevel@tonic-gate 			sign = 0;
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 		if (c == 'b') {
2607c478bd9Sstevel@tonic-gate 			bs = va_arg(args, char *);
2617c478bd9Sstevel@tonic-gate 			base = *bs++;
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 
26477889f88SYuri Pankov 		/*
26577889f88SYuri Pankov 		 * Fill in the number string buffer and calculate the
26677889f88SYuri Pankov 		 * number string length.
26777889f88SYuri Pankov 		 */
26877889f88SYuri Pankov 		tmp = ul;
26977889f88SYuri Pankov 		sp = numbuf;
27077889f88SYuri Pankov 		do {
27177889f88SYuri Pankov 			*sp++ = digits[tmp % base];
27277889f88SYuri Pankov 			numwidth++;
27377889f88SYuri Pankov 		} while ((tmp /= base) != 0);
27477889f88SYuri Pankov 
27577889f88SYuri Pankov 		/*
27677889f88SYuri Pankov 		 * Reduce the total field width by precision or the number
27777889f88SYuri Pankov 		 * string length depending on which one is bigger, and sign.
27877889f88SYuri Pankov 		 */
27977889f88SYuri Pankov 		if (prec >= numwidth)
28077889f88SYuri Pankov 			width -= prec;
28177889f88SYuri Pankov 		else
28277889f88SYuri Pankov 			width -= numwidth;
28377889f88SYuri Pankov 		width -= sign;
2847c478bd9Sstevel@tonic-gate 
28577889f88SYuri Pankov 		/* Add the sign if width is '0'-padded */
2867c478bd9Sstevel@tonic-gate 		if (sign && pad == '0')
2877c478bd9Sstevel@tonic-gate 			ADDCHAR('-');
28877889f88SYuri Pankov 
28977889f88SYuri Pankov 		/* If not left-aligned, add the width padding */
29077889f88SYuri Pankov 		if (!left_align) {
29177889f88SYuri Pankov 			while (width-- > 0)
29277889f88SYuri Pankov 				ADDCHAR(pad);
29377889f88SYuri Pankov 		}
29477889f88SYuri Pankov 
29577889f88SYuri Pankov 		/* Add the sign if width is NOT '0'-padded */
29677889f88SYuri Pankov 		if (sign && pad != '0')
2977c478bd9Sstevel@tonic-gate 			ADDCHAR('-');
2987c478bd9Sstevel@tonic-gate 
29977889f88SYuri Pankov 		/* Add the precision '0'-padding */
30077889f88SYuri Pankov 		while (prec-- > numwidth)
30177889f88SYuri Pankov 			ADDCHAR('0');
3027c478bd9Sstevel@tonic-gate 
30377889f88SYuri Pankov 		/* Print out the number */
3047c478bd9Sstevel@tonic-gate 		while (sp > numbuf) {
3057c478bd9Sstevel@tonic-gate 			sp--;
3067c478bd9Sstevel@tonic-gate 			ADDCHAR(*sp);
3077c478bd9Sstevel@tonic-gate 		}
3087c478bd9Sstevel@tonic-gate 
30977889f88SYuri Pankov 		/* Add left-alignment padding */
31077889f88SYuri Pankov 		while (width-- > 0)
311237b1f31SPatrick Mooney 			ADDCHAR(' ');
312237b1f31SPatrick Mooney 
3137c478bd9Sstevel@tonic-gate 		if (c == 'b' && ul != 0) {
3147c478bd9Sstevel@tonic-gate 			int any = 0;
3157c478bd9Sstevel@tonic-gate 			c = *bs++;
3167c478bd9Sstevel@tonic-gate 			while (c != 0) {
3177c478bd9Sstevel@tonic-gate 				if (ul & (1 << (c - 1))) {
3187c478bd9Sstevel@tonic-gate 					if (any++ == 0)
3197c478bd9Sstevel@tonic-gate 						ADDCHAR('<');
3207c478bd9Sstevel@tonic-gate 					while ((c = *bs++) >= 32)
3217c478bd9Sstevel@tonic-gate 						ADDCHAR(c);
3227c478bd9Sstevel@tonic-gate 					ADDCHAR(',');
3237c478bd9Sstevel@tonic-gate 				} else {
3247c478bd9Sstevel@tonic-gate 					while ((c = *bs++) >= 32)
3257c478bd9Sstevel@tonic-gate 						continue;
3267c478bd9Sstevel@tonic-gate 				}
3277c478bd9Sstevel@tonic-gate 			}
3287c478bd9Sstevel@tonic-gate 			if (any) {
3297c478bd9Sstevel@tonic-gate 				bufp--;
3307c478bd9Sstevel@tonic-gate 				ADDCHAR('>');
3317c478bd9Sstevel@tonic-gate 			}
3327c478bd9Sstevel@tonic-gate 		}
3337c478bd9Sstevel@tonic-gate 	}
3347c478bd9Sstevel@tonic-gate 	if (bufp - buf < buflen)
3357c478bd9Sstevel@tonic-gate 		bufp[0] = c;
3367c478bd9Sstevel@tonic-gate 	else if (buflen != 0)
3377c478bd9Sstevel@tonic-gate 		buf[buflen - 1] = c;
3387c478bd9Sstevel@tonic-gate 
3397c478bd9Sstevel@tonic-gate 	va_end(args);
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	return (bufp - buf);
3427c478bd9Sstevel@tonic-gate }
3437c478bd9Sstevel@tonic-gate 
3443c5da275SMatthew Ahrens /*PRINTFLIKE3*/
3457c478bd9Sstevel@tonic-gate size_t
snprintf(char * buf,size_t buflen,const char * fmt,...)3467c478bd9Sstevel@tonic-gate snprintf(char *buf, size_t buflen, const char *fmt, ...)
3477c478bd9Sstevel@tonic-gate {
3487c478bd9Sstevel@tonic-gate 	va_list args;
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate 	va_start(args, fmt);
3517c478bd9Sstevel@tonic-gate 	buflen = vsnprintf(buf, buflen, fmt, args);
3527c478bd9Sstevel@tonic-gate 	va_end(args);
3537c478bd9Sstevel@tonic-gate 
3547c478bd9Sstevel@tonic-gate 	return (buflen);
3557c478bd9Sstevel@tonic-gate }
3567c478bd9Sstevel@tonic-gate 
357