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