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