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