1 /*
2 * Copyright (c) 1986, 1988, 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 * (c) UNIX System Laboratories, Inc.
5 * All or some portions of this file are derived from material licensed
6 * to the University of California by American Telephone and Telegraph
7 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8 * the permission of UNIX System Laboratories, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94
35 */
36
37 #include <sys/cdefs.h>
38
39 /*
40 * Standaloneified version of the FreeBSD kernel printf family.
41 */
42
43 #include <sys/types.h>
44 #include <sys/stddef.h>
45 #include <sys/stdint.h>
46 #include <limits.h>
47 #include <string.h>
48 #include "stand.h"
49
50 /*
51 * Note that stdarg.h and the ANSI style va_start macro is used for both
52 * ANSI and traditional C compilers.
53 */
54 #include <machine/stdarg.h>
55
56 #define MAXNBUF (sizeof (intmax_t) * CHAR_BIT + 1)
57
58 typedef void (kvprintf_fn_t)(int, void *);
59
60 static char *ksprintn(char *, uintmax_t, int, int *, int);
61 static int kvprintf(char const *, kvprintf_fn_t *, void *, int, va_list);
62
63 static void
putchar_wrapper(int cc,void * arg __unused)64 putchar_wrapper(int cc, void *arg __unused)
65 {
66
67 putchar(cc);
68 }
69
70 int
printf(const char * fmt,...)71 printf(const char *fmt, ...)
72 {
73 va_list ap;
74 int retval;
75
76 va_start(ap, fmt);
77 retval = kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
78 va_end(ap);
79 return (retval);
80 }
81
82 void
vprintf(const char * fmt,va_list ap)83 vprintf(const char *fmt, va_list ap)
84 {
85
86 kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
87 }
88
89 int
sprintf(char * buf,const char * cfmt,...)90 sprintf(char *buf, const char *cfmt, ...)
91 {
92 int retval;
93 va_list ap;
94
95 va_start(ap, cfmt);
96 retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
97 buf[retval] = '\0';
98 va_end(ap);
99 return (retval);
100 }
101
102 struct print_buf {
103 char *buf;
104 size_t size;
105 };
106
107 static void
snprint_func(int ch,void * arg)108 snprint_func(int ch, void *arg)
109 {
110 struct print_buf *pbuf = arg;
111
112 if (pbuf->size < 2) {
113 /*
114 * Reserve last buffer position for the terminating
115 * character:
116 */
117 return;
118 }
119 *(pbuf->buf)++ = ch;
120 pbuf->size--;
121 }
122
123 int
asprintf(char ** buf,const char * cfmt,...)124 asprintf(char **buf, const char *cfmt, ...)
125 {
126 int retval;
127 struct print_buf arg;
128 va_list ap;
129
130 *buf = NULL;
131 va_start(ap, cfmt);
132 retval = kvprintf(cfmt, NULL, NULL, 10, ap);
133 va_end(ap);
134 if (retval <= 0)
135 return (-1);
136
137 arg.size = retval + 1;
138 arg.buf = *buf = malloc(arg.size);
139 if (*buf == NULL)
140 return (-1);
141
142 va_start(ap, cfmt);
143 retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
144 va_end(ap);
145
146 if (arg.size >= 1)
147 *(arg.buf)++ = 0;
148 return (retval);
149 }
150
151 int
snprintf(char * buf,size_t size,const char * cfmt,...)152 snprintf(char *buf, size_t size, const char *cfmt, ...)
153 {
154 int retval;
155 va_list ap;
156 struct print_buf arg;
157
158 arg.buf = buf;
159 arg.size = size;
160
161 va_start(ap, cfmt);
162 retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
163 va_end(ap);
164
165 if (arg.size >= 1)
166 *(arg.buf)++ = 0;
167 return (retval);
168 }
169
170 void
vsprintf(char * buf,const char * cfmt,va_list ap)171 vsprintf(char *buf, const char *cfmt, va_list ap)
172 {
173 int retval;
174
175 retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
176 buf[retval] = '\0';
177 }
178
179 void
vsnprintf(char * buf,size_t size,const char * cfmt,va_list ap)180 vsnprintf(char *buf, size_t size, const char *cfmt, va_list ap)
181 {
182 int retval;
183 struct print_buf arg;
184
185 arg.buf = buf;
186 arg.size = size;
187
188 retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
189 buf[retval] = '\0';
190 }
191
192 /*
193 * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
194 * order; return an optional length and a pointer to the last character
195 * written in the buffer (i.e., the first character of the string).
196 * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
197 */
198 static char *
ksprintn(char * nbuf,uintmax_t num,int base,int * lenp,int upper)199 ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
200 {
201 char *p, c;
202
203 p = nbuf;
204 *p = '\0';
205 do {
206 c = hex2ascii(num % base);
207 *++p = upper ? toupper(c) : c;
208 } while (num /= base);
209 if (lenp)
210 *lenp = p - nbuf;
211 return (p);
212 }
213
214 /*
215 * Scaled down version of printf(3).
216 *
217 * Two additional formats:
218 *
219 * The format %b is supported to decode error registers.
220 * Its usage is:
221 *
222 * printf("reg=%b\n", regval, "<base><arg>*");
223 *
224 * where <base> is the output base expressed as a control character, e.g.
225 * \10 gives octal; \20 gives hex. Each arg is a sequence of characters,
226 * the first of which gives the bit number to be inspected (origin 1), and
227 * the next characters (up to a control character, i.e. a character <= 32),
228 * give the name of the register. Thus:
229 *
230 * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
231 *
232 * would produce output:
233 *
234 * reg=3<BITTWO,BITONE>
235 *
236 * XXX: %D -- Hexdump, takes pointer and separator string:
237 * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX
238 * ("%*D", len, ptr, " " -> XX XX XX XX ...
239 */
240 static int
kvprintf(char const * fmt,kvprintf_fn_t * func,void * arg,int radix,va_list ap)241 kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap)
242 {
243 #define PCHAR(c) { \
244 int cc = (c); \
245 \
246 if (func) { \
247 (*func)(cc, arg); \
248 } else if (d != NULL) { \
249 *d++ = cc; \
250 } \
251 retval++; \
252 }
253
254 char nbuf[MAXNBUF];
255 char *d;
256 const char *p, *percent, *q;
257 uint16_t *S;
258 uchar_t *up;
259 int ch, n;
260 uintmax_t num;
261 int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
262 int cflag, hflag, jflag, tflag, zflag;
263 int dwidth, upper;
264 char padc;
265 int stop = 0, retval = 0;
266
267 num = 0;
268 if (!func)
269 d = (char *)arg;
270 else
271 d = NULL;
272
273 if (fmt == NULL)
274 fmt = "(fmt null)\n";
275
276 if (radix < 2 || radix > 36)
277 radix = 10;
278
279 for (;;) {
280 padc = ' ';
281 width = 0;
282 while ((ch = (uchar_t)*fmt++) != '%' || stop) {
283 if (ch == '\0')
284 return (retval);
285 PCHAR(ch);
286 }
287 percent = fmt - 1;
288 qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
289 sign = 0; dot = 0; dwidth = 0; upper = 0;
290 cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
291 reswitch: switch (ch = (uchar_t)*fmt++) {
292 case '.':
293 dot = 1;
294 goto reswitch;
295 case '#':
296 sharpflag = 1;
297 goto reswitch;
298 case '+':
299 sign = 1;
300 goto reswitch;
301 case '-':
302 ladjust = 1;
303 goto reswitch;
304 case '%':
305 PCHAR(ch);
306 break;
307 case '*':
308 if (!dot) {
309 width = va_arg(ap, int);
310 if (width < 0) {
311 ladjust = !ladjust;
312 width = -width;
313 }
314 } else {
315 dwidth = va_arg(ap, int);
316 }
317 goto reswitch;
318 case '0':
319 if (!dot) {
320 padc = '0';
321 goto reswitch;
322 }
323 /* FALLTHROUGH */
324 case '1': case '2': case '3': case '4':
325 case '5': case '6': case '7': case '8': case '9':
326 for (n = 0; ; ++fmt) {
327 n = n * 10 + ch - '0';
328 ch = *fmt;
329 if (ch < '0' || ch > '9')
330 break;
331 }
332 if (dot)
333 dwidth = n;
334 else
335 width = n;
336 goto reswitch;
337 case 'b':
338 num = (uint_t)va_arg(ap, int);
339 p = va_arg(ap, char *);
340 for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q; )
341 PCHAR(*q--);
342
343 if (num == 0)
344 break;
345
346 for (tmp = 0; *p; ) {
347 n = *p++;
348 if (num & (1 << (n - 1))) {
349 PCHAR(tmp ? ',' : '<');
350 for (; (n = *p) > ' '; ++p)
351 PCHAR(n);
352 tmp = 1;
353 } else
354 for (; *p > ' '; ++p)
355 continue;
356 }
357 if (tmp)
358 PCHAR('>');
359 break;
360 case 'c':
361 PCHAR(va_arg(ap, int));
362 break;
363 case 'D':
364 up = va_arg(ap, uchar_t *);
365 p = va_arg(ap, char *);
366 if (!width)
367 width = 16;
368 while (width--) {
369 PCHAR(hex2ascii(*up >> 4));
370 PCHAR(hex2ascii(*up & 0x0f));
371 up++;
372 if (width)
373 for (q = p; *q; q++)
374 PCHAR(*q);
375 }
376 break;
377 case 'd':
378 case 'i':
379 base = 10;
380 sign = 1;
381 goto handle_sign;
382 case 'h':
383 if (hflag) {
384 hflag = 0;
385 cflag = 1;
386 } else
387 hflag = 1;
388 goto reswitch;
389 case 'j':
390 jflag = 1;
391 goto reswitch;
392 case 'l':
393 if (lflag) {
394 lflag = 0;
395 qflag = 1;
396 } else
397 lflag = 1;
398 goto reswitch;
399 case 'n':
400 if (jflag)
401 *(va_arg(ap, intmax_t *)) = retval;
402 else if (qflag)
403 *(va_arg(ap, quad_t *)) = retval;
404 else if (lflag)
405 *(va_arg(ap, long *)) = retval;
406 else if (zflag)
407 *(va_arg(ap, size_t *)) = retval;
408 else if (hflag)
409 *(va_arg(ap, short *)) = retval;
410 else if (cflag)
411 *(va_arg(ap, char *)) = retval;
412 else
413 *(va_arg(ap, int *)) = retval;
414 break;
415 case 'o':
416 base = 8;
417 goto handle_nosign;
418 case 'p':
419 base = 16;
420 sharpflag = (width == 0);
421 sign = 0;
422 num = (uintptr_t)va_arg(ap, void *);
423 goto number;
424 case 'q':
425 qflag = 1;
426 goto reswitch;
427 case 'r':
428 base = radix;
429 if (sign)
430 goto handle_sign;
431 goto handle_nosign;
432 case 's':
433 p = va_arg(ap, char *);
434 if (p == NULL)
435 p = "(null)";
436 if (!dot)
437 n = strlen(p);
438 else
439 for (n = 0; n < dwidth && p[n]; n++)
440 continue;
441
442 width -= n;
443
444 if (!ladjust && width > 0)
445 while (width--)
446 PCHAR(padc);
447 while (n--)
448 PCHAR(*p++);
449 if (ladjust && width > 0)
450 while (width--)
451 PCHAR(padc);
452 break;
453 case 'S': /* Assume console can cope with wide chars */
454 S = va_arg(ap, uint16_t *);
455 if (S == NULL)
456 S = (uint16_t *)L"(null)";
457 if (!dot) {
458 for (n = 0; S[n] != 0; n++)
459 continue;
460 } else {
461 for (n = 0; n < dwidth && S[n]; n++)
462 continue;
463 }
464
465 width -= n;
466
467 if (!ladjust && width > 0)
468 while (width--)
469 PCHAR(padc);
470 while (n--)
471 PCHAR(*S++);
472 if (ladjust && width > 0)
473 while (width--)
474 PCHAR(padc);
475 break;
476 case 't':
477 tflag = 1;
478 goto reswitch;
479 case 'u':
480 base = 10;
481 goto handle_nosign;
482 case 'X':
483 upper = 1;
484 /* FALLTHROUGH */
485 case 'x':
486 base = 16;
487 goto handle_nosign;
488 case 'y':
489 base = 16;
490 sign = 1;
491 goto handle_sign;
492 case 'z':
493 zflag = 1;
494 goto reswitch;
495 handle_nosign:
496 sign = 0;
497 if (jflag)
498 num = va_arg(ap, uintmax_t);
499 else if (qflag)
500 num = va_arg(ap, uint64_t);
501 else if (tflag)
502 num = va_arg(ap, ptrdiff_t);
503 else if (lflag)
504 num = va_arg(ap, ulong_t);
505 else if (zflag)
506 num = va_arg(ap, size_t);
507 else if (hflag)
508 num = (ushort_t)va_arg(ap, int);
509 else if (cflag)
510 num = (uchar_t)va_arg(ap, int);
511 else
512 num = va_arg(ap, uint_t);
513 goto number;
514 handle_sign:
515 if (jflag)
516 num = va_arg(ap, intmax_t);
517 else if (qflag)
518 num = va_arg(ap, quad_t);
519 else if (tflag)
520 num = va_arg(ap, ptrdiff_t);
521 else if (lflag)
522 num = va_arg(ap, long);
523 else if (zflag)
524 num = va_arg(ap, ssize_t);
525 else if (hflag)
526 num = (short)va_arg(ap, int);
527 else if (cflag)
528 num = (char)va_arg(ap, int);
529 else
530 num = va_arg(ap, int);
531 number:
532 if (sign && (intmax_t)num < 0) {
533 neg = 1;
534 num = -(intmax_t)num;
535 }
536 p = ksprintn(nbuf, num, base, &n, upper);
537 tmp = 0;
538 if (sharpflag && num != 0) {
539 if (base == 8)
540 tmp++;
541 else if (base == 16)
542 tmp += 2;
543 }
544 if (neg)
545 tmp++;
546
547 if (!ladjust && padc == '0')
548 dwidth = width - tmp;
549 width -= tmp + imax(dwidth, n);
550 dwidth -= n;
551 if (!ladjust)
552 while (width-- > 0)
553 PCHAR(' ');
554 if (neg)
555 PCHAR('-');
556 if (sharpflag && num != 0) {
557 if (base == 8) {
558 PCHAR('0');
559 } else if (base == 16) {
560 PCHAR('0');
561 PCHAR('x');
562 }
563 }
564 while (dwidth-- > 0)
565 PCHAR('0');
566
567 while (*p)
568 PCHAR(*p--);
569
570 if (ladjust)
571 while (width-- > 0)
572 PCHAR(' ');
573
574 break;
575 default:
576 while (percent < fmt)
577 PCHAR(*percent++);
578 /*
579 * Since we ignore a formatting argument it is no
580 * longer safe to obey the remaining formatting
581 * arguments as the arguments will no longer match
582 * the format specs.
583 */
584 stop = 1;
585 break;
586 }
587 }
588 #undef PCHAR
589 }
590