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