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