xref: /openbsd/sys/lib/libsa/printf.c (revision 4bdff4be)
1 /*	$OpenBSD: printf.c,v 1.29 2019/05/11 16:56:47 deraadt Exp $	*/
2 /*	$NetBSD: printf.c,v 1.10 1996/11/30 04:19:21 gwr Exp $	*/
3 
4 /*-
5  * Copyright (c) 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *	@(#)printf.c	8.1 (Berkeley) 6/11/93
33  */
34 
35 /*
36  * Scaled down version of printf(3).
37  *
38  * One additional format:
39  *
40  * The format %b is supported to decode error registers.
41  * Its usage is:
42  *
43  *	printf("reg=%b\n", regval, "<base><arg>*");
44  *
45  * where <base> is the output base expressed as a control character, e.g.
46  * \10 gives octal; \20 gives hex.  Each arg is a sequence of characters,
47  * the first of which gives the bit number to be inspected (origin 1), and
48  * the next characters (up to a control character, i.e. a character <= 32),
49  * give the name of the register.  Thus:
50  *
51  *	printf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
52  *
53  * would produce output:
54  *
55  *	reg=3<BITTWO,BITONE>
56  */
57 
58 #include <sys/types.h>
59 #include <sys/stdarg.h>
60 
61 #include "stand.h"
62 
63 /*
64  * macros for converting digits to letters and vice versa
65  */
66 #define	to_digit(c)	((c) - '0')
67 #define	is_digit(c)	((unsigned)to_digit(c) <= 9)
68 #define	to_char(n)	((n) + '0')
69 
70 void kprintn(void (*)(int), u_long, int, int, char);
71 #ifdef LIBSA_LONGLONG_PRINTF
72 void kprintn64(void (*)(int), u_int64_t, int, int, char);
73 #endif
74 void kdoprnt(void (*)(int), const char *, va_list);
75 
76 const char hexdig[] = "0123456789abcdef";
77 
78 void
79 printf(const char *fmt, ...)
80 {
81 	va_list ap;
82 
83 	va_start(ap, fmt);
84 	kdoprnt(putchar, fmt, ap);
85 	va_end(ap);
86 }
87 
88 void
89 vprintf(const char *fmt, va_list ap)
90 {
91 	kdoprnt(putchar, fmt, ap);
92 }
93 
94 void
95 kdoprnt(void (*put)(int), const char *fmt, va_list ap)
96 {
97 #ifdef LIBSA_LONGLONG_PRINTF
98 	u_int64_t ull;
99 #endif
100 	unsigned long ul;
101 	int ch, lflag, width, n;
102 	char *p, padchar;
103 
104 	for (;;) {
105 		while ((ch = *fmt++) != '%') {
106 			if (ch == '\0')
107 				return;
108 			put(ch);
109 		}
110 		lflag = 0;
111 		padchar = ' ';
112 		width = 0;
113 rflag:		ch = *fmt++;
114 reswitch:	switch (ch) {
115 		case '0':
116 			/*
117 			 * ``Note that 0 is taken as a flag, not as the
118 			 * beginning of a field width.''
119 			 *	-- ANSI X3J11
120 			 */
121 			padchar = '0';
122 			goto rflag;
123 		case '1': case '2': case '3': case '4':
124 		case '5': case '6': case '7': case '8': case '9':
125 			n = 0;
126 			do {
127 				n = 10 * n + to_digit(ch);
128 				ch = *fmt++;
129 			} while (is_digit(ch));
130 			width = n;
131 			goto reswitch;
132 		case 'l':
133 			lflag++;
134 			goto rflag;
135 		case 'b':
136 		{
137 			int set, n;
138 
139 			ul = va_arg(ap, int);
140 			p = va_arg(ap, char *);
141 			kprintn(put, ul, *p++, width, padchar);
142 
143 			if (!ul)
144 				break;
145 
146 			for (set = 0; (n = *p++);) {
147 				if (ul & (1 << (n - 1))) {
148 					put(set ? ',' : '<');
149 					for (; (n = *p) > ' '; ++p)
150 						put(n);
151 					set = 1;
152 				} else
153 					for (; *p > ' '; ++p)
154 						;
155 			}
156 			if (set)
157 				put('>');
158 		}
159 			break;
160 		case 'c':
161 			ch = va_arg(ap, int);
162 			put(ch & 0x7f);
163 			break;
164 		case 's':
165 			p = va_arg(ap, char *);
166 			while ((ch = *p++))
167 				put(ch);
168 			break;
169 		case 'd':
170 #ifdef LIBSA_LONGLONG_PRINTF
171 			if (lflag > 1) {
172 				ull = va_arg(ap, int64_t);
173 				if ((int64_t)ull < 0) {
174 					put('-');
175 					ull = -(int64_t)ull;
176 				}
177 				kprintn64(put, ull, 10, width, padchar);
178 				break;
179 			}
180 #endif
181 			ul = lflag ?
182 			    va_arg(ap, long) : va_arg(ap, int);
183 			if ((long)ul < 0) {
184 				put('-');
185 				ul = -(long)ul;
186 			}
187 			kprintn(put, ul, 10, width, padchar);
188 			break;
189 		case 'o':
190 #ifdef LIBSA_LONGLONG_PRINTF
191 			if (lflag > 1) {
192 				ull = va_arg(ap, u_int64_t);
193 				kprintn64(put, ull, 8, width, padchar);
194 				break;
195 			}
196 #endif
197 			ul = lflag ?
198 			    va_arg(ap, u_long) : va_arg(ap, u_int);
199 			kprintn(put, ul, 8, width, padchar);
200 			break;
201 		case 'u':
202 #ifdef LIBSA_LONGLONG_PRINTF
203 			if (lflag > 1) {
204 				ull = va_arg(ap, u_int64_t);
205 				kprintn64(put, ull, 10, width, padchar);
206 				break;
207 			}
208 #endif
209 			ul = lflag ?
210 			    va_arg(ap, u_long) : va_arg(ap, u_int);
211 			kprintn(put, ul, 10, width, padchar);
212 			break;
213 		case 'p':
214 			put('0');
215 			put('x');
216 			lflag += sizeof(void *)==sizeof(u_long)? 1 : 0;
217 		case 'x':
218 #ifdef LIBSA_LONGLONG_PRINTF
219 			if (lflag > 1) {
220 				ull = va_arg(ap, u_int64_t);
221 				kprintn64(put, ull, 16, width, padchar);
222 				break;
223 			}
224 #else
225  			if (lflag > 1) {
226 				/* hold an int64_t in base 16 */
227 				char *p, buf[(sizeof(u_int64_t) * NBBY / 4) + 1];
228 				u_int64_t ull;
229 
230  				ull = va_arg(ap, u_int64_t);
231 				p = buf;
232 				do {
233 					*p++ = hexdig[ull & 15];
234 				} while (ull >>= 4);
235 				while ((p - buf) < width &&
236 				    (p - buf) < sizeof(buf)) {
237 					*p++ = padchar;
238 				}
239 				do {
240 					put(*--p);
241 				} while (p > buf);
242  				break;
243  			}
244 #endif
245 			ul = lflag ?
246 			    va_arg(ap, u_long) : va_arg(ap, u_int);
247 			kprintn(put, ul, 16, width, padchar);
248 			break;
249 		default:
250 			put('%');
251 #ifdef LIBSA_LONGLONG_PRINTF
252 			while (--lflag)
253 #else
254 			if (lflag)
255 #endif
256 				put('l');
257 			put(ch);
258 		}
259 	}
260 }
261 
262 void
263 kprintn(void (*put)(int), unsigned long ul, int base, int width, char padchar)
264 {
265 	/* hold a long in base 8 */
266 	char *p, buf[(sizeof(long) * NBBY / 3) + 1];
267 
268 	p = buf;
269 	do {
270 		*p++ = hexdig[ul % base];
271 	} while (ul /= base);
272 	while ((p - buf) < width && (p - buf) < sizeof(buf)) {
273 		*p++ = padchar;
274 	}
275 	do {
276 		put(*--p);
277 	} while (p > buf);
278 }
279 
280 #ifdef LIBSA_LONGLONG_PRINTF
281 void
282 kprintn64(void (*put)(int), u_int64_t ull, int base, int width, char padchar)
283 {
284 	/* hold an int64_t in base 8 */
285 	char *p, buf[(sizeof(u_int64_t) * NBBY / 3) + 1];
286 
287 	p = buf;
288 	do {
289 		*p++ = hexdig[ull % base];
290 	} while (ull /= base);
291 	while ((p - buf) < width && (p - buf) < sizeof(buf)) {
292 		*p++ = padchar;
293 	}
294 	do {
295 		put(*--p);
296 	} while (p > buf);
297 }
298 #endif
299 
300 int donottwiddle = 0;
301 
302 void
303 twiddle(void)
304 {
305 	static int pos;
306 
307 	if (!donottwiddle) {
308 		putchar("|/-\\"[pos++ & 3]);
309 		putchar('\b');
310 	}
311 }
312