printf.c revision 277560
138451Smsmith/*-
238451Smsmith * Copyright (c) 1986, 1988, 1991, 1993
338451Smsmith *	The Regents of the University of California.  All rights reserved.
438451Smsmith * (c) UNIX System Laboratories, Inc.
538451Smsmith * All or some portions of this file are derived from material licensed
638451Smsmith * to the University of California by American Telephone and Telegraph
738451Smsmith * Co. or Unix System Laboratories, Inc. and are reproduced herein with
838451Smsmith * the permission of UNIX System Laboratories, Inc.
938451Smsmith *
1038451Smsmith * Redistribution and use in source and binary forms, with or without
1138451Smsmith * modification, are permitted provided that the following conditions
1238451Smsmith * are met:
1338451Smsmith * 1. Redistributions of source code must retain the above copyright
1438451Smsmith *    notice, this list of conditions and the following disclaimer.
1538451Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1638451Smsmith *    notice, this list of conditions and the following disclaimer in the
1738451Smsmith *    documentation and/or other materials provided with the distribution.
1838451Smsmith * 4. Neither the name of the University nor the names of its contributors
1938451Smsmith *    may be used to endorse or promote products derived from this software
2038451Smsmith *    without specific prior written permission.
2138451Smsmith *
2238451Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2338451Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2438451Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2538451Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2638451Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2738451Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2838451Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2938451Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3038451Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3138451Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3238451Smsmith * SUCH DAMAGE.
3338451Smsmith *
3438451Smsmith *	@(#)subr_prf.c	8.3 (Berkeley) 1/21/94
3538451Smsmith */
3638451Smsmith
3784221Sdillon#include <sys/cdefs.h>
3884221Sdillon__FBSDID("$FreeBSD: head/lib/libstand/printf.c 277560 2015-01-23 07:30:57Z danfe $");
3984221Sdillon
4038451Smsmith/*
4138451Smsmith * Standaloneified version of the FreeBSD kernel printf family.
4238451Smsmith */
4338451Smsmith
4438451Smsmith#include <sys/types.h>
45113159Speter#include <sys/stddef.h>
46113159Speter#include <sys/stdint.h>
47103949Smike#include <limits.h>
4855137Speter#include <string.h>
4938451Smsmith#include "stand.h"
5038451Smsmith
5138451Smsmith/*
5238451Smsmith * Note that stdarg.h and the ANSI style va_start macro is used for both
5338451Smsmith * ANSI and traditional C compilers.
5438451Smsmith */
5538451Smsmith#include <machine/stdarg.h>
5638451Smsmith
57113159Speter#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
58113159Speter
59266878Shselaskytypedef void (kvprintf_fn_t)(int, void *);
60266878Shselasky
61156518Sjkimstatic char	*ksprintn (char *buf, uintmax_t num, int base, int *len, int upper);
62266878Shselaskystatic int	kvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap);
6338451Smsmith
64266878Shselaskystatic void
65266878Shselaskyputchar_wrapper(int cc, void *arg)
66266878Shselasky{
67266879Shselasky
68266878Shselasky	putchar(cc);
69266878Shselasky}
70266878Shselasky
7138451Smsmithint
7238451Smsmithprintf(const char *fmt, ...)
7338451Smsmith{
7438451Smsmith	va_list ap;
7538451Smsmith	int retval;
7638451Smsmith
7738451Smsmith	va_start(ap, fmt);
78266878Shselasky	retval = kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
7938451Smsmith	va_end(ap);
8038451Smsmith	return retval;
8138451Smsmith}
8238451Smsmith
8338451Smsmithvoid
8438451Smsmithvprintf(const char *fmt, va_list ap)
8538451Smsmith{
86266879Shselasky
87266878Shselasky	kvprintf(fmt, putchar_wrapper, NULL, 10, ap);
8838451Smsmith}
8938451Smsmith
9038451Smsmithint
9138451Smsmithsprintf(char *buf, const char *cfmt, ...)
9238451Smsmith{
9338451Smsmith	int retval;
9438451Smsmith	va_list ap;
9538451Smsmith
9638451Smsmith	va_start(ap, cfmt);
9738451Smsmith	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
9838451Smsmith	buf[retval] = '\0';
9938451Smsmith	va_end(ap);
10038451Smsmith	return retval;
10138451Smsmith}
10238451Smsmith
103266878Shselaskystruct print_buf {
104266878Shselasky	char *buf;
105266878Shselasky	size_t size;
106266878Shselasky};
107266878Shselasky
108266878Shselaskystatic void
109266878Shselaskysnprint_func(int ch, void *arg)
110266878Shselasky{
111266878Shselasky	struct print_buf *pbuf = arg;
112266878Shselasky
113266878Shselasky	if (pbuf->size < 2) {
114266878Shselasky		/*
115266878Shselasky		 * Reserve last buffer position for the terminating
116266878Shselasky		 * character:
117266878Shselasky		 */
118266878Shselasky		return;
119266878Shselasky	}
120266878Shselasky	*(pbuf->buf)++ = ch;
121266878Shselasky	pbuf->size--;
122266878Shselasky}
123266878Shselasky
124266878Shselaskyint
125266878Shselaskysnprintf(char *buf, size_t size, const char *cfmt, ...)
126266878Shselasky{
127266878Shselasky	int retval;
128266878Shselasky	va_list ap;
129266878Shselasky	struct print_buf arg;
130266878Shselasky
131266878Shselasky	arg.buf = buf;
132266878Shselasky	arg.size = size;
133266878Shselasky
134266878Shselasky	va_start(ap, cfmt);
135266878Shselasky	retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
136266878Shselasky	va_end(ap);
137266878Shselasky
138266878Shselasky	if (arg.size >= 1)
139266878Shselasky		*(arg.buf)++ = 0;
140266878Shselasky	return retval;
141266878Shselasky}
142266878Shselasky
14340805Smsmithvoid
14440805Smsmithvsprintf(char *buf, const char *cfmt, va_list ap)
14540805Smsmith{
14640805Smsmith	int	retval;
14740805Smsmith
14840805Smsmith	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
14940805Smsmith	buf[retval] = '\0';
15040805Smsmith}
15140805Smsmith
15238451Smsmith/*
153113159Speter * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
154113159Speter * order; return an optional length and a pointer to the last character
155113159Speter * written in the buffer (i.e., the first character of the string).
156113159Speter * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
15738451Smsmith */
15838451Smsmithstatic char *
159156518Sjkimksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
160113159Speter{
161156518Sjkim	char *p, c;
16238451Smsmith
163113159Speter	p = nbuf;
164113159Speter	*p = '\0';
16538451Smsmith	do {
166156518Sjkim		c = hex2ascii(num % base);
167156518Sjkim		*++p = upper ? toupper(c) : c;
168113159Speter	} while (num /= base);
16938451Smsmith	if (lenp)
170113159Speter		*lenp = p - nbuf;
17138451Smsmith	return (p);
17238451Smsmith}
17338451Smsmith
17438451Smsmith/*
17538451Smsmith * Scaled down version of printf(3).
17638451Smsmith *
17738451Smsmith * Two additional formats:
17838451Smsmith *
17938451Smsmith * The format %b is supported to decode error registers.
18038451Smsmith * Its usage is:
18138451Smsmith *
18238451Smsmith *	printf("reg=%b\n", regval, "<base><arg>*");
18338451Smsmith *
18438451Smsmith * where <base> is the output base expressed as a control character, e.g.
18538451Smsmith * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
18638451Smsmith * the first of which gives the bit number to be inspected (origin 1), and
18738451Smsmith * the next characters (up to a control character, i.e. a character <= 32),
18838451Smsmith * give the name of the register.  Thus:
18938451Smsmith *
190277560Sdanfe *	kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
19138451Smsmith *
19238451Smsmith * would produce output:
19338451Smsmith *
19438451Smsmith *	reg=3<BITTWO,BITONE>
19538451Smsmith *
19638451Smsmith * XXX:  %D  -- Hexdump, takes pointer and separator string:
19738451Smsmith *		("%6D", ptr, ":")   -> XX:XX:XX:XX:XX:XX
19838451Smsmith *		("%*D", len, ptr, " " -> XX XX XX XX ...
19938451Smsmith */
20038451Smsmithstatic int
201266878Shselaskykvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap)
20238451Smsmith{
203266878Shselasky#define PCHAR(c) {int cc=(c); if (func) (*func)(cc, arg); else *d++ = cc; retval++; }
204113159Speter	char nbuf[MAXNBUF];
205113159Speter	char *d;
206113159Speter	const char *p, *percent, *q;
20738451Smsmith	u_char *up;
20838451Smsmith	int ch, n;
209113159Speter	uintmax_t num;
210113159Speter	int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
211209837Sjkim	int cflag, hflag, jflag, tflag, zflag;
212156518Sjkim	int dwidth, upper;
21338451Smsmith	char padc;
214209837Sjkim	int stop = 0, retval = 0;
21538451Smsmith
216113159Speter	num = 0;
21738451Smsmith	if (!func)
21838451Smsmith		d = (char *) arg;
21938451Smsmith	else
22038451Smsmith		d = NULL;
22138451Smsmith
22238451Smsmith	if (fmt == NULL)
22338451Smsmith		fmt = "(fmt null)\n";
22438451Smsmith
22538451Smsmith	if (radix < 2 || radix > 36)
22638451Smsmith		radix = 10;
22738451Smsmith
22838451Smsmith	for (;;) {
22938451Smsmith		padc = ' ';
23038451Smsmith		width = 0;
231209837Sjkim		while ((ch = (u_char)*fmt++) != '%' || stop) {
232113159Speter			if (ch == '\0')
233113159Speter				return (retval);
23438451Smsmith			PCHAR(ch);
23538451Smsmith		}
236113159Speter		percent = fmt - 1;
237113159Speter		qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
238156518Sjkim		sign = 0; dot = 0; dwidth = 0; upper = 0;
239209837Sjkim		cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
24038451Smsmithreswitch:	switch (ch = (u_char)*fmt++) {
24138451Smsmith		case '.':
24238451Smsmith			dot = 1;
24338451Smsmith			goto reswitch;
24438451Smsmith		case '#':
24538451Smsmith			sharpflag = 1;
24638451Smsmith			goto reswitch;
24738451Smsmith		case '+':
24838451Smsmith			sign = 1;
24938451Smsmith			goto reswitch;
25038451Smsmith		case '-':
25138451Smsmith			ladjust = 1;
25238451Smsmith			goto reswitch;
25338451Smsmith		case '%':
25438451Smsmith			PCHAR(ch);
25538451Smsmith			break;
25638451Smsmith		case '*':
25738451Smsmith			if (!dot) {
25838451Smsmith				width = va_arg(ap, int);
25938451Smsmith				if (width < 0) {
26038451Smsmith					ladjust = !ladjust;
26138451Smsmith					width = -width;
26238451Smsmith				}
26338451Smsmith			} else {
26438451Smsmith				dwidth = va_arg(ap, int);
26538451Smsmith			}
26638451Smsmith			goto reswitch;
26738451Smsmith		case '0':
26838451Smsmith			if (!dot) {
26938451Smsmith				padc = '0';
27038451Smsmith				goto reswitch;
27138451Smsmith			}
27238451Smsmith		case '1': case '2': case '3': case '4':
27338451Smsmith		case '5': case '6': case '7': case '8': case '9':
27438451Smsmith				for (n = 0;; ++fmt) {
27538451Smsmith					n = n * 10 + ch - '0';
27638451Smsmith					ch = *fmt;
27738451Smsmith					if (ch < '0' || ch > '9')
27838451Smsmith						break;
27938451Smsmith				}
28038451Smsmith			if (dot)
28138451Smsmith				dwidth = n;
28238451Smsmith			else
28338451Smsmith				width = n;
28438451Smsmith			goto reswitch;
28538451Smsmith		case 'b':
286209837Sjkim			num = (u_int)va_arg(ap, int);
28738451Smsmith			p = va_arg(ap, char *);
288156518Sjkim			for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
28938451Smsmith				PCHAR(*q--);
29038451Smsmith
291113159Speter			if (num == 0)
29238451Smsmith				break;
29338451Smsmith
29438451Smsmith			for (tmp = 0; *p;) {
29538451Smsmith				n = *p++;
296113159Speter				if (num & (1 << (n - 1))) {
29738451Smsmith					PCHAR(tmp ? ',' : '<');
29838451Smsmith					for (; (n = *p) > ' '; ++p)
29938451Smsmith						PCHAR(n);
30038451Smsmith					tmp = 1;
30138451Smsmith				} else
30238451Smsmith					for (; *p > ' '; ++p)
30338451Smsmith						continue;
30438451Smsmith			}
30538451Smsmith			if (tmp)
30638451Smsmith				PCHAR('>');
30738451Smsmith			break;
30838451Smsmith		case 'c':
30938451Smsmith			PCHAR(va_arg(ap, int));
31038451Smsmith			break;
31138451Smsmith		case 'D':
31238451Smsmith			up = va_arg(ap, u_char *);
31338451Smsmith			p = va_arg(ap, char *);
31438451Smsmith			if (!width)
31538451Smsmith				width = 16;
31638451Smsmith			while(width--) {
31738451Smsmith				PCHAR(hex2ascii(*up >> 4));
31838451Smsmith				PCHAR(hex2ascii(*up & 0x0f));
31938451Smsmith				up++;
32038451Smsmith				if (width)
32138451Smsmith					for (q=p;*q;q++)
32238451Smsmith						PCHAR(*q);
32338451Smsmith			}
32438451Smsmith			break;
32538451Smsmith		case 'd':
326113159Speter		case 'i':
327113159Speter			base = 10;
32838451Smsmith			sign = 1;
329113159Speter			goto handle_sign;
330209837Sjkim		case 'h':
331209837Sjkim			if (hflag) {
332209837Sjkim				hflag = 0;
333209837Sjkim				cflag = 1;
334209837Sjkim			} else
335209837Sjkim				hflag = 1;
336209837Sjkim			goto reswitch;
337113159Speter		case 'j':
338113159Speter			jflag = 1;
339113159Speter			goto reswitch;
34038451Smsmith		case 'l':
341113159Speter			if (lflag) {
342113159Speter				lflag = 0;
343113159Speter				qflag = 1;
344113159Speter			} else
345113159Speter				lflag = 1;
34638451Smsmith			goto reswitch;
34738451Smsmith		case 'n':
348113159Speter			if (jflag)
349113159Speter				*(va_arg(ap, intmax_t *)) = retval;
350113159Speter			else if (qflag)
351113159Speter				*(va_arg(ap, quad_t *)) = retval;
352113159Speter			else if (lflag)
353113159Speter				*(va_arg(ap, long *)) = retval;
354113159Speter			else if (zflag)
355113159Speter				*(va_arg(ap, size_t *)) = retval;
356209837Sjkim			else if (hflag)
357209837Sjkim				*(va_arg(ap, short *)) = retval;
358209837Sjkim			else if (cflag)
359209837Sjkim				*(va_arg(ap, char *)) = retval;
360113159Speter			else
361113159Speter				*(va_arg(ap, int *)) = retval;
362113159Speter			break;
36338451Smsmith		case 'o':
36438451Smsmith			base = 8;
365113159Speter			goto handle_nosign;
36638451Smsmith		case 'p':
36738451Smsmith			base = 16;
368113159Speter			sharpflag = (width == 0);
369113159Speter			sign = 0;
370113159Speter			num = (uintptr_t)va_arg(ap, void *);
37138451Smsmith			goto number;
372113159Speter		case 'q':
373113159Speter			qflag = 1;
374113159Speter			goto reswitch;
375113159Speter		case 'r':
376113159Speter			base = radix;
377113159Speter			if (sign)
378113159Speter				goto handle_sign;
379113159Speter			goto handle_nosign;
38038451Smsmith		case 's':
38138451Smsmith			p = va_arg(ap, char *);
38238451Smsmith			if (p == NULL)
38338451Smsmith				p = "(null)";
38438451Smsmith			if (!dot)
38538451Smsmith				n = strlen (p);
38638451Smsmith			else
38738451Smsmith				for (n = 0; n < dwidth && p[n]; n++)
38838451Smsmith					continue;
38938451Smsmith
39038451Smsmith			width -= n;
39138451Smsmith
39238451Smsmith			if (!ladjust && width > 0)
39338451Smsmith				while (width--)
39438451Smsmith					PCHAR(padc);
39538451Smsmith			while (n--)
39638451Smsmith				PCHAR(*p++);
39738451Smsmith			if (ladjust && width > 0)
39838451Smsmith				while (width--)
39938451Smsmith					PCHAR(padc);
40038451Smsmith			break;
401113159Speter		case 't':
402113159Speter			tflag = 1;
403113159Speter			goto reswitch;
40438451Smsmith		case 'u':
40538451Smsmith			base = 10;
406113159Speter			goto handle_nosign;
407156518Sjkim		case 'X':
408156518Sjkim			upper = 1;
40938451Smsmith		case 'x':
41038451Smsmith			base = 16;
411113159Speter			goto handle_nosign;
412113159Speter		case 'y':
413113159Speter			base = 16;
414113159Speter			sign = 1;
415113159Speter			goto handle_sign;
416113159Speter		case 'z':
417113159Speter			zflag = 1;
418113159Speter			goto reswitch;
419113159Speterhandle_nosign:
420113159Speter			sign = 0;
421113159Speter			if (jflag)
422113159Speter				num = va_arg(ap, uintmax_t);
423113159Speter			else if (qflag)
424113159Speter				num = va_arg(ap, u_quad_t);
425113159Speter			else if (tflag)
426113159Speter				num = va_arg(ap, ptrdiff_t);
427113159Speter			else if (lflag)
428113159Speter				num = va_arg(ap, u_long);
429113159Speter			else if (zflag)
430113159Speter				num = va_arg(ap, size_t);
431209837Sjkim			else if (hflag)
432209837Sjkim				num = (u_short)va_arg(ap, int);
433209837Sjkim			else if (cflag)
434209837Sjkim				num = (u_char)va_arg(ap, int);
435113159Speter			else
436113159Speter				num = va_arg(ap, u_int);
437113159Speter			goto number;
438113159Speterhandle_sign:
439113159Speter			if (jflag)
440113159Speter				num = va_arg(ap, intmax_t);
441113159Speter			else if (qflag)
442113159Speter				num = va_arg(ap, quad_t);
443113159Speter			else if (tflag)
444113159Speter				num = va_arg(ap, ptrdiff_t);
445113159Speter			else if (lflag)
446113159Speter				num = va_arg(ap, long);
447113159Speter			else if (zflag)
448185037Sdelphij				num = va_arg(ap, ssize_t);
449209837Sjkim			else if (hflag)
450209837Sjkim				num = (short)va_arg(ap, int);
451209837Sjkim			else if (cflag)
452209837Sjkim				num = (char)va_arg(ap, int);
453113159Speter			else
454113159Speter				num = va_arg(ap, int);
455113159Speternumber:
456113159Speter			if (sign && (intmax_t)num < 0) {
45738451Smsmith				neg = 1;
458113159Speter				num = -(intmax_t)num;
45938451Smsmith			}
460209837Sjkim			p = ksprintn(nbuf, num, base, &n, upper);
461209837Sjkim			tmp = 0;
462113159Speter			if (sharpflag && num != 0) {
46338451Smsmith				if (base == 8)
46438451Smsmith					tmp++;
46538451Smsmith				else if (base == 16)
46638451Smsmith					tmp += 2;
46738451Smsmith			}
46838451Smsmith			if (neg)
46938451Smsmith				tmp++;
47038451Smsmith
471209837Sjkim			if (!ladjust && padc == '0')
472209837Sjkim				dwidth = width - tmp;
473209949Sjkim			width -= tmp + imax(dwidth, n);
474209837Sjkim			dwidth -= n;
475209837Sjkim			if (!ladjust)
476209837Sjkim				while (width-- > 0)
477209837Sjkim					PCHAR(' ');
47838451Smsmith			if (neg)
47938451Smsmith				PCHAR('-');
480113159Speter			if (sharpflag && num != 0) {
48138451Smsmith				if (base == 8) {
48238451Smsmith					PCHAR('0');
48338451Smsmith				} else if (base == 16) {
48438451Smsmith					PCHAR('0');
48538451Smsmith					PCHAR('x');
48638451Smsmith				}
48738451Smsmith			}
488209837Sjkim			while (dwidth-- > 0)
489209837Sjkim				PCHAR('0');
49038451Smsmith
49138451Smsmith			while (*p)
49238451Smsmith				PCHAR(*p--);
49338451Smsmith
494209837Sjkim			if (ladjust)
495209837Sjkim				while (width-- > 0)
496209837Sjkim					PCHAR(' ');
49738451Smsmith
49838451Smsmith			break;
49938451Smsmith		default:
500113159Speter			while (percent < fmt)
501113159Speter				PCHAR(*percent++);
502209837Sjkim			/*
503277560Sdanfe			 * Since we ignore a formatting argument it is no
504209837Sjkim			 * longer safe to obey the remaining formatting
505209837Sjkim			 * arguments as the arguments will no longer match
506209837Sjkim			 * the format specs.
507209837Sjkim			 */
508209837Sjkim			stop = 1;
50938451Smsmith			break;
51038451Smsmith		}
51138451Smsmith	}
51238451Smsmith#undef PCHAR
51338451Smsmith}
514