printf.c revision 334935
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: stable/11/stand/libsa/printf.c 334935 2018-06-10 22:26:15Z ian $");
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
83334935Sianint
8438451Smsmithvprintf(const char *fmt, va_list ap)
8538451Smsmith{
86266879Shselasky
87334935Sian	return (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
143334935Sianint
144334935Sianvsnprintf(char *buf, size_t size, const char *cfmt, va_list ap)
145334935Sian{
146334935Sian	struct print_buf arg;
147334935Sian	int retval;
148334935Sian
149334935Sian	arg.buf = buf;
150334935Sian	arg.size = size;
151334935Sian
152334935Sian	retval = kvprintf(cfmt, &snprint_func, &arg, 10, ap);
153334935Sian
154334935Sian	if (arg.size >= 1)
155334935Sian		*(arg.buf)++ = 0;
156334935Sian
157334935Sian	return (retval);
158334935Sian}
159334935Sian
160334935Sianint
16140805Smsmithvsprintf(char *buf, const char *cfmt, va_list ap)
16240805Smsmith{
16340805Smsmith	int	retval;
16440805Smsmith
16540805Smsmith	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
16640805Smsmith	buf[retval] = '\0';
167334935Sian
168334935Sian	return (retval);
16940805Smsmith}
17040805Smsmith
17138451Smsmith/*
172113159Speter * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
173113159Speter * order; return an optional length and a pointer to the last character
174113159Speter * written in the buffer (i.e., the first character of the string).
175113159Speter * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
17638451Smsmith */
17738451Smsmithstatic char *
178156518Sjkimksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
179113159Speter{
180156518Sjkim	char *p, c;
18138451Smsmith
182113159Speter	p = nbuf;
183113159Speter	*p = '\0';
18438451Smsmith	do {
185156518Sjkim		c = hex2ascii(num % base);
186156518Sjkim		*++p = upper ? toupper(c) : c;
187113159Speter	} while (num /= base);
18838451Smsmith	if (lenp)
189113159Speter		*lenp = p - nbuf;
19038451Smsmith	return (p);
19138451Smsmith}
19238451Smsmith
19338451Smsmith/*
19438451Smsmith * Scaled down version of printf(3).
19538451Smsmith *
19638451Smsmith * Two additional formats:
19738451Smsmith *
19838451Smsmith * The format %b is supported to decode error registers.
19938451Smsmith * Its usage is:
20038451Smsmith *
20138451Smsmith *	printf("reg=%b\n", regval, "<base><arg>*");
20238451Smsmith *
20338451Smsmith * where <base> is the output base expressed as a control character, e.g.
20438451Smsmith * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
20538451Smsmith * the first of which gives the bit number to be inspected (origin 1), and
20638451Smsmith * the next characters (up to a control character, i.e. a character <= 32),
20738451Smsmith * give the name of the register.  Thus:
20838451Smsmith *
209277560Sdanfe *	kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE");
21038451Smsmith *
21138451Smsmith * would produce output:
21238451Smsmith *
21338451Smsmith *	reg=3<BITTWO,BITONE>
21438451Smsmith *
21538451Smsmith * XXX:  %D  -- Hexdump, takes pointer and separator string:
21638451Smsmith *		("%6D", ptr, ":")   -> XX:XX:XX:XX:XX:XX
21738451Smsmith *		("%*D", len, ptr, " " -> XX XX XX XX ...
21838451Smsmith */
21938451Smsmithstatic int
220266878Shselaskykvprintf(char const *fmt, kvprintf_fn_t *func, void *arg, int radix, va_list ap)
22138451Smsmith{
222266878Shselasky#define PCHAR(c) {int cc=(c); if (func) (*func)(cc, arg); else *d++ = cc; retval++; }
223113159Speter	char nbuf[MAXNBUF];
224113159Speter	char *d;
225113159Speter	const char *p, *percent, *q;
226300078Simp	uint16_t *S;
22738451Smsmith	u_char *up;
22838451Smsmith	int ch, n;
229113159Speter	uintmax_t num;
230113159Speter	int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
231209837Sjkim	int cflag, hflag, jflag, tflag, zflag;
232156518Sjkim	int dwidth, upper;
23338451Smsmith	char padc;
234209837Sjkim	int stop = 0, retval = 0;
23538451Smsmith
236113159Speter	num = 0;
23738451Smsmith	if (!func)
23838451Smsmith		d = (char *) arg;
23938451Smsmith	else
24038451Smsmith		d = NULL;
24138451Smsmith
24238451Smsmith	if (fmt == NULL)
24338451Smsmith		fmt = "(fmt null)\n";
24438451Smsmith
24538451Smsmith	if (radix < 2 || radix > 36)
24638451Smsmith		radix = 10;
24738451Smsmith
24838451Smsmith	for (;;) {
24938451Smsmith		padc = ' ';
25038451Smsmith		width = 0;
251209837Sjkim		while ((ch = (u_char)*fmt++) != '%' || stop) {
252113159Speter			if (ch == '\0')
253113159Speter				return (retval);
25438451Smsmith			PCHAR(ch);
25538451Smsmith		}
256113159Speter		percent = fmt - 1;
257113159Speter		qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
258156518Sjkim		sign = 0; dot = 0; dwidth = 0; upper = 0;
259209837Sjkim		cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
26038451Smsmithreswitch:	switch (ch = (u_char)*fmt++) {
26138451Smsmith		case '.':
26238451Smsmith			dot = 1;
26338451Smsmith			goto reswitch;
26438451Smsmith		case '#':
26538451Smsmith			sharpflag = 1;
26638451Smsmith			goto reswitch;
26738451Smsmith		case '+':
26838451Smsmith			sign = 1;
26938451Smsmith			goto reswitch;
27038451Smsmith		case '-':
27138451Smsmith			ladjust = 1;
27238451Smsmith			goto reswitch;
27338451Smsmith		case '%':
27438451Smsmith			PCHAR(ch);
27538451Smsmith			break;
27638451Smsmith		case '*':
27738451Smsmith			if (!dot) {
27838451Smsmith				width = va_arg(ap, int);
27938451Smsmith				if (width < 0) {
28038451Smsmith					ladjust = !ladjust;
28138451Smsmith					width = -width;
28238451Smsmith				}
28338451Smsmith			} else {
28438451Smsmith				dwidth = va_arg(ap, int);
28538451Smsmith			}
28638451Smsmith			goto reswitch;
28738451Smsmith		case '0':
28838451Smsmith			if (!dot) {
28938451Smsmith				padc = '0';
29038451Smsmith				goto reswitch;
29138451Smsmith			}
29238451Smsmith		case '1': case '2': case '3': case '4':
29338451Smsmith		case '5': case '6': case '7': case '8': case '9':
29438451Smsmith				for (n = 0;; ++fmt) {
29538451Smsmith					n = n * 10 + ch - '0';
29638451Smsmith					ch = *fmt;
29738451Smsmith					if (ch < '0' || ch > '9')
29838451Smsmith						break;
29938451Smsmith				}
30038451Smsmith			if (dot)
30138451Smsmith				dwidth = n;
30238451Smsmith			else
30338451Smsmith				width = n;
30438451Smsmith			goto reswitch;
30538451Smsmith		case 'b':
306209837Sjkim			num = (u_int)va_arg(ap, int);
30738451Smsmith			p = va_arg(ap, char *);
308156518Sjkim			for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
30938451Smsmith				PCHAR(*q--);
31038451Smsmith
311113159Speter			if (num == 0)
31238451Smsmith				break;
31338451Smsmith
31438451Smsmith			for (tmp = 0; *p;) {
31538451Smsmith				n = *p++;
316113159Speter				if (num & (1 << (n - 1))) {
31738451Smsmith					PCHAR(tmp ? ',' : '<');
31838451Smsmith					for (; (n = *p) > ' '; ++p)
31938451Smsmith						PCHAR(n);
32038451Smsmith					tmp = 1;
32138451Smsmith				} else
32238451Smsmith					for (; *p > ' '; ++p)
32338451Smsmith						continue;
32438451Smsmith			}
32538451Smsmith			if (tmp)
32638451Smsmith				PCHAR('>');
32738451Smsmith			break;
32838451Smsmith		case 'c':
32938451Smsmith			PCHAR(va_arg(ap, int));
33038451Smsmith			break;
33138451Smsmith		case 'D':
33238451Smsmith			up = va_arg(ap, u_char *);
33338451Smsmith			p = va_arg(ap, char *);
33438451Smsmith			if (!width)
33538451Smsmith				width = 16;
33638451Smsmith			while(width--) {
33738451Smsmith				PCHAR(hex2ascii(*up >> 4));
33838451Smsmith				PCHAR(hex2ascii(*up & 0x0f));
33938451Smsmith				up++;
34038451Smsmith				if (width)
34138451Smsmith					for (q=p;*q;q++)
34238451Smsmith						PCHAR(*q);
34338451Smsmith			}
34438451Smsmith			break;
34538451Smsmith		case 'd':
346113159Speter		case 'i':
347113159Speter			base = 10;
34838451Smsmith			sign = 1;
349113159Speter			goto handle_sign;
350209837Sjkim		case 'h':
351209837Sjkim			if (hflag) {
352209837Sjkim				hflag = 0;
353209837Sjkim				cflag = 1;
354209837Sjkim			} else
355209837Sjkim				hflag = 1;
356209837Sjkim			goto reswitch;
357113159Speter		case 'j':
358113159Speter			jflag = 1;
359113159Speter			goto reswitch;
36038451Smsmith		case 'l':
361113159Speter			if (lflag) {
362113159Speter				lflag = 0;
363113159Speter				qflag = 1;
364113159Speter			} else
365113159Speter				lflag = 1;
36638451Smsmith			goto reswitch;
36738451Smsmith		case 'n':
368113159Speter			if (jflag)
369113159Speter				*(va_arg(ap, intmax_t *)) = retval;
370113159Speter			else if (qflag)
371113159Speter				*(va_arg(ap, quad_t *)) = retval;
372113159Speter			else if (lflag)
373113159Speter				*(va_arg(ap, long *)) = retval;
374113159Speter			else if (zflag)
375113159Speter				*(va_arg(ap, size_t *)) = retval;
376209837Sjkim			else if (hflag)
377209837Sjkim				*(va_arg(ap, short *)) = retval;
378209837Sjkim			else if (cflag)
379209837Sjkim				*(va_arg(ap, char *)) = retval;
380113159Speter			else
381113159Speter				*(va_arg(ap, int *)) = retval;
382113159Speter			break;
38338451Smsmith		case 'o':
38438451Smsmith			base = 8;
385113159Speter			goto handle_nosign;
38638451Smsmith		case 'p':
38738451Smsmith			base = 16;
388113159Speter			sharpflag = (width == 0);
389113159Speter			sign = 0;
390113159Speter			num = (uintptr_t)va_arg(ap, void *);
39138451Smsmith			goto number;
392113159Speter		case 'q':
393113159Speter			qflag = 1;
394113159Speter			goto reswitch;
395113159Speter		case 'r':
396113159Speter			base = radix;
397113159Speter			if (sign)
398113159Speter				goto handle_sign;
399113159Speter			goto handle_nosign;
40038451Smsmith		case 's':
40138451Smsmith			p = va_arg(ap, char *);
40238451Smsmith			if (p == NULL)
40338451Smsmith				p = "(null)";
40438451Smsmith			if (!dot)
40538451Smsmith				n = strlen (p);
40638451Smsmith			else
40738451Smsmith				for (n = 0; n < dwidth && p[n]; n++)
40838451Smsmith					continue;
40938451Smsmith
41038451Smsmith			width -= n;
41138451Smsmith
41238451Smsmith			if (!ladjust && width > 0)
41338451Smsmith				while (width--)
41438451Smsmith					PCHAR(padc);
41538451Smsmith			while (n--)
41638451Smsmith				PCHAR(*p++);
41738451Smsmith			if (ladjust && width > 0)
41838451Smsmith				while (width--)
41938451Smsmith					PCHAR(padc);
42038451Smsmith			break;
421300078Simp		case 'S':	/* Assume console can cope with wide chars */
422300078Simp			for (S = va_arg(ap, uint16_t *); *S != 0; S++)
423300078Simp				PCHAR(*S);
424300078Simp 			break;
425113159Speter		case 't':
426113159Speter			tflag = 1;
427113159Speter			goto reswitch;
42838451Smsmith		case 'u':
42938451Smsmith			base = 10;
430113159Speter			goto handle_nosign;
431156518Sjkim		case 'X':
432156518Sjkim			upper = 1;
43338451Smsmith		case 'x':
43438451Smsmith			base = 16;
435113159Speter			goto handle_nosign;
436113159Speter		case 'y':
437113159Speter			base = 16;
438113159Speter			sign = 1;
439113159Speter			goto handle_sign;
440113159Speter		case 'z':
441113159Speter			zflag = 1;
442113159Speter			goto reswitch;
443113159Speterhandle_nosign:
444113159Speter			sign = 0;
445113159Speter			if (jflag)
446113159Speter				num = va_arg(ap, uintmax_t);
447113159Speter			else if (qflag)
448113159Speter				num = va_arg(ap, u_quad_t);
449113159Speter			else if (tflag)
450113159Speter				num = va_arg(ap, ptrdiff_t);
451113159Speter			else if (lflag)
452113159Speter				num = va_arg(ap, u_long);
453113159Speter			else if (zflag)
454113159Speter				num = va_arg(ap, size_t);
455209837Sjkim			else if (hflag)
456209837Sjkim				num = (u_short)va_arg(ap, int);
457209837Sjkim			else if (cflag)
458209837Sjkim				num = (u_char)va_arg(ap, int);
459113159Speter			else
460113159Speter				num = va_arg(ap, u_int);
461113159Speter			goto number;
462113159Speterhandle_sign:
463113159Speter			if (jflag)
464113159Speter				num = va_arg(ap, intmax_t);
465113159Speter			else if (qflag)
466113159Speter				num = va_arg(ap, quad_t);
467113159Speter			else if (tflag)
468113159Speter				num = va_arg(ap, ptrdiff_t);
469113159Speter			else if (lflag)
470113159Speter				num = va_arg(ap, long);
471113159Speter			else if (zflag)
472185037Sdelphij				num = va_arg(ap, ssize_t);
473209837Sjkim			else if (hflag)
474209837Sjkim				num = (short)va_arg(ap, int);
475209837Sjkim			else if (cflag)
476209837Sjkim				num = (char)va_arg(ap, int);
477113159Speter			else
478113159Speter				num = va_arg(ap, int);
479113159Speternumber:
480113159Speter			if (sign && (intmax_t)num < 0) {
48138451Smsmith				neg = 1;
482113159Speter				num = -(intmax_t)num;
48338451Smsmith			}
484209837Sjkim			p = ksprintn(nbuf, num, base, &n, upper);
485209837Sjkim			tmp = 0;
486113159Speter			if (sharpflag && num != 0) {
48738451Smsmith				if (base == 8)
48838451Smsmith					tmp++;
48938451Smsmith				else if (base == 16)
49038451Smsmith					tmp += 2;
49138451Smsmith			}
49238451Smsmith			if (neg)
49338451Smsmith				tmp++;
49438451Smsmith
495209837Sjkim			if (!ladjust && padc == '0')
496209837Sjkim				dwidth = width - tmp;
497209949Sjkim			width -= tmp + imax(dwidth, n);
498209837Sjkim			dwidth -= n;
499209837Sjkim			if (!ladjust)
500209837Sjkim				while (width-- > 0)
501209837Sjkim					PCHAR(' ');
50238451Smsmith			if (neg)
50338451Smsmith				PCHAR('-');
504113159Speter			if (sharpflag && num != 0) {
50538451Smsmith				if (base == 8) {
50638451Smsmith					PCHAR('0');
50738451Smsmith				} else if (base == 16) {
50838451Smsmith					PCHAR('0');
50938451Smsmith					PCHAR('x');
51038451Smsmith				}
51138451Smsmith			}
512209837Sjkim			while (dwidth-- > 0)
513209837Sjkim				PCHAR('0');
51438451Smsmith
51538451Smsmith			while (*p)
51638451Smsmith				PCHAR(*p--);
51738451Smsmith
518209837Sjkim			if (ladjust)
519209837Sjkim				while (width-- > 0)
520209837Sjkim					PCHAR(' ');
52138451Smsmith
52238451Smsmith			break;
52338451Smsmith		default:
524113159Speter			while (percent < fmt)
525113159Speter				PCHAR(*percent++);
526209837Sjkim			/*
527277560Sdanfe			 * Since we ignore a formatting argument it is no
528209837Sjkim			 * longer safe to obey the remaining formatting
529209837Sjkim			 * arguments as the arguments will no longer match
530209837Sjkim			 * the format specs.
531209837Sjkim			 */
532209837Sjkim			stop = 1;
53338451Smsmith			break;
53438451Smsmith		}
53538451Smsmith	}
53638451Smsmith#undef PCHAR
53738451Smsmith}
538