printf.c revision 156518
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 * 3. All advertising materials mentioning features or use of this software
1938451Smsmith *    must display the following acknowledgement:
2038451Smsmith *	This product includes software developed by the University of
2138451Smsmith *	California, Berkeley and its contributors.
2238451Smsmith * 4. Neither the name of the University nor the names of its contributors
2338451Smsmith *    may be used to endorse or promote products derived from this software
2438451Smsmith *    without specific prior written permission.
2538451Smsmith *
2638451Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2738451Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2838451Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2938451Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3038451Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3138451Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3238451Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3338451Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3438451Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3538451Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3638451Smsmith * SUCH DAMAGE.
3738451Smsmith *
3838451Smsmith *	@(#)subr_prf.c	8.3 (Berkeley) 1/21/94
3938451Smsmith */
4038451Smsmith
4184221Sdillon#include <sys/cdefs.h>
4284221Sdillon__FBSDID("$FreeBSD: head/lib/libstand/printf.c 156518 2006-03-09 22:37:34Z jkim $");
4384221Sdillon
4438451Smsmith/*
4538451Smsmith * Standaloneified version of the FreeBSD kernel printf family.
4638451Smsmith */
4738451Smsmith
4838451Smsmith#include <sys/types.h>
49113159Speter#include <sys/stddef.h>
50113159Speter#include <sys/stdint.h>
51103949Smike#include <limits.h>
5255137Speter#include <string.h>
5338451Smsmith#include "stand.h"
5438451Smsmith
5538451Smsmith/*
5638451Smsmith * Note that stdarg.h and the ANSI style va_start macro is used for both
5738451Smsmith * ANSI and traditional C compilers.
5838451Smsmith */
5938451Smsmith#include <machine/stdarg.h>
6038451Smsmith
61113159Speter#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
62113159Speter
63156518Sjkimstatic char	*ksprintn (char *buf, uintmax_t num, int base, int *len, int upper);
6438451Smsmithstatic int	kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap);
6538451Smsmith
6638451Smsmithint
6738451Smsmithprintf(const char *fmt, ...)
6838451Smsmith{
6938451Smsmith	va_list ap;
7038451Smsmith	int retval;
7138451Smsmith
7238451Smsmith	va_start(ap, fmt);
7338451Smsmith	retval = kvprintf(fmt, putchar, NULL, 10, ap);
7438451Smsmith	va_end(ap);
7538451Smsmith	return retval;
7638451Smsmith}
7738451Smsmith
7838451Smsmithvoid
7938451Smsmithvprintf(const char *fmt, va_list ap)
8038451Smsmith{
8138451Smsmith
8238451Smsmith	kvprintf(fmt, putchar, NULL, 10, ap);
8338451Smsmith}
8438451Smsmith
8538451Smsmithint
8638451Smsmithsprintf(char *buf, const char *cfmt, ...)
8738451Smsmith{
8838451Smsmith	int retval;
8938451Smsmith	va_list ap;
9038451Smsmith
9138451Smsmith	va_start(ap, cfmt);
9238451Smsmith	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
9338451Smsmith	buf[retval] = '\0';
9438451Smsmith	va_end(ap);
9538451Smsmith	return retval;
9638451Smsmith}
9738451Smsmith
9840805Smsmithvoid
9940805Smsmithvsprintf(char *buf, const char *cfmt, va_list ap)
10040805Smsmith{
10140805Smsmith	int	retval;
10240805Smsmith
10340805Smsmith	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
10440805Smsmith	buf[retval] = '\0';
10540805Smsmith}
10640805Smsmith
10738451Smsmith/*
108113159Speter * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
109113159Speter * order; return an optional length and a pointer to the last character
110113159Speter * written in the buffer (i.e., the first character of the string).
111113159Speter * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
11238451Smsmith */
11338451Smsmithstatic char *
114156518Sjkimksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
115113159Speter{
116156518Sjkim	char *p, c;
11738451Smsmith
118113159Speter	p = nbuf;
119113159Speter	*p = '\0';
12038451Smsmith	do {
121156518Sjkim		c = hex2ascii(num % base);
122156518Sjkim		*++p = upper ? toupper(c) : c;
123113159Speter	} while (num /= base);
12438451Smsmith	if (lenp)
125113159Speter		*lenp = p - nbuf;
12638451Smsmith	return (p);
12738451Smsmith}
12838451Smsmith
12938451Smsmith/*
13038451Smsmith * Scaled down version of printf(3).
13138451Smsmith *
13238451Smsmith * Two additional formats:
13338451Smsmith *
13438451Smsmith * The format %b is supported to decode error registers.
13538451Smsmith * Its usage is:
13638451Smsmith *
13738451Smsmith *	printf("reg=%b\n", regval, "<base><arg>*");
13838451Smsmith *
13938451Smsmith * where <base> is the output base expressed as a control character, e.g.
14038451Smsmith * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
14138451Smsmith * the first of which gives the bit number to be inspected (origin 1), and
14238451Smsmith * the next characters (up to a control character, i.e. a character <= 32),
14338451Smsmith * give the name of the register.  Thus:
14438451Smsmith *
14538451Smsmith *	kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
14638451Smsmith *
14738451Smsmith * would produce output:
14838451Smsmith *
14938451Smsmith *	reg=3<BITTWO,BITONE>
15038451Smsmith *
15138451Smsmith * XXX:  %D  -- Hexdump, takes pointer and separator string:
15238451Smsmith *		("%6D", ptr, ":")   -> XX:XX:XX:XX:XX:XX
15338451Smsmith *		("%*D", len, ptr, " " -> XX XX XX XX ...
15438451Smsmith */
15538451Smsmithstatic int
15638451Smsmithkvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap)
15738451Smsmith{
15838451Smsmith#define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; }
159113159Speter	char nbuf[MAXNBUF];
160113159Speter	char *d;
161113159Speter	const char *p, *percent, *q;
16238451Smsmith	u_char *up;
16338451Smsmith	int ch, n;
164113159Speter	uintmax_t num;
165113159Speter	int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
166113159Speter	int jflag, tflag, zflag;
167156518Sjkim	int dwidth, upper;
16838451Smsmith	char padc;
16938451Smsmith	int retval = 0;
17038451Smsmith
171113159Speter	num = 0;
17238451Smsmith	if (!func)
17338451Smsmith		d = (char *) arg;
17438451Smsmith	else
17538451Smsmith		d = NULL;
17638451Smsmith
17738451Smsmith	if (fmt == NULL)
17838451Smsmith		fmt = "(fmt null)\n";
17938451Smsmith
18038451Smsmith	if (radix < 2 || radix > 36)
18138451Smsmith		radix = 10;
18238451Smsmith
18338451Smsmith	for (;;) {
18438451Smsmith		padc = ' ';
18538451Smsmith		width = 0;
18638451Smsmith		while ((ch = (u_char)*fmt++) != '%') {
187113159Speter			if (ch == '\0')
188113159Speter				return (retval);
18938451Smsmith			PCHAR(ch);
19038451Smsmith		}
191113159Speter		percent = fmt - 1;
192113159Speter		qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
193156518Sjkim		sign = 0; dot = 0; dwidth = 0; upper = 0;
194113159Speter		jflag = 0; tflag = 0; zflag = 0;
19538451Smsmithreswitch:	switch (ch = (u_char)*fmt++) {
19638451Smsmith		case '.':
19738451Smsmith			dot = 1;
19838451Smsmith			goto reswitch;
19938451Smsmith		case '#':
20038451Smsmith			sharpflag = 1;
20138451Smsmith			goto reswitch;
20238451Smsmith		case '+':
20338451Smsmith			sign = 1;
20438451Smsmith			goto reswitch;
20538451Smsmith		case '-':
20638451Smsmith			ladjust = 1;
20738451Smsmith			goto reswitch;
20838451Smsmith		case '%':
20938451Smsmith			PCHAR(ch);
21038451Smsmith			break;
21138451Smsmith		case '*':
21238451Smsmith			if (!dot) {
21338451Smsmith				width = va_arg(ap, int);
21438451Smsmith				if (width < 0) {
21538451Smsmith					ladjust = !ladjust;
21638451Smsmith					width = -width;
21738451Smsmith				}
21838451Smsmith			} else {
21938451Smsmith				dwidth = va_arg(ap, int);
22038451Smsmith			}
22138451Smsmith			goto reswitch;
22238451Smsmith		case '0':
22338451Smsmith			if (!dot) {
22438451Smsmith				padc = '0';
22538451Smsmith				goto reswitch;
22638451Smsmith			}
22738451Smsmith		case '1': case '2': case '3': case '4':
22838451Smsmith		case '5': case '6': case '7': case '8': case '9':
22938451Smsmith				for (n = 0;; ++fmt) {
23038451Smsmith					n = n * 10 + ch - '0';
23138451Smsmith					ch = *fmt;
23238451Smsmith					if (ch < '0' || ch > '9')
23338451Smsmith						break;
23438451Smsmith				}
23538451Smsmith			if (dot)
23638451Smsmith				dwidth = n;
23738451Smsmith			else
23838451Smsmith				width = n;
23938451Smsmith			goto reswitch;
24038451Smsmith		case 'b':
241113159Speter			num = va_arg(ap, int);
24238451Smsmith			p = va_arg(ap, char *);
243156518Sjkim			for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
24438451Smsmith				PCHAR(*q--);
24538451Smsmith
246113159Speter			if (num == 0)
24738451Smsmith				break;
24838451Smsmith
24938451Smsmith			for (tmp = 0; *p;) {
25038451Smsmith				n = *p++;
251113159Speter				if (num & (1 << (n - 1))) {
25238451Smsmith					PCHAR(tmp ? ',' : '<');
25338451Smsmith					for (; (n = *p) > ' '; ++p)
25438451Smsmith						PCHAR(n);
25538451Smsmith					tmp = 1;
25638451Smsmith				} else
25738451Smsmith					for (; *p > ' '; ++p)
25838451Smsmith						continue;
25938451Smsmith			}
26038451Smsmith			if (tmp)
26138451Smsmith				PCHAR('>');
26238451Smsmith			break;
26338451Smsmith		case 'c':
26438451Smsmith			PCHAR(va_arg(ap, int));
26538451Smsmith			break;
26638451Smsmith		case 'D':
26738451Smsmith			up = va_arg(ap, u_char *);
26838451Smsmith			p = va_arg(ap, char *);
26938451Smsmith			if (!width)
27038451Smsmith				width = 16;
27138451Smsmith			while(width--) {
27238451Smsmith				PCHAR(hex2ascii(*up >> 4));
27338451Smsmith				PCHAR(hex2ascii(*up & 0x0f));
27438451Smsmith				up++;
27538451Smsmith				if (width)
27638451Smsmith					for (q=p;*q;q++)
27738451Smsmith						PCHAR(*q);
27838451Smsmith			}
27938451Smsmith			break;
28038451Smsmith		case 'd':
281113159Speter		case 'i':
282113159Speter			base = 10;
28338451Smsmith			sign = 1;
284113159Speter			goto handle_sign;
285113159Speter		case 'j':
286113159Speter			jflag = 1;
287113159Speter			goto reswitch;
28838451Smsmith		case 'l':
289113159Speter			if (lflag) {
290113159Speter				lflag = 0;
291113159Speter				qflag = 1;
292113159Speter			} else
293113159Speter				lflag = 1;
29438451Smsmith			goto reswitch;
29538451Smsmith		case 'n':
296113159Speter			if (jflag)
297113159Speter				*(va_arg(ap, intmax_t *)) = retval;
298113159Speter			else if (qflag)
299113159Speter				*(va_arg(ap, quad_t *)) = retval;
300113159Speter			else if (lflag)
301113159Speter				*(va_arg(ap, long *)) = retval;
302113159Speter			else if (zflag)
303113159Speter				*(va_arg(ap, size_t *)) = retval;
304113159Speter			else
305113159Speter				*(va_arg(ap, int *)) = retval;
306113159Speter			break;
30738451Smsmith		case 'o':
30838451Smsmith			base = 8;
309113159Speter			goto handle_nosign;
31038451Smsmith		case 'p':
31138451Smsmith			base = 16;
312113159Speter			sharpflag = (width == 0);
313113159Speter			sign = 0;
314113159Speter			num = (uintptr_t)va_arg(ap, void *);
31538451Smsmith			goto number;
316113159Speter		case 'q':
317113159Speter			qflag = 1;
318113159Speter			goto reswitch;
319113159Speter		case 'r':
320113159Speter			base = radix;
321113159Speter			if (sign)
322113159Speter				goto handle_sign;
323113159Speter			goto handle_nosign;
32438451Smsmith		case 's':
32538451Smsmith			p = va_arg(ap, char *);
32638451Smsmith			if (p == NULL)
32738451Smsmith				p = "(null)";
32838451Smsmith			if (!dot)
32938451Smsmith				n = strlen (p);
33038451Smsmith			else
33138451Smsmith				for (n = 0; n < dwidth && p[n]; n++)
33238451Smsmith					continue;
33338451Smsmith
33438451Smsmith			width -= n;
33538451Smsmith
33638451Smsmith			if (!ladjust && width > 0)
33738451Smsmith				while (width--)
33838451Smsmith					PCHAR(padc);
33938451Smsmith			while (n--)
34038451Smsmith				PCHAR(*p++);
34138451Smsmith			if (ladjust && width > 0)
34238451Smsmith				while (width--)
34338451Smsmith					PCHAR(padc);
34438451Smsmith			break;
345113159Speter		case 't':
346113159Speter			tflag = 1;
347113159Speter			goto reswitch;
34838451Smsmith		case 'u':
34938451Smsmith			base = 10;
350113159Speter			goto handle_nosign;
351156518Sjkim		case 'X':
352156518Sjkim			upper = 1;
35338451Smsmith		case 'x':
35438451Smsmith			base = 16;
355113159Speter			goto handle_nosign;
356113159Speter		case 'y':
357113159Speter			base = 16;
358113159Speter			sign = 1;
359113159Speter			goto handle_sign;
360113159Speter		case 'z':
361113159Speter			zflag = 1;
362113159Speter			goto reswitch;
363113159Speterhandle_nosign:
364113159Speter			sign = 0;
365113159Speter			if (jflag)
366113159Speter				num = va_arg(ap, uintmax_t);
367113159Speter			else if (qflag)
368113159Speter				num = va_arg(ap, u_quad_t);
369113159Speter			else if (tflag)
370113159Speter				num = va_arg(ap, ptrdiff_t);
371113159Speter			else if (lflag)
372113159Speter				num = va_arg(ap, u_long);
373113159Speter			else if (zflag)
374113159Speter				num = va_arg(ap, size_t);
375113159Speter			else
376113159Speter				num = va_arg(ap, u_int);
377113159Speter			goto number;
378113159Speterhandle_sign:
379113159Speter			if (jflag)
380113159Speter				num = va_arg(ap, intmax_t);
381113159Speter			else if (qflag)
382113159Speter				num = va_arg(ap, quad_t);
383113159Speter			else if (tflag)
384113159Speter				num = va_arg(ap, ptrdiff_t);
385113159Speter			else if (lflag)
386113159Speter				num = va_arg(ap, long);
387113159Speter			else if (zflag)
388113159Speter				num = va_arg(ap, size_t);
389113159Speter			else
390113159Speter				num = va_arg(ap, int);
391113159Speternumber:
392113159Speter			if (sign && (intmax_t)num < 0) {
39338451Smsmith				neg = 1;
394113159Speter				num = -(intmax_t)num;
39538451Smsmith			}
396156518Sjkim			p = ksprintn(nbuf, num, base, &tmp, upper);
397113159Speter			if (sharpflag && num != 0) {
39838451Smsmith				if (base == 8)
39938451Smsmith					tmp++;
40038451Smsmith				else if (base == 16)
40138451Smsmith					tmp += 2;
40238451Smsmith			}
40338451Smsmith			if (neg)
40438451Smsmith				tmp++;
40538451Smsmith
40638451Smsmith			if (!ladjust && width && (width -= tmp) > 0)
40738451Smsmith				while (width--)
40838451Smsmith					PCHAR(padc);
40938451Smsmith			if (neg)
41038451Smsmith				PCHAR('-');
411113159Speter			if (sharpflag && num != 0) {
41238451Smsmith				if (base == 8) {
41338451Smsmith					PCHAR('0');
41438451Smsmith				} else if (base == 16) {
41538451Smsmith					PCHAR('0');
41638451Smsmith					PCHAR('x');
41738451Smsmith				}
41838451Smsmith			}
41938451Smsmith
42038451Smsmith			while (*p)
42138451Smsmith				PCHAR(*p--);
42238451Smsmith
42338451Smsmith			if (ladjust && width && (width -= tmp) > 0)
42438451Smsmith				while (width--)
42538451Smsmith					PCHAR(padc);
42638451Smsmith
42738451Smsmith			break;
42838451Smsmith		default:
429113159Speter			while (percent < fmt)
430113159Speter				PCHAR(*percent++);
43138451Smsmith			break;
43238451Smsmith		}
43338451Smsmith	}
43438451Smsmith#undef PCHAR
43538451Smsmith}
436