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