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