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