xref: /optee_os/lib/libutils/isoc/snprintf.c (revision b01047730e77127c23a36591643eeb8bb0487d68)
1*b0104773SPascal Brand /*
2*b0104773SPascal Brand  * Imported from NetBSD 5.1 with modifications to make it a vsnprintf(3)
3*b0104773SPascal Brand  * function
4*b0104773SPascal Brand  */
5*b0104773SPascal Brand 
6*b0104773SPascal Brand /*    $NetBSD: subr_prf.c,v 1.124.4.1 2009/02/02 19:47:47 snj Exp $    */
7*b0104773SPascal Brand 
8*b0104773SPascal Brand /*-
9*b0104773SPascal Brand  * Copyright (c) 1986, 1988, 1991, 1993
10*b0104773SPascal Brand  *    The Regents of the University of California.  All rights reserved.
11*b0104773SPascal Brand  * (c) UNIX System Laboratories, Inc.
12*b0104773SPascal Brand  * All or some portions of this file are derived from material licensed
13*b0104773SPascal Brand  * to the University of California by American Telephone and Telegraph
14*b0104773SPascal Brand  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
15*b0104773SPascal Brand  * the permission of UNIX System Laboratories, Inc.
16*b0104773SPascal Brand  *
17*b0104773SPascal Brand  * Redistribution and use in source and binary forms, with or without
18*b0104773SPascal Brand  * modification, are permitted provided that the following conditions
19*b0104773SPascal Brand  * are met:
20*b0104773SPascal Brand  * 1. Redistributions of source code must retain the above copyright
21*b0104773SPascal Brand  *    notice, this list of conditions and the following disclaimer.
22*b0104773SPascal Brand  * 2. Redistributions in binary form must reproduce the above copyright
23*b0104773SPascal Brand  *    notice, this list of conditions and the following disclaimer in the
24*b0104773SPascal Brand  *    documentation and/or other materials provided with the distribution.
25*b0104773SPascal Brand  * 3. Neither the name of the University nor the names of its contributors
26*b0104773SPascal Brand  *    may be used to endorse or promote products derived from this software
27*b0104773SPascal Brand  *    without specific prior written permission.
28*b0104773SPascal Brand  *
29*b0104773SPascal Brand  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30*b0104773SPascal Brand  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31*b0104773SPascal Brand  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32*b0104773SPascal Brand  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33*b0104773SPascal Brand  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34*b0104773SPascal Brand  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35*b0104773SPascal Brand  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36*b0104773SPascal Brand  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37*b0104773SPascal Brand  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38*b0104773SPascal Brand  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39*b0104773SPascal Brand  * SUCH DAMAGE.
40*b0104773SPascal Brand  *
41*b0104773SPascal Brand  *    @(#)subr_prf.c    8.4 (Berkeley) 5/4/95
42*b0104773SPascal Brand  */
43*b0104773SPascal Brand 
44*b0104773SPascal Brand #include <unistd.h>
45*b0104773SPascal Brand #include <stdint.h>
46*b0104773SPascal Brand #include <string.h>
47*b0104773SPascal Brand #include <stdio.h>
48*b0104773SPascal Brand #include <stdarg.h>
49*b0104773SPascal Brand 
50*b0104773SPascal Brand /* flags for kprintf */
51*b0104773SPascal Brand #define TOCONS        0x0001	/* to the console */
52*b0104773SPascal Brand #define TOTTY        0x0002	/* to the process' tty */
53*b0104773SPascal Brand #define TOLOG        0x0004	/* to the kernel message buffer */
54*b0104773SPascal Brand #define TOBUFONLY    0x0008	/* to the buffer (only) [for snprintf] */
55*b0104773SPascal Brand #define TODDB        0x0010	/* to ddb console */
56*b0104773SPascal Brand #define NOLOCK        0x1000	/* don't acquire a tty lock */
57*b0104773SPascal Brand 
58*b0104773SPascal Brand /* max size buffer kprintf needs to print quad_t [size in base 8 + \0] */
59*b0104773SPascal Brand #define KPRINTF_BUFSIZE        23	/*(8 * 8 / 3 + 2) */
60*b0104773SPascal Brand 
61*b0104773SPascal Brand /*
62*b0104773SPascal Brand  * The following macro is used to remove const cast-away warnings
63*b0104773SPascal Brand  * from gcc -Wcast-qual; it should be used with caution because it
64*b0104773SPascal Brand  * can hide valid errors; in particular most valid uses are in
65*b0104773SPascal Brand  * situations where the API requires it, not to cast away string
66*b0104773SPascal Brand  * constants. We don't use *intptr_t on purpose here and we are
67*b0104773SPascal Brand  * explicit about unsigned long so that we don't have additional
68*b0104773SPascal Brand  * dependencies.
69*b0104773SPascal Brand  */
70*b0104773SPascal Brand #define __UNCONST(a)    ((void *)(unsigned long)(const void *)(a))
71*b0104773SPascal Brand 
72*b0104773SPascal Brand #define putchar(c, flags, tty) \
73*b0104773SPascal Brand 	do { (void)(c); (void)(flags); (void)(tty); } while(0)
74*b0104773SPascal Brand 
75*b0104773SPascal Brand static int kprintf(const char *fmt0, int oflags, void *vp, char *sbuf,
76*b0104773SPascal Brand 		   va_list ap);
77*b0104773SPascal Brand 
78*b0104773SPascal Brand static const char hexdigits[] = "0123456789abcdef";
79*b0104773SPascal Brand static const char HEXDIGITS[] = "0123456789ABCDEF";
80*b0104773SPascal Brand 
81*b0104773SPascal Brand /*
82*b0104773SPascal Brand  * snprintf: print a message to a buffer
83*b0104773SPascal Brand  */
84*b0104773SPascal Brand int snprintf(char *bf, size_t size, const char *fmt, ...)
85*b0104773SPascal Brand {
86*b0104773SPascal Brand 	int retval;
87*b0104773SPascal Brand 	va_list ap;
88*b0104773SPascal Brand 	char *p;
89*b0104773SPascal Brand 
90*b0104773SPascal Brand 	if (size < 1)
91*b0104773SPascal Brand 		return -1;
92*b0104773SPascal Brand 	p = bf + size - 1;
93*b0104773SPascal Brand 	va_start(ap, fmt);
94*b0104773SPascal Brand 	retval = kprintf(fmt, TOBUFONLY, &p, bf, ap);
95*b0104773SPascal Brand 	va_end(ap);
96*b0104773SPascal Brand 	*(p) = 0; /* null terminate */
97*b0104773SPascal Brand 	return retval;
98*b0104773SPascal Brand }
99*b0104773SPascal Brand 
100*b0104773SPascal Brand /*
101*b0104773SPascal Brand  * vsnprintf: print a message to a buffer [already have va_alist]
102*b0104773SPascal Brand  */
103*b0104773SPascal Brand int vsnprintf(char *bf, size_t size, const char *fmt, va_list ap)
104*b0104773SPascal Brand {
105*b0104773SPascal Brand 	int retval;
106*b0104773SPascal Brand 	char *p;
107*b0104773SPascal Brand 
108*b0104773SPascal Brand 	if (size < 1)
109*b0104773SPascal Brand 		return -1;
110*b0104773SPascal Brand 	p = bf + size - 1;
111*b0104773SPascal Brand 	retval = kprintf(fmt, TOBUFONLY, &p, bf, ap);
112*b0104773SPascal Brand 	*(p) = 0; /* null terminate */
113*b0104773SPascal Brand 	return retval;
114*b0104773SPascal Brand }
115*b0104773SPascal Brand 
116*b0104773SPascal Brand /*
117*b0104773SPascal Brand  * kprintf: scaled down version of printf(3).
118*b0104773SPascal Brand  *
119*b0104773SPascal Brand  * this version based on vfprintf() from libc which was derived from
120*b0104773SPascal Brand  * software contributed to Berkeley by Chris Torek.
121*b0104773SPascal Brand  *
122*b0104773SPascal Brand  */
123*b0104773SPascal Brand 
124*b0104773SPascal Brand /*
125*b0104773SPascal Brand  * macros for converting digits to letters and vice versa
126*b0104773SPascal Brand  */
127*b0104773SPascal Brand #define    to_digit(c)    ((c) - '0')
128*b0104773SPascal Brand #define is_digit(c)    ((unsigned)to_digit(c) <= 9)
129*b0104773SPascal Brand #define    to_char(n)    ((n) + '0')
130*b0104773SPascal Brand 
131*b0104773SPascal Brand /*
132*b0104773SPascal Brand  * flags used during conversion.
133*b0104773SPascal Brand  */
134*b0104773SPascal Brand #define    ALT        0x001	/* alternate form */
135*b0104773SPascal Brand #define    HEXPREFIX    0x002	/* add 0x or 0X prefix */
136*b0104773SPascal Brand #define    LADJUST        0x004	/* left adjustment */
137*b0104773SPascal Brand #define    LONGDBL        0x008	/* long double; unimplemented */
138*b0104773SPascal Brand #define    LONGINT        0x010	/* long integer */
139*b0104773SPascal Brand #define    SHORTINT    0x040	/* short integer */
140*b0104773SPascal Brand #define    MAXINT        0x080	/* intmax_t */
141*b0104773SPascal Brand #define    PTRINT        0x100	/* intptr_t */
142*b0104773SPascal Brand #define    SIZEINT        0x200	/* size_t */
143*b0104773SPascal Brand #define    ZEROPAD        0x400	/* zero (as opposed to blank) pad */
144*b0104773SPascal Brand #define FPT        0x800	/* Floating point number */
145*b0104773SPascal Brand 
146*b0104773SPascal Brand     /*
147*b0104773SPascal Brand      * To extend shorts properly, we need both signed and unsigned
148*b0104773SPascal Brand      * argument extraction methods.
149*b0104773SPascal Brand      */
150*b0104773SPascal Brand #define    SARG() \
151*b0104773SPascal Brand 	(flags&MAXINT ? va_arg(ap, intmax_t) : \
152*b0104773SPascal Brand 	 flags&PTRINT ? va_arg(ap, intptr_t) : \
153*b0104773SPascal Brand 	 flags&SIZEINT ? va_arg(ap, ssize_t) : /* XXX */ \
154*b0104773SPascal Brand 	 flags&LONGINT ? va_arg(ap, long) : \
155*b0104773SPascal Brand 	 flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
156*b0104773SPascal Brand 	 (long)va_arg(ap, int))
157*b0104773SPascal Brand #define    UARG() \
158*b0104773SPascal Brand 	(flags&MAXINT ? va_arg(ap, uintmax_t) : \
159*b0104773SPascal Brand 	 flags&PTRINT ? va_arg(ap, uintptr_t) : \
160*b0104773SPascal Brand 	 flags&SIZEINT ? va_arg(ap, size_t) : \
161*b0104773SPascal Brand 	 flags&LONGINT ? va_arg(ap, unsigned long) : \
162*b0104773SPascal Brand 	 flags&SHORTINT ? (unsigned long)(unsigned short)va_arg(ap, int) : \
163*b0104773SPascal Brand 	 (unsigned long)va_arg(ap, unsigned))
164*b0104773SPascal Brand 
165*b0104773SPascal Brand #define KPRINTF_PUTCHAR(C) { \
166*b0104773SPascal Brand 		if (oflags == TOBUFONLY) { \
167*b0104773SPascal Brand 			if ((vp != NULL) && (sbuf == tailp)) { \
168*b0104773SPascal Brand 				ret += 1; /* indicate error */ \
169*b0104773SPascal Brand 				goto overflow; \
170*b0104773SPascal Brand 			} \
171*b0104773SPascal Brand 			*sbuf++ = (C); \
172*b0104773SPascal Brand 		} else { \
173*b0104773SPascal Brand 			putchar((C), oflags, (struct tty *)vp); \
174*b0104773SPascal Brand 		} \
175*b0104773SPascal Brand 	}
176*b0104773SPascal Brand 
177*b0104773SPascal Brand /*
178*b0104773SPascal Brand  * Guts of kernel printf.  Note, we already expect to be in a mutex!
179*b0104773SPascal Brand  */
180*b0104773SPascal Brand static int
181*b0104773SPascal Brand kprintf(const char *fmt0, int oflags, void *vp, char *sbuf, va_list ap)
182*b0104773SPascal Brand {
183*b0104773SPascal Brand 	const char *fmt;	/* format string */
184*b0104773SPascal Brand 	int ch;			/* character from fmt */
185*b0104773SPascal Brand 	int n;			/* handy integer (short term usage) */
186*b0104773SPascal Brand 	char *cp;		/* handy char pointer (short term usage) */
187*b0104773SPascal Brand 	int flags;		/* flags as above */
188*b0104773SPascal Brand 	int ret;		/* return value accumulator */
189*b0104773SPascal Brand 	int width;		/* width from format (%8d), or 0 */
190*b0104773SPascal Brand 	int prec;		/* precision from format (%.3d), or -1 */
191*b0104773SPascal Brand 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
192*b0104773SPascal Brand 
193*b0104773SPascal Brand 	uint32_t _uquad;	/* integer arguments %[diouxX] */
194*b0104773SPascal Brand 	enum { OCT, DEC, HEX } base;	/* base for [diouxX] conversion */
195*b0104773SPascal Brand 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
196*b0104773SPascal Brand 	int realsz;		/* field size expanded by dprec */
197*b0104773SPascal Brand 	int size;		/* size of converted field or string */
198*b0104773SPascal Brand 	const char *xdigs;	/* digits for [xX] conversion */
199*b0104773SPascal Brand 	char bf[KPRINTF_BUFSIZE];	/* space for %c, %[diouxX] */
200*b0104773SPascal Brand 	char *tailp;		/* tail pointer for snprintf */
201*b0104773SPascal Brand 
202*b0104773SPascal Brand 	tailp = NULL;		/* XXX: shutup gcc */
203*b0104773SPascal Brand 	if (oflags == TOBUFONLY && (vp != NULL))
204*b0104773SPascal Brand 		tailp = *(char **)vp;
205*b0104773SPascal Brand 
206*b0104773SPascal Brand 	cp = NULL;		/* XXX: shutup gcc */
207*b0104773SPascal Brand 	size = 0;		/* XXX: shutup gcc */
208*b0104773SPascal Brand 
209*b0104773SPascal Brand 	fmt = fmt0;
210*b0104773SPascal Brand 	ret = 0;
211*b0104773SPascal Brand 
212*b0104773SPascal Brand 	xdigs = NULL;		/* XXX: shut up gcc warning */
213*b0104773SPascal Brand 
214*b0104773SPascal Brand 	/*
215*b0104773SPascal Brand 	 * Scan the format for conversions (`%' character).
216*b0104773SPascal Brand 	 */
217*b0104773SPascal Brand 	for (;;) {
218*b0104773SPascal Brand 		while (*fmt != '%' && *fmt) {
219*b0104773SPascal Brand 			ret++;
220*b0104773SPascal Brand 			KPRINTF_PUTCHAR(*fmt++);
221*b0104773SPascal Brand 		}
222*b0104773SPascal Brand 		if (*fmt == 0)
223*b0104773SPascal Brand 			goto done;
224*b0104773SPascal Brand 
225*b0104773SPascal Brand 		fmt++;		/* skip over '%' */
226*b0104773SPascal Brand 
227*b0104773SPascal Brand 		flags = 0;
228*b0104773SPascal Brand 		dprec = 0;
229*b0104773SPascal Brand 		width = 0;
230*b0104773SPascal Brand 		prec = -1;
231*b0104773SPascal Brand 		sign = '\0';
232*b0104773SPascal Brand 
233*b0104773SPascal Brand rflag:		ch = *fmt++;
234*b0104773SPascal Brand reswitch:	switch (ch) {
235*b0104773SPascal Brand 		case ' ':
236*b0104773SPascal Brand 			/*
237*b0104773SPascal Brand 			 * ``If the space and + flags both appear, the space
238*b0104773SPascal Brand 			 * flag will be ignored.''
239*b0104773SPascal Brand 			 *    -- ANSI X3J11
240*b0104773SPascal Brand 			 */
241*b0104773SPascal Brand 			if (!sign)
242*b0104773SPascal Brand 				sign = ' ';
243*b0104773SPascal Brand 			goto rflag;
244*b0104773SPascal Brand 		case '#':
245*b0104773SPascal Brand 			flags |= ALT;
246*b0104773SPascal Brand 			goto rflag;
247*b0104773SPascal Brand 		case '*':
248*b0104773SPascal Brand 			/*
249*b0104773SPascal Brand 			 * ``A negative field width argument is taken as a
250*b0104773SPascal Brand 			 * - flag followed by a positive field width.''
251*b0104773SPascal Brand 			 *    -- ANSI X3J11
252*b0104773SPascal Brand 			 * They don't exclude field widths read from args.
253*b0104773SPascal Brand 			 */
254*b0104773SPascal Brand 			if ((width = va_arg(ap, int)) >= 0)
255*b0104773SPascal Brand 				goto rflag;
256*b0104773SPascal Brand 			width = -width;
257*b0104773SPascal Brand 			/* FALLTHROUGH */
258*b0104773SPascal Brand 		case '-':
259*b0104773SPascal Brand 			flags |= LADJUST;
260*b0104773SPascal Brand 			goto rflag;
261*b0104773SPascal Brand 		case '+':
262*b0104773SPascal Brand 			sign = '+';
263*b0104773SPascal Brand 			goto rflag;
264*b0104773SPascal Brand 		case '.':
265*b0104773SPascal Brand 			if ((ch = *fmt++) == '*') {
266*b0104773SPascal Brand 				n = va_arg(ap, int);
267*b0104773SPascal Brand 				prec = n < 0 ? -1 : n;
268*b0104773SPascal Brand 				goto rflag;
269*b0104773SPascal Brand 			}
270*b0104773SPascal Brand 			n = 0;
271*b0104773SPascal Brand 			while (is_digit(ch)) {
272*b0104773SPascal Brand 				n = 10 * n + to_digit(ch);
273*b0104773SPascal Brand 				ch = *fmt++;
274*b0104773SPascal Brand 			}
275*b0104773SPascal Brand 			prec = n < 0 ? -1 : n;
276*b0104773SPascal Brand 			goto reswitch;
277*b0104773SPascal Brand 		case '0':
278*b0104773SPascal Brand 			/*
279*b0104773SPascal Brand 			 * ``Note that 0 is taken as a flag, not as the
280*b0104773SPascal Brand 			 * beginning of a field width.''
281*b0104773SPascal Brand 			 *    -- ANSI X3J11
282*b0104773SPascal Brand 			 */
283*b0104773SPascal Brand 			flags |= ZEROPAD;
284*b0104773SPascal Brand 			goto rflag;
285*b0104773SPascal Brand 		case '1':
286*b0104773SPascal Brand 		case '2':
287*b0104773SPascal Brand 		case '3':
288*b0104773SPascal Brand 		case '4':
289*b0104773SPascal Brand 		case '5':
290*b0104773SPascal Brand 		case '6':
291*b0104773SPascal Brand 		case '7':
292*b0104773SPascal Brand 		case '8':
293*b0104773SPascal Brand 		case '9':
294*b0104773SPascal Brand 			n = 0;
295*b0104773SPascal Brand 			do {
296*b0104773SPascal Brand 				n = 10 * n + to_digit(ch);
297*b0104773SPascal Brand 				ch = *fmt++;
298*b0104773SPascal Brand 			} while (is_digit(ch));
299*b0104773SPascal Brand 			width = n;
300*b0104773SPascal Brand 			goto reswitch;
301*b0104773SPascal Brand 		case 'h':
302*b0104773SPascal Brand 			flags |= SHORTINT;
303*b0104773SPascal Brand 			goto rflag;
304*b0104773SPascal Brand 		case 'j':
305*b0104773SPascal Brand 			flags |= MAXINT;
306*b0104773SPascal Brand 			goto rflag;
307*b0104773SPascal Brand 		case 'l':
308*b0104773SPascal Brand 			flags |= LONGINT;
309*b0104773SPascal Brand 			goto rflag;
310*b0104773SPascal Brand 		case 't':
311*b0104773SPascal Brand 			flags |= PTRINT;
312*b0104773SPascal Brand 			goto rflag;
313*b0104773SPascal Brand 		case 'z':
314*b0104773SPascal Brand 			flags |= SIZEINT;
315*b0104773SPascal Brand 			goto rflag;
316*b0104773SPascal Brand 		case 'c':
317*b0104773SPascal Brand 			*(cp = bf) = va_arg(ap, int);
318*b0104773SPascal Brand 			size = 1;
319*b0104773SPascal Brand 			sign = '\0';
320*b0104773SPascal Brand 			break;
321*b0104773SPascal Brand 		case 'D':
322*b0104773SPascal Brand 			flags |= LONGINT;
323*b0104773SPascal Brand 		 /* FALLTHROUGH */ case 'd':
324*b0104773SPascal Brand 		case 'i':
325*b0104773SPascal Brand 			_uquad = SARG();
326*b0104773SPascal Brand 			if ((int32_t) _uquad < 0) {
327*b0104773SPascal Brand 				_uquad = -_uquad;
328*b0104773SPascal Brand 				sign = '-';
329*b0104773SPascal Brand 			}
330*b0104773SPascal Brand 			base = DEC;
331*b0104773SPascal Brand 			goto number;
332*b0104773SPascal Brand 		case 'n':
333*b0104773SPascal Brand 			if (flags & MAXINT)
334*b0104773SPascal Brand 				*va_arg(ap, intmax_t *) = ret;
335*b0104773SPascal Brand 			else if (flags & PTRINT)
336*b0104773SPascal Brand 				*va_arg(ap, intptr_t *) = ret;
337*b0104773SPascal Brand 			else if (flags & SIZEINT)
338*b0104773SPascal Brand 				*va_arg(ap, ssize_t *) = ret;
339*b0104773SPascal Brand 			else if (flags & LONGINT)
340*b0104773SPascal Brand 				*va_arg(ap, long *) = ret;
341*b0104773SPascal Brand 			else if (flags & SHORTINT)
342*b0104773SPascal Brand 				*va_arg(ap, short *) = ret;
343*b0104773SPascal Brand 			else
344*b0104773SPascal Brand 				*va_arg(ap, int *) = ret;
345*b0104773SPascal Brand 			continue;	/* no output */
346*b0104773SPascal Brand 		case 'O':
347*b0104773SPascal Brand 			flags |= LONGINT;
348*b0104773SPascal Brand 		 /* FALLTHROUGH */ case 'o':
349*b0104773SPascal Brand 			_uquad = UARG();
350*b0104773SPascal Brand 			base = OCT;
351*b0104773SPascal Brand 			goto nosign;
352*b0104773SPascal Brand 		case 'p':
353*b0104773SPascal Brand 			/*
354*b0104773SPascal Brand 			 * ``The argument shall be a pointer to void.  The
355*b0104773SPascal Brand 			 * value of the pointer is converted to a sequence
356*b0104773SPascal Brand 			 * of printable characters, in an implementation-
357*b0104773SPascal Brand 			 * defined manner.''
358*b0104773SPascal Brand 			 *    -- ANSI X3J11
359*b0104773SPascal Brand 			 */
360*b0104773SPascal Brand 			/* NOSTRICT */
361*b0104773SPascal Brand 			_uquad = (unsigned long)va_arg(ap, void *);
362*b0104773SPascal Brand 			base = HEX;
363*b0104773SPascal Brand 			xdigs = hexdigits;
364*b0104773SPascal Brand 			flags |= HEXPREFIX;
365*b0104773SPascal Brand 			ch = 'x';
366*b0104773SPascal Brand 			goto nosign;
367*b0104773SPascal Brand 		case 's':
368*b0104773SPascal Brand 			if ((cp = va_arg(ap, char *)) == NULL)
369*b0104773SPascal Brand 				/* XXXUNCONST */ cp = __UNCONST("(null)");
370*b0104773SPascal Brand 			if (prec >= 0) {
371*b0104773SPascal Brand 				/*
372*b0104773SPascal Brand 				 * can't use strlen; can only look for the
373*b0104773SPascal Brand 				 * NUL in the first `prec' characters, and
374*b0104773SPascal Brand 				 * strlen() will go further.
375*b0104773SPascal Brand 				 */
376*b0104773SPascal Brand 				char *p = memchr(cp, 0, prec);
377*b0104773SPascal Brand 
378*b0104773SPascal Brand 				if (p != NULL) {
379*b0104773SPascal Brand 					size = p - cp;
380*b0104773SPascal Brand 					if (size > prec)
381*b0104773SPascal Brand 						size = prec;
382*b0104773SPascal Brand 				} else {
383*b0104773SPascal Brand 					size = prec;
384*b0104773SPascal Brand 				}
385*b0104773SPascal Brand 			} else {
386*b0104773SPascal Brand 				size = strlen(cp);
387*b0104773SPascal Brand 			}
388*b0104773SPascal Brand 			sign = '\0';
389*b0104773SPascal Brand 			break;
390*b0104773SPascal Brand 		case 'U':
391*b0104773SPascal Brand 			flags |= LONGINT;
392*b0104773SPascal Brand 		 /* FALLTHROUGH */ case 'u':
393*b0104773SPascal Brand 			_uquad = UARG();
394*b0104773SPascal Brand 			base = DEC;
395*b0104773SPascal Brand 			goto nosign;
396*b0104773SPascal Brand 		case 'X':
397*b0104773SPascal Brand 			xdigs = HEXDIGITS;
398*b0104773SPascal Brand 			goto hex;
399*b0104773SPascal Brand 		case 'x':
400*b0104773SPascal Brand 			xdigs = hexdigits;
401*b0104773SPascal Brand hex:			_uquad = UARG();
402*b0104773SPascal Brand 			base = HEX;
403*b0104773SPascal Brand 			/* leading 0x/X only if non-zero */
404*b0104773SPascal Brand 			if (flags & ALT && _uquad != 0)
405*b0104773SPascal Brand 				flags |= HEXPREFIX;
406*b0104773SPascal Brand 
407*b0104773SPascal Brand 			/* unsigned conversions */
408*b0104773SPascal Brand nosign:		sign = '\0';
409*b0104773SPascal Brand 			/*
410*b0104773SPascal Brand 			 * ``... diouXx conversions ... if a precision is
411*b0104773SPascal Brand 			 * specified, the 0 flag will be ignored.''
412*b0104773SPascal Brand 			 *    -- ANSI X3J11
413*b0104773SPascal Brand 			 */
414*b0104773SPascal Brand number:		if ((dprec = prec) >= 0)
415*b0104773SPascal Brand 				flags &= ~ZEROPAD;
416*b0104773SPascal Brand 
417*b0104773SPascal Brand 			/*
418*b0104773SPascal Brand 			 * ``The result of converting a zero value with an
419*b0104773SPascal Brand 			 * explicit precision of zero is no characters.''
420*b0104773SPascal Brand 			 *    -- ANSI X3J11
421*b0104773SPascal Brand 			 */
422*b0104773SPascal Brand 			cp = bf + KPRINTF_BUFSIZE;
423*b0104773SPascal Brand 			if (_uquad != 0 || prec != 0) {
424*b0104773SPascal Brand 				/*
425*b0104773SPascal Brand 				 * Unsigned mod is hard, and unsigned mod
426*b0104773SPascal Brand 				 * by a constant is easier than that by
427*b0104773SPascal Brand 				 * a variable; hence this switch.
428*b0104773SPascal Brand 				 */
429*b0104773SPascal Brand 				switch (base) {
430*b0104773SPascal Brand 				case OCT:
431*b0104773SPascal Brand 					do {
432*b0104773SPascal Brand 						*--cp = to_char(_uquad & 7);
433*b0104773SPascal Brand 						_uquad >>= 3;
434*b0104773SPascal Brand 					} while (_uquad);
435*b0104773SPascal Brand 					/* handle octal leading 0 */
436*b0104773SPascal Brand 					if (flags & ALT && *cp != '0')
437*b0104773SPascal Brand 						*--cp = '0';
438*b0104773SPascal Brand 					break;
439*b0104773SPascal Brand 
440*b0104773SPascal Brand 				case DEC:
441*b0104773SPascal Brand 					/* many numbers are 1 digit */
442*b0104773SPascal Brand 					while (_uquad >= 10) {
443*b0104773SPascal Brand 						*--cp = to_char(_uquad % 10);
444*b0104773SPascal Brand 						_uquad /= 10;
445*b0104773SPascal Brand 					}
446*b0104773SPascal Brand 					*--cp = to_char(_uquad);
447*b0104773SPascal Brand 					break;
448*b0104773SPascal Brand 
449*b0104773SPascal Brand 				case HEX:
450*b0104773SPascal Brand 					do {
451*b0104773SPascal Brand 						*--cp = xdigs[_uquad & 15];
452*b0104773SPascal Brand 						_uquad >>= 4;
453*b0104773SPascal Brand 					} while (_uquad);
454*b0104773SPascal Brand 					break;
455*b0104773SPascal Brand 
456*b0104773SPascal Brand 				default:
457*b0104773SPascal Brand 					 /* XXXUNCONST */
458*b0104773SPascal Brand 					    cp =
459*b0104773SPascal Brand 					    __UNCONST
460*b0104773SPascal Brand 					    ("bug in kprintf: bad base");
461*b0104773SPascal Brand 					size = strlen(cp);
462*b0104773SPascal Brand 					goto skipsize;
463*b0104773SPascal Brand 				}
464*b0104773SPascal Brand 			}
465*b0104773SPascal Brand 			size = bf + KPRINTF_BUFSIZE - cp;
466*b0104773SPascal Brand skipsize:
467*b0104773SPascal Brand 			break;
468*b0104773SPascal Brand 		default:	/* "%?" prints ?, unless ? is NUL */
469*b0104773SPascal Brand 			if (ch == '\0')
470*b0104773SPascal Brand 				goto done;
471*b0104773SPascal Brand 			/* pretend it was %c with argument ch */
472*b0104773SPascal Brand 			cp = bf;
473*b0104773SPascal Brand 			*cp = ch;
474*b0104773SPascal Brand 			size = 1;
475*b0104773SPascal Brand 			sign = '\0';
476*b0104773SPascal Brand 			break;
477*b0104773SPascal Brand 		}
478*b0104773SPascal Brand 
479*b0104773SPascal Brand 		/*
480*b0104773SPascal Brand 		 * All reasonable formats wind up here.  At this point, `cp'
481*b0104773SPascal Brand 		 * points to a string which (if not flags&LADJUST) should be
482*b0104773SPascal Brand 		 * padded out to `width' places.  If flags&ZEROPAD, it should
483*b0104773SPascal Brand 		 * first be prefixed by any sign or other prefix; otherwise,
484*b0104773SPascal Brand 		 * it should be blank padded before the prefix is emitted.
485*b0104773SPascal Brand 		 * After any left-hand padding and prefixing, emit zeroes
486*b0104773SPascal Brand 		 * required by a decimal [diouxX] precision, then print the
487*b0104773SPascal Brand 		 * string proper, then emit zeroes required by any leftover
488*b0104773SPascal Brand 		 * floating precision; finally, if LADJUST, pad with blanks.
489*b0104773SPascal Brand 		 *
490*b0104773SPascal Brand 		 * Compute actual size, so we know how much to pad.
491*b0104773SPascal Brand 		 * size excludes decimal prec; realsz includes it.
492*b0104773SPascal Brand 		 */
493*b0104773SPascal Brand 		realsz = dprec > size ? dprec : size;
494*b0104773SPascal Brand 		if (sign)
495*b0104773SPascal Brand 			realsz++;
496*b0104773SPascal Brand 		else if (flags & HEXPREFIX)
497*b0104773SPascal Brand 			realsz += 2;
498*b0104773SPascal Brand 
499*b0104773SPascal Brand 		/* adjust ret */
500*b0104773SPascal Brand 		ret += width > realsz ? width : realsz;
501*b0104773SPascal Brand 
502*b0104773SPascal Brand 		/* right-adjusting blank padding */
503*b0104773SPascal Brand 		if ((flags & (LADJUST | ZEROPAD)) == 0) {
504*b0104773SPascal Brand 			n = width - realsz;
505*b0104773SPascal Brand 			while (n-- > 0)
506*b0104773SPascal Brand 				KPRINTF_PUTCHAR(' ');
507*b0104773SPascal Brand 		}
508*b0104773SPascal Brand 
509*b0104773SPascal Brand 		/* prefix */
510*b0104773SPascal Brand 		if (sign) {
511*b0104773SPascal Brand 			KPRINTF_PUTCHAR(sign);
512*b0104773SPascal Brand 		} else if (flags & HEXPREFIX) {
513*b0104773SPascal Brand 			KPRINTF_PUTCHAR('0');
514*b0104773SPascal Brand 			KPRINTF_PUTCHAR(ch);
515*b0104773SPascal Brand 		}
516*b0104773SPascal Brand 
517*b0104773SPascal Brand 		/* right-adjusting zero padding */
518*b0104773SPascal Brand 		if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) {
519*b0104773SPascal Brand 			n = width - realsz;
520*b0104773SPascal Brand 			while (n-- > 0)
521*b0104773SPascal Brand 				KPRINTF_PUTCHAR('0');
522*b0104773SPascal Brand 		}
523*b0104773SPascal Brand 
524*b0104773SPascal Brand 		/* leading zeroes from decimal precision */
525*b0104773SPascal Brand 		n = dprec - size;
526*b0104773SPascal Brand 		while (n-- > 0)
527*b0104773SPascal Brand 			KPRINTF_PUTCHAR('0');
528*b0104773SPascal Brand 
529*b0104773SPascal Brand 		/* the string or number proper */
530*b0104773SPascal Brand 		while (size--)
531*b0104773SPascal Brand 			KPRINTF_PUTCHAR(*cp++);
532*b0104773SPascal Brand 		/* left-adjusting padding (always blank) */
533*b0104773SPascal Brand 		if (flags & LADJUST) {
534*b0104773SPascal Brand 			n = width - realsz;
535*b0104773SPascal Brand 			while (n-- > 0)
536*b0104773SPascal Brand 				KPRINTF_PUTCHAR(' ');
537*b0104773SPascal Brand 		}
538*b0104773SPascal Brand 	}
539*b0104773SPascal Brand 
540*b0104773SPascal Brand done:
541*b0104773SPascal Brand 	if ((oflags == TOBUFONLY) && (vp != NULL))
542*b0104773SPascal Brand 		*(char **)vp = sbuf;
543*b0104773SPascal Brand overflow:
544*b0104773SPascal Brand 	return ret;
545*b0104773SPascal Brand }
546