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