xref: /openbsd-src/sys/lib/libsa/printf.c (revision 9f11ffb7133c203312a01e4b986886bc88c7d74b)
1 /*	$OpenBSD: printf.c,v 1.28 2018/01/17 08:46:15 patrick 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 #ifndef	STRIPPED
136 		case 'b':
137 		{
138 			int set, n;
139 
140 			ul = va_arg(ap, int);
141 			p = va_arg(ap, char *);
142 			kprintn(put, ul, *p++, width, padchar);
143 
144 			if (!ul)
145 				break;
146 
147 			for (set = 0; (n = *p++);) {
148 				if (ul & (1 << (n - 1))) {
149 					put(set ? ',' : '<');
150 					for (; (n = *p) > ' '; ++p)
151 						put(n);
152 					set = 1;
153 				} else
154 					for (; *p > ' '; ++p)
155 						;
156 			}
157 			if (set)
158 				put('>');
159 		}
160 			break;
161 #endif
162 		case 'c':
163 			ch = va_arg(ap, int);
164 			put(ch & 0x7f);
165 			break;
166 		case 's':
167 			p = va_arg(ap, char *);
168 			while ((ch = *p++))
169 				put(ch);
170 			break;
171 		case 'd':
172 #ifdef LIBSA_LONGLONG_PRINTF
173 			if (lflag > 1) {
174 				ull = va_arg(ap, int64_t);
175 				if ((int64_t)ull < 0) {
176 					put('-');
177 					ull = -(int64_t)ull;
178 				}
179 				kprintn64(put, ull, 10, width, padchar);
180 				break;
181 			}
182 #endif
183 			ul = lflag ?
184 			    va_arg(ap, long) : va_arg(ap, int);
185 			if ((long)ul < 0) {
186 				put('-');
187 				ul = -(long)ul;
188 			}
189 			kprintn(put, ul, 10, width, padchar);
190 			break;
191 		case 'o':
192 #ifdef LIBSA_LONGLONG_PRINTF
193 			if (lflag > 1) {
194 				ull = va_arg(ap, u_int64_t);
195 				kprintn64(put, ull, 8, width, padchar);
196 				break;
197 			}
198 #endif
199 			ul = lflag ?
200 			    va_arg(ap, u_long) : va_arg(ap, u_int);
201 			kprintn(put, ul, 8, width, padchar);
202 			break;
203 		case 'u':
204 #ifdef LIBSA_LONGLONG_PRINTF
205 			if (lflag > 1) {
206 				ull = va_arg(ap, u_int64_t);
207 				kprintn64(put, ull, 10, width, padchar);
208 				break;
209 			}
210 #endif
211 			ul = lflag ?
212 			    va_arg(ap, u_long) : va_arg(ap, u_int);
213 			kprintn(put, ul, 10, width, padchar);
214 			break;
215 		case 'p':
216 			put('0');
217 			put('x');
218 			lflag += sizeof(void *)==sizeof(u_long)? 1 : 0;
219 		case 'x':
220 #ifdef LIBSA_LONGLONG_PRINTF
221 			if (lflag > 1) {
222 				ull = va_arg(ap, u_int64_t);
223 				kprintn64(put, ull, 16, width, padchar);
224 				break;
225 			}
226 #else
227  			if (lflag > 1) {
228 				/* hold an int64_t in base 16 */
229 				char *p, buf[(sizeof(u_int64_t) * NBBY / 4) + 1];
230 				u_int64_t ull;
231 
232  				ull = va_arg(ap, u_int64_t);
233 				p = buf;
234 				do {
235 					*p++ = hexdig[ull & 15];
236 				} while (ull >>= 4);
237 				while ((p - buf) < width &&
238 				    (p - buf) < sizeof(buf)) {
239 					*p++ = padchar;
240 				}
241 				do {
242 					put(*--p);
243 				} while (p > buf);
244  				break;
245  			}
246 #endif
247 			ul = lflag ?
248 			    va_arg(ap, u_long) : va_arg(ap, u_int);
249 			kprintn(put, ul, 16, width, padchar);
250 			break;
251 		default:
252 			put('%');
253 #ifdef LIBSA_LONGLONG_PRINTF
254 			while (--lflag)
255 #else
256 			if (lflag)
257 #endif
258 				put('l');
259 			put(ch);
260 		}
261 	}
262 }
263 
264 void
265 kprintn(void (*put)(int), unsigned long ul, int base, int width, char padchar)
266 {
267 	/* hold a long in base 8 */
268 	char *p, buf[(sizeof(long) * NBBY / 3) + 1];
269 
270 	p = buf;
271 	do {
272 		*p++ = hexdig[ul % base];
273 	} while (ul /= base);
274 	while ((p - buf) < width && (p - buf) < sizeof(buf)) {
275 		*p++ = padchar;
276 	}
277 	do {
278 		put(*--p);
279 	} while (p > buf);
280 }
281 
282 #ifdef LIBSA_LONGLONG_PRINTF
283 void
284 kprintn64(void (*put)(int), u_int64_t ull, int base, int width, char padchar)
285 {
286 	/* hold an int64_t in base 8 */
287 	char *p, buf[(sizeof(u_int64_t) * NBBY / 3) + 1];
288 
289 	p = buf;
290 	do {
291 		*p++ = hexdig[ull % base];
292 	} while (ull /= base);
293 	while ((p - buf) < width && (p - buf) < sizeof(buf)) {
294 		*p++ = padchar;
295 	}
296 	do {
297 		put(*--p);
298 	} while (p > buf);
299 }
300 #endif
301 
302 int donottwiddle = 0;
303 
304 void
305 twiddle(void)
306 {
307 	static int pos;
308 
309 	if (!donottwiddle) {
310 		putchar("|/-\\"[pos++ & 3]);
311 		putchar('\b');
312 	}
313 }
314