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 * 4. 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__FBSDID("$FreeBSD$");
39
40/*
41 * Standaloneified version of the FreeBSD kernel printf family.
42 */
43
44#include <sys/types.h>
45#include <sys/stddef.h>
46#include <sys/stdint.h>
47#include <limits.h>
48#include <string.h>
49#include "stand.h"
50
51/*
52 * Note that stdarg.h and the ANSI style va_start macro is used for both
53 * ANSI and traditional C compilers.
54 */
55#include <machine/stdarg.h>
56
57#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1)
58
59static char	*ksprintn (char *buf, uintmax_t num, int base, int *len, int upper);
60static int	kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap);
61
62int
63printf(const char *fmt, ...)
64{
65	va_list ap;
66	int retval;
67
68	va_start(ap, fmt);
69	retval = kvprintf(fmt, putchar, NULL, 10, ap);
70	va_end(ap);
71	return retval;
72}
73
74void
75vprintf(const char *fmt, va_list ap)
76{
77
78	kvprintf(fmt, putchar, NULL, 10, ap);
79}
80
81int
82sprintf(char *buf, const char *cfmt, ...)
83{
84	int retval;
85	va_list ap;
86
87	va_start(ap, cfmt);
88	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
89	buf[retval] = '\0';
90	va_end(ap);
91	return retval;
92}
93
94void
95vsprintf(char *buf, const char *cfmt, va_list ap)
96{
97	int	retval;
98
99	retval = kvprintf(cfmt, NULL, (void *)buf, 10, ap);
100	buf[retval] = '\0';
101}
102
103/*
104 * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse
105 * order; return an optional length and a pointer to the last character
106 * written in the buffer (i.e., the first character of the string).
107 * The buffer pointed to by `nbuf' must have length >= MAXNBUF.
108 */
109static char *
110ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
111{
112	char *p, c;
113
114	p = nbuf;
115	*p = '\0';
116	do {
117		c = hex2ascii(num % base);
118		*++p = upper ? toupper(c) : c;
119	} while (num /= base);
120	if (lenp)
121		*lenp = p - nbuf;
122	return (p);
123}
124
125/*
126 * Scaled down version of printf(3).
127 *
128 * Two additional formats:
129 *
130 * The format %b is supported to decode error registers.
131 * Its usage is:
132 *
133 *	printf("reg=%b\n", regval, "<base><arg>*");
134 *
135 * where <base> is the output base expressed as a control character, e.g.
136 * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
137 * the first of which gives the bit number to be inspected (origin 1), and
138 * the next characters (up to a control character, i.e. a character <= 32),
139 * give the name of the register.  Thus:
140 *
141 *	kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
142 *
143 * would produce output:
144 *
145 *	reg=3<BITTWO,BITONE>
146 *
147 * XXX:  %D  -- Hexdump, takes pointer and separator string:
148 *		("%6D", ptr, ":")   -> XX:XX:XX:XX:XX:XX
149 *		("%*D", len, ptr, " " -> XX XX XX XX ...
150 */
151static int
152kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap)
153{
154#define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; }
155	char nbuf[MAXNBUF];
156	char *d;
157	const char *p, *percent, *q;
158	u_char *up;
159	int ch, n;
160	uintmax_t num;
161	int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
162	int cflag, hflag, jflag, tflag, zflag;
163	int dwidth, upper;
164	char padc;
165	int stop = 0, retval = 0;
166
167	num = 0;
168	if (!func)
169		d = (char *) arg;
170	else
171		d = NULL;
172
173	if (fmt == NULL)
174		fmt = "(fmt null)\n";
175
176	if (radix < 2 || radix > 36)
177		radix = 10;
178
179	for (;;) {
180		padc = ' ';
181		width = 0;
182		while ((ch = (u_char)*fmt++) != '%' || stop) {
183			if (ch == '\0')
184				return (retval);
185			PCHAR(ch);
186		}
187		percent = fmt - 1;
188		qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
189		sign = 0; dot = 0; dwidth = 0; upper = 0;
190		cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
191reswitch:	switch (ch = (u_char)*fmt++) {
192		case '.':
193			dot = 1;
194			goto reswitch;
195		case '#':
196			sharpflag = 1;
197			goto reswitch;
198		case '+':
199			sign = 1;
200			goto reswitch;
201		case '-':
202			ladjust = 1;
203			goto reswitch;
204		case '%':
205			PCHAR(ch);
206			break;
207		case '*':
208			if (!dot) {
209				width = va_arg(ap, int);
210				if (width < 0) {
211					ladjust = !ladjust;
212					width = -width;
213				}
214			} else {
215				dwidth = va_arg(ap, int);
216			}
217			goto reswitch;
218		case '0':
219			if (!dot) {
220				padc = '0';
221				goto reswitch;
222			}
223		case '1': case '2': case '3': case '4':
224		case '5': case '6': case '7': case '8': case '9':
225				for (n = 0;; ++fmt) {
226					n = n * 10 + ch - '0';
227					ch = *fmt;
228					if (ch < '0' || ch > '9')
229						break;
230				}
231			if (dot)
232				dwidth = n;
233			else
234				width = n;
235			goto reswitch;
236		case 'b':
237			num = (u_int)va_arg(ap, int);
238			p = va_arg(ap, char *);
239			for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
240				PCHAR(*q--);
241
242			if (num == 0)
243				break;
244
245			for (tmp = 0; *p;) {
246				n = *p++;
247				if (num & (1 << (n - 1))) {
248					PCHAR(tmp ? ',' : '<');
249					for (; (n = *p) > ' '; ++p)
250						PCHAR(n);
251					tmp = 1;
252				} else
253					for (; *p > ' '; ++p)
254						continue;
255			}
256			if (tmp)
257				PCHAR('>');
258			break;
259		case 'c':
260			PCHAR(va_arg(ap, int));
261			break;
262		case 'D':
263			up = va_arg(ap, u_char *);
264			p = va_arg(ap, char *);
265			if (!width)
266				width = 16;
267			while(width--) {
268				PCHAR(hex2ascii(*up >> 4));
269				PCHAR(hex2ascii(*up & 0x0f));
270				up++;
271				if (width)
272					for (q=p;*q;q++)
273						PCHAR(*q);
274			}
275			break;
276		case 'd':
277		case 'i':
278			base = 10;
279			sign = 1;
280			goto handle_sign;
281		case 'h':
282			if (hflag) {
283				hflag = 0;
284				cflag = 1;
285			} else
286				hflag = 1;
287			goto reswitch;
288		case 'j':
289			jflag = 1;
290			goto reswitch;
291		case 'l':
292			if (lflag) {
293				lflag = 0;
294				qflag = 1;
295			} else
296				lflag = 1;
297			goto reswitch;
298		case 'n':
299			if (jflag)
300				*(va_arg(ap, intmax_t *)) = retval;
301			else if (qflag)
302				*(va_arg(ap, quad_t *)) = retval;
303			else if (lflag)
304				*(va_arg(ap, long *)) = retval;
305			else if (zflag)
306				*(va_arg(ap, size_t *)) = retval;
307			else if (hflag)
308				*(va_arg(ap, short *)) = retval;
309			else if (cflag)
310				*(va_arg(ap, char *)) = retval;
311			else
312				*(va_arg(ap, int *)) = retval;
313			break;
314		case 'o':
315			base = 8;
316			goto handle_nosign;
317		case 'p':
318			base = 16;
319			sharpflag = (width == 0);
320			sign = 0;
321			num = (uintptr_t)va_arg(ap, void *);
322			goto number;
323		case 'q':
324			qflag = 1;
325			goto reswitch;
326		case 'r':
327			base = radix;
328			if (sign)
329				goto handle_sign;
330			goto handle_nosign;
331		case 's':
332			p = va_arg(ap, char *);
333			if (p == NULL)
334				p = "(null)";
335			if (!dot)
336				n = strlen (p);
337			else
338				for (n = 0; n < dwidth && p[n]; n++)
339					continue;
340
341			width -= n;
342
343			if (!ladjust && width > 0)
344				while (width--)
345					PCHAR(padc);
346			while (n--)
347				PCHAR(*p++);
348			if (ladjust && width > 0)
349				while (width--)
350					PCHAR(padc);
351			break;
352		case 't':
353			tflag = 1;
354			goto reswitch;
355		case 'u':
356			base = 10;
357			goto handle_nosign;
358		case 'X':
359			upper = 1;
360		case 'x':
361			base = 16;
362			goto handle_nosign;
363		case 'y':
364			base = 16;
365			sign = 1;
366			goto handle_sign;
367		case 'z':
368			zflag = 1;
369			goto reswitch;
370handle_nosign:
371			sign = 0;
372			if (jflag)
373				num = va_arg(ap, uintmax_t);
374			else if (qflag)
375				num = va_arg(ap, u_quad_t);
376			else if (tflag)
377				num = va_arg(ap, ptrdiff_t);
378			else if (lflag)
379				num = va_arg(ap, u_long);
380			else if (zflag)
381				num = va_arg(ap, size_t);
382			else if (hflag)
383				num = (u_short)va_arg(ap, int);
384			else if (cflag)
385				num = (u_char)va_arg(ap, int);
386			else
387				num = va_arg(ap, u_int);
388			goto number;
389handle_sign:
390			if (jflag)
391				num = va_arg(ap, intmax_t);
392			else if (qflag)
393				num = va_arg(ap, quad_t);
394			else if (tflag)
395				num = va_arg(ap, ptrdiff_t);
396			else if (lflag)
397				num = va_arg(ap, long);
398			else if (zflag)
399				num = va_arg(ap, ssize_t);
400			else if (hflag)
401				num = (short)va_arg(ap, int);
402			else if (cflag)
403				num = (char)va_arg(ap, int);
404			else
405				num = va_arg(ap, int);
406number:
407			if (sign && (intmax_t)num < 0) {
408				neg = 1;
409				num = -(intmax_t)num;
410			}
411			p = ksprintn(nbuf, num, base, &n, upper);
412			tmp = 0;
413			if (sharpflag && num != 0) {
414				if (base == 8)
415					tmp++;
416				else if (base == 16)
417					tmp += 2;
418			}
419			if (neg)
420				tmp++;
421
422			if (!ladjust && padc == '0')
423				dwidth = width - tmp;
424			width -= tmp + imax(dwidth, n);
425			dwidth -= n;
426			if (!ladjust)
427				while (width-- > 0)
428					PCHAR(' ');
429			if (neg)
430				PCHAR('-');
431			if (sharpflag && num != 0) {
432				if (base == 8) {
433					PCHAR('0');
434				} else if (base == 16) {
435					PCHAR('0');
436					PCHAR('x');
437				}
438			}
439			while (dwidth-- > 0)
440				PCHAR('0');
441
442			while (*p)
443				PCHAR(*p--);
444
445			if (ladjust)
446				while (width-- > 0)
447					PCHAR(' ');
448
449			break;
450		default:
451			while (percent < fmt)
452				PCHAR(*percent++);
453			/*
454			 * Since we ignore an formatting argument it is no
455			 * longer safe to obey the remaining formatting
456			 * arguments as the arguments will no longer match
457			 * the format specs.
458			 */
459			stop = 1;
460			break;
461		}
462	}
463#undef PCHAR
464}
465