xref: /openbsd/sys/lib/libsa/printf.c (revision 404b540a)
1 /*	$OpenBSD: printf.c,v 1.24 2006/09/18 21:11:50 mpf 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/cdefs.h>
59 #include <sys/types.h>
60 #include <sys/stdarg.h>
61 
62 #include "stand.h"
63 
64 void kprintn(void (*)(int), u_long, int);
65 #ifdef LIBSA_LONGLONG_PRINTF
66 void kprintn64(void (*)(int), u_int64_t, int);
67 #endif
68 void kdoprnt(void (*)(int), const char *, va_list);
69 
70 const char hexdig[] = "0123456789abcdef";
71 
72 void
73 printf(const char *fmt, ...)
74 {
75 	va_list ap;
76 
77 	va_start(ap, fmt);
78 	kdoprnt(putchar, fmt, ap);
79 	va_end(ap);
80 }
81 
82 void
83 vprintf(const char *fmt, va_list ap)
84 {
85 	kdoprnt(putchar, fmt, ap);
86 }
87 
88 void
89 kdoprnt(void (*put)(int), const char *fmt, va_list ap)
90 {
91 #ifdef LIBSA_LONGLONG_PRINTF
92 	u_int64_t ull;
93 #endif
94 	unsigned long ul;
95 	int ch, lflag;
96 	char *p;
97 
98 	for (;;) {
99 		while ((ch = *fmt++) != '%') {
100 			if (ch == '\0')
101 				return;
102 			put(ch);
103 		}
104 		lflag = 0;
105 reswitch:	switch (ch = *fmt++) {
106 		case 'l':
107 			lflag++;
108 			goto reswitch;
109 #ifndef	STRIPPED
110 		case 'b':
111 		{
112 			int set, n;
113 
114 			ul = va_arg(ap, int);
115 			p = va_arg(ap, char *);
116 			kprintn(put, ul, *p++);
117 
118 			if (!ul)
119 				break;
120 
121 			for (set = 0; (n = *p++);) {
122 				if (ul & (1 << (n - 1))) {
123 					put(set ? ',' : '<');
124 					for (; (n = *p) > ' '; ++p)
125 						put(n);
126 					set = 1;
127 				} else
128 					for (; *p > ' '; ++p)
129 						;
130 			}
131 			if (set)
132 				put('>');
133 		}
134 			break;
135 #endif
136 		case 'c':
137 			ch = va_arg(ap, int);
138 			put(ch & 0x7f);
139 			break;
140 		case 's':
141 			p = va_arg(ap, char *);
142 			while ((ch = *p++))
143 				put(ch);
144 			break;
145 		case 'd':
146 #ifdef LIBSA_LONGLONG_PRINTF
147 			if (lflag > 1) {
148 				ull = va_arg(ap, int64_t);
149 				if ((int64_t)ull < 0) {
150 					put('-');
151 					ull = -(int64_t)ull;
152 				}
153 				kprintn64(put, ull, 10);
154 				break;
155 			}
156 #endif
157 			ul = lflag ?
158 			    va_arg(ap, long) : va_arg(ap, int);
159 			if ((long)ul < 0) {
160 				put('-');
161 				ul = -(long)ul;
162 			}
163 			kprintn(put, ul, 10);
164 			break;
165 		case 'o':
166 #ifdef LIBSA_LONGLONG_PRINTF
167 			if (lflag > 1) {
168 				ull = va_arg(ap, u_int64_t);
169 				kprintn64(put, ull, 8);
170 				break;
171 			}
172 #endif
173 			ul = lflag ?
174 			    va_arg(ap, u_long) : va_arg(ap, u_int);
175 			kprintn(put, ul, 8);
176 			break;
177 		case 'u':
178 #ifdef LIBSA_LONGLONG_PRINTF
179 			if (lflag > 1) {
180 				ull = va_arg(ap, u_int64_t);
181 				kprintn64(put, ull, 10);
182 				break;
183 			}
184 #endif
185 			ul = lflag ?
186 			    va_arg(ap, u_long) : va_arg(ap, u_int);
187 			kprintn(put, ul, 10);
188 			break;
189 		case 'p':
190 			put('0');
191 			put('x');
192 			lflag += sizeof(void *)==sizeof(u_long)? 1 : 0;
193 		case 'x':
194 #ifdef LIBSA_LONGLONG_PRINTF
195 			if (lflag > 1) {
196 				ull = va_arg(ap, u_int64_t);
197 				kprintn64(put, ull, 16);
198 				break;
199 			}
200 #else
201  			if (lflag > 1) {
202 				/* hold an int64_t in base 16 */
203 				char *p, buf[(sizeof(u_int64_t) * NBBY / 4) + 1];
204 				u_int64_t ull;
205 
206  				ull = va_arg(ap, u_int64_t);
207 				p = buf;
208 				do {
209 					*p++ = hexdig[ull & 15];
210 				} while (ull >>= 4);
211 				do {
212 					put(*--p);
213 				} while (p > buf);
214  				break;
215  			}
216 #endif
217 			ul = lflag ?
218 			    va_arg(ap, u_long) : va_arg(ap, u_int);
219 			kprintn(put, ul, 16);
220 			break;
221 		default:
222 			put('%');
223 #ifdef LIBSA_LONGLONG_PRINTF
224 			while (--lflag)
225 #else
226 			if (lflag)
227 #endif
228 				put('l');
229 			put(ch);
230 		}
231 	}
232 	va_end(ap);
233 }
234 
235 void
236 kprintn(void (*put)(int), unsigned long ul, int base)
237 {
238 	/* hold a long in base 8 */
239 	char *p, buf[(sizeof(long) * NBBY / 3) + 1];
240 
241 	p = buf;
242 	do {
243 		*p++ = hexdig[ul % base];
244 	} while (ul /= base);
245 	do {
246 		put(*--p);
247 	} while (p > buf);
248 }
249 
250 #ifdef LIBSA_LONGLONG_PRINTF
251 void
252 kprintn64(void (*put)(int), u_int64_t ull, int base)
253 {
254 	/* hold an int64_t in base 8 */
255 	char *p, buf[(sizeof(u_int64_t) * NBBY / 3) + 1];
256 
257 	p = buf;
258 	do {
259 		*p++ = hexdig[ull % base];
260 	} while (ull /= base);
261 	do {
262 		put(*--p);
263 	} while (p > buf);
264 }
265 #endif
266 
267 int donottwiddle = 0;
268 
269 void
270 twiddle(void)
271 {
272 	static int pos;
273 
274 	if (!donottwiddle) {
275 		putchar("|/-\\"[pos++ & 3]);
276 		putchar('\b');
277 	}
278 }
279