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