printf.c revision 84221
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. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the University of
21 *	California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 *    may be used to endorse or promote products derived from this software
24 *    without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 *
38 *	@(#)subr_prf.c	8.3 (Berkeley) 1/21/94
39 */
40
41#include <sys/cdefs.h>
42__FBSDID("$FreeBSD: head/lib/libstand/printf.c 84221 2001-09-30 22:28:01Z dillon $");
43
44/*
45 * Standaloneified version of the FreeBSD kernel printf family.
46 */
47
48#include <sys/types.h>
49#include <string.h>
50#include "stand.h"
51
52/*
53 * Note that stdarg.h and the ANSI style va_start macro is used for both
54 * ANSI and traditional C compilers.
55 */
56#include <machine/stdarg.h>
57
58static char	*ksprintn (u_long num, int base, int *len);
59static int	kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap);
60
61int
62printf(const char *fmt, ...)
63{
64	va_list ap;
65	int retval;
66
67	va_start(ap, fmt);
68	retval = kvprintf(fmt, putchar, NULL, 10, ap);
69	va_end(ap);
70	return retval;
71}
72
73void
74vprintf(const char *fmt, va_list ap)
75{
76
77	kvprintf(fmt, putchar, NULL, 10, ap);
78}
79
80int
81sprintf(char *buf, const char *cfmt, ...)
82{
83	int retval;
84	va_list ap;
85
86	va_start(ap, cfmt);
87	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
88	buf[retval] = '\0';
89	va_end(ap);
90	return retval;
91}
92
93void
94vsprintf(char *buf, const char *cfmt, va_list ap)
95{
96	int	retval;
97
98	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
99	buf[retval] = '\0';
100}
101
102/*
103 * Put a number (base <= 16) in a buffer in reverse order; return an
104 * optional length and a pointer to the NULL terminated (preceded?)
105 * buffer.
106 */
107static char *
108ksprintn(ul, base, lenp)
109	register u_long ul;
110	register int base, *lenp;
111{					/* A long in base 8, plus NULL. */
112	static char buf[sizeof(long) * NBBY / 3 + 2];
113	register char *p;
114
115	p = buf;
116	do {
117		*++p = hex2ascii(ul % base);
118	} while (ul /= base);
119	if (lenp)
120		*lenp = p - buf;
121	return (p);
122}
123
124/*
125 * Scaled down version of printf(3).
126 *
127 * Two additional formats:
128 *
129 * The format %b is supported to decode error registers.
130 * Its usage is:
131 *
132 *	printf("reg=%b\n", regval, "<base><arg>*");
133 *
134 * where <base> is the output base expressed as a control character, e.g.
135 * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
136 * the first of which gives the bit number to be inspected (origin 1), and
137 * the next characters (up to a control character, i.e. a character <= 32),
138 * give the name of the register.  Thus:
139 *
140 *	kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
141 *
142 * would produce output:
143 *
144 *	reg=3<BITTWO,BITONE>
145 *
146 * XXX:  %D  -- Hexdump, takes pointer and separator string:
147 *		("%6D", ptr, ":")   -> XX:XX:XX:XX:XX:XX
148 *		("%*D", len, ptr, " " -> XX XX XX XX ...
149 */
150static int
151kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap)
152{
153#define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; }
154	char *p, *q, *d;
155	u_char *up;
156	int ch, n;
157	u_long ul;
158	int base, lflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
159	int dwidth;
160	char padc;
161	int retval = 0;
162
163	if (!func)
164		d = (char *) arg;
165	else
166		d = NULL;
167
168	if (fmt == NULL)
169		fmt = "(fmt null)\n";
170
171	if (radix < 2 || radix > 36)
172		radix = 10;
173
174	for (;;) {
175		padc = ' ';
176		width = 0;
177		while ((ch = (u_char)*fmt++) != '%') {
178			if (ch == '\0')
179				return retval;
180			PCHAR(ch);
181		}
182		lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
183		sign = 0; dot = 0; dwidth = 0;
184reswitch:	switch (ch = (u_char)*fmt++) {
185		case '.':
186			dot = 1;
187			goto reswitch;
188		case '#':
189			sharpflag = 1;
190			goto reswitch;
191		case '+':
192			sign = 1;
193			goto reswitch;
194		case '-':
195			ladjust = 1;
196			goto reswitch;
197		case '%':
198			PCHAR(ch);
199			break;
200		case '*':
201			if (!dot) {
202				width = va_arg(ap, int);
203				if (width < 0) {
204					ladjust = !ladjust;
205					width = -width;
206				}
207			} else {
208				dwidth = va_arg(ap, int);
209			}
210			goto reswitch;
211		case '0':
212			if (!dot) {
213				padc = '0';
214				goto reswitch;
215			}
216		case '1': case '2': case '3': case '4':
217		case '5': case '6': case '7': case '8': case '9':
218				for (n = 0;; ++fmt) {
219					n = n * 10 + ch - '0';
220					ch = *fmt;
221					if (ch < '0' || ch > '9')
222						break;
223				}
224			if (dot)
225				dwidth = n;
226			else
227				width = n;
228			goto reswitch;
229		case 'b':
230			ul = va_arg(ap, int);
231			p = va_arg(ap, char *);
232			for (q = ksprintn(ul, *p++, NULL); *q;)
233				PCHAR(*q--);
234
235			if (!ul)
236				break;
237
238			for (tmp = 0; *p;) {
239				n = *p++;
240				if (ul & (1 << (n - 1))) {
241					PCHAR(tmp ? ',' : '<');
242					for (; (n = *p) > ' '; ++p)
243						PCHAR(n);
244					tmp = 1;
245				} else
246					for (; *p > ' '; ++p)
247						continue;
248			}
249			if (tmp)
250				PCHAR('>');
251			break;
252		case 'c':
253			PCHAR(va_arg(ap, int));
254			break;
255		case 'D':
256			up = va_arg(ap, u_char *);
257			p = va_arg(ap, char *);
258			if (!width)
259				width = 16;
260			while(width--) {
261				PCHAR(hex2ascii(*up >> 4));
262				PCHAR(hex2ascii(*up & 0x0f));
263				up++;
264				if (width)
265					for (q=p;*q;q++)
266						PCHAR(*q);
267			}
268			break;
269		case 'd':
270			ul = lflag ? va_arg(ap, long) : va_arg(ap, int);
271			sign = 1;
272			base = 10;
273			goto number;
274		case 'l':
275			lflag = 1;
276			goto reswitch;
277		case 'n':
278			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
279			base = radix;
280			goto number;
281		case 'o':
282			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
283			base = 8;
284			goto number;
285		case 'p':
286			ul = (u_long)va_arg(ap, void *);
287			base = 16;
288			sharpflag = 1;
289			goto number;
290		case 's':
291			p = va_arg(ap, char *);
292			if (p == NULL)
293				p = "(null)";
294			if (!dot)
295				n = strlen (p);
296			else
297				for (n = 0; n < dwidth && p[n]; n++)
298					continue;
299
300			width -= n;
301
302			if (!ladjust && width > 0)
303				while (width--)
304					PCHAR(padc);
305			while (n--)
306				PCHAR(*p++);
307			if (ladjust && width > 0)
308				while (width--)
309					PCHAR(padc);
310			break;
311		case 'u':
312			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
313			base = 10;
314			goto number;
315		case 'x':
316			ul = lflag ? va_arg(ap, u_long) : va_arg(ap, u_int);
317			base = 16;
318number:			if (sign && (long)ul < 0L) {
319				neg = 1;
320				ul = -(long)ul;
321			}
322			p = ksprintn(ul, base, &tmp);
323			if (sharpflag && ul != 0) {
324				if (base == 8)
325					tmp++;
326				else if (base == 16)
327					tmp += 2;
328			}
329			if (neg)
330				tmp++;
331
332			if (!ladjust && width && (width -= tmp) > 0)
333				while (width--)
334					PCHAR(padc);
335			if (neg)
336				PCHAR('-');
337			if (sharpflag && ul != 0) {
338				if (base == 8) {
339					PCHAR('0');
340				} else if (base == 16) {
341					PCHAR('0');
342					PCHAR('x');
343				}
344			}
345
346			while (*p)
347				PCHAR(*p--);
348
349			if (ladjust && width && (width -= tmp) > 0)
350				while (width--)
351					PCHAR(padc);
352
353			break;
354		default:
355			PCHAR('%');
356			if (lflag)
357				PCHAR('l');
358			PCHAR(ch);
359			break;
360		}
361	}
362#undef PCHAR
363}
364
365