xref: /netbsd-src/sys/lib/libsa/subr_prf.c (revision 46f5119e40af2e51998f686b2fdcc76b5488f7f3)
1 /*	$NetBSD: subr_prf.c,v 1.19 2011/02/25 00:20:36 joerg Exp $	*/
2 
3 /*-
4  * Copyright (c) 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  *	@(#)printf.c	8.1 (Berkeley) 6/11/93
32  */
33 
34 /*
35  * Scaled down version of printf(3).
36  */
37 
38 #include <sys/cdefs.h>
39 #include <sys/types.h>
40 #include <sys/stdint.h>		/* XXX: for intptr_t */
41 #include <machine/stdarg.h>
42 
43 #include "stand.h"
44 
45 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
46 #define INTMAX_T	longlong_t
47 #define UINTMAX_T	u_longlong_t
48 #else
49 #define INTMAX_T	long
50 #define UINTMAX_T	u_long
51 #endif
52 
53 #if 0 /* XXX: abuse intptr_t until the situation with ptrdiff_t is clear */
54 #define PTRDIFF_T	ptrdiff_t
55 #else
56 #define PTRDIFF_T	intptr_t
57 #endif
58 
59 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
60 static void kprintn(void (*)(int), UINTMAX_T, int, int, int);
61 #else
62 static void kprintn(void (*)(int), UINTMAX_T, int);
63 #endif
64 static void sputchar(int);
65 static void kdoprnt(void (*)(int), const char *, va_list);
66 
67 static char *sbuf, *ebuf;
68 
69 const char hexdigits[16] = "0123456789abcdef";
70 
71 #define LONG		0x01
72 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
73 #define LLONG		0x02
74 #endif
75 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
76 #define ALT		0x04
77 #define SPACE		0x08
78 #define LADJUST		0x10
79 #define SIGN		0x20
80 #define ZEROPAD		0x40
81 #define NEGATIVE	0x80
82 #define KPRINTN(base)	kprintn(put, ul, base, lflag, width)
83 #define RZERO()							\
84 do {								\
85 	if ((lflag & (ZEROPAD|LADJUST)) == ZEROPAD) {		\
86 		while (width-- > 0)				\
87 			put('0');				\
88 	}							\
89 } while (/*CONSTCOND*/0)
90 #define RPAD()							\
91 do {								\
92 	if (lflag & LADJUST) {					\
93 		while (width-- > 0)				\
94 			put(' ');				\
95 	}							\
96 } while (/*CONSTCOND*/0)
97 #define LPAD()							\
98 do {								\
99 	if ((lflag & (ZEROPAD|LADJUST)) == 0) {			\
100 		while (width-- > 0)				\
101 			put(' ');				\
102 	}							\
103 } while (/*CONSTCOND*/0)
104 #else	/* LIBSA_PRINTF_WIDTH_SUPPORT */
105 #define KPRINTN(base)	kprintn(put, ul, base)
106 #define RZERO()		/**/
107 #define RPAD()		/**/
108 #define LPAD()		/**/
109 #endif	/* LIBSA_PRINTF_WIDTH_SUPPORT */
110 
111 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
112 #define KPRINT(base)						\
113 do {								\
114 	ul = (lflag & LLONG)					\
115 	    ? va_arg(ap, u_longlong_t)				\
116 	    : (lflag & LONG)					\
117 		? va_arg(ap, u_long)				\
118 		: va_arg(ap, u_int);				\
119 	KPRINTN(base);						\
120 } while (/*CONSTCOND*/0)
121 #else	/* LIBSA_PRINTF_LONGLONG_SUPPORT */
122 #define KPRINT(base)						\
123 do {								\
124 	ul = (lflag & LONG)					\
125 	    ? va_arg(ap, u_long) : va_arg(ap, u_int);		\
126 	KPRINTN(base);						\
127 } while (/*CONSTCOND*/0)
128 #endif	/* LIBSA_PRINTF_LONGLONG_SUPPORT */
129 
130 static void
131 sputchar(int c)
132 {
133 
134 	if (sbuf < ebuf)
135 		*sbuf++ = c;
136 }
137 
138 void
139 vprintf(const char *fmt, va_list ap)
140 {
141 
142 	kdoprnt(putchar, fmt, ap);
143 }
144 
145 int
146 vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
147 {
148 
149 	sbuf = buf;
150 	ebuf = buf + size - 1;
151 	kdoprnt(sputchar, fmt, ap);
152 	*sbuf = '\0';
153 	return sbuf - buf;
154 }
155 
156 static void
157 kdoprnt(void (*put)(int), const char *fmt, va_list ap)
158 {
159 	char *p;
160 	int ch;
161 	UINTMAX_T ul;
162 	int lflag;
163 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
164 	int width;
165 	char *q;
166 #endif
167 
168 	for (;;) {
169 		while ((ch = *fmt++) != '%') {
170 			if (ch == '\0')
171 				return;
172 			put(ch);
173 		}
174 		lflag = 0;
175 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
176 		width = 0;
177 #endif
178 reswitch:
179 		switch (ch = *fmt++) {
180 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
181 		case '#':
182 			lflag |= ALT;
183 			goto reswitch;
184 		case ' ':
185 			lflag |= SPACE;
186 			goto reswitch;
187 		case '-':
188 			lflag |= LADJUST;
189 			goto reswitch;
190 		case '+':
191 			lflag |= SIGN;
192 			goto reswitch;
193 		case '0':
194 			lflag |= ZEROPAD;
195 			goto reswitch;
196 		case '1': case '2': case '3': case '4': case '5':
197 		case '6': case '7': case '8': case '9':
198 			for (;;) {
199 				width *= 10;
200 				width += ch - '0';
201 				ch = *fmt;
202 				if ((unsigned)ch - '0' > 9)
203 					break;
204 				++fmt;
205 			}
206 #endif
207 			goto reswitch;
208 		case 'l':
209 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
210 			if (*fmt == 'l') {
211 				++fmt;
212 				lflag |= LLONG;
213 			} else
214 #endif
215 				lflag |= LONG;
216 			goto reswitch;
217 		case 't':
218 			if (sizeof(PTRDIFF_T) == sizeof(long))
219 				lflag |= LONG;
220 			goto reswitch;
221 		case 'z':
222 			if (sizeof(ssize_t) == sizeof(long))
223 				lflag |= LONG;
224 			goto reswitch;
225 		case 'c':
226 			ch = va_arg(ap, int);
227 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
228 			--width;
229 #endif
230 			RPAD();
231 			put(ch & 0xFF);
232 			LPAD();
233 			break;
234 		case 's':
235 			p = va_arg(ap, char *);
236 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
237 			for (q = p; *q; ++q);
238 			width -= q - p;
239 #endif
240 			RPAD();
241 			while ((ch = (unsigned char)*p++))
242 				put(ch);
243 			LPAD();
244 			break;
245 		case 'd':
246 			ul =
247 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
248 			(lflag & LLONG) ? va_arg(ap, longlong_t) :
249 #endif
250 			(lflag & LONG) ? va_arg(ap, long) : va_arg(ap, int);
251 			if ((INTMAX_T)ul < 0) {
252 				ul = -(INTMAX_T)ul;
253 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
254 				lflag |= NEGATIVE;
255 #else
256 				put('-');
257 #endif
258 			}
259 			KPRINTN(10);
260 			break;
261 		case 'o':
262 			KPRINT(8);
263 			break;
264 		case 'u':
265 			KPRINT(10);
266 			break;
267 		case 'p':
268 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
269 			lflag |= (LONG|ALT);
270 #else
271 			put('0');
272 			put('x');
273 #endif
274 			/* FALLTHROUGH */
275 		case 'x':
276 			KPRINT(16);
277 			break;
278 		default:
279 			if (ch == '\0')
280 				return;
281 			put(ch);
282 			break;
283 		}
284 	}
285 }
286 
287 static void
288 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
289 kprintn(void (*put)(int), UINTMAX_T ul, int base, int lflag, int width)
290 #else
291 kprintn(void (*put)(int), UINTMAX_T ul, int base)
292 #endif
293 {
294 					/* hold a INTMAX_T in base 8 */
295 	char *p, buf[(sizeof(INTMAX_T) * NBBY / 3) + 1 + 2 /* ALT + SIGN */];
296 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
297 	char *q;
298 #endif
299 
300 	p = buf;
301 	do {
302 		*p++ = hexdigits[ul % base];
303 	} while (ul /= base);
304 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
305 	q = p;
306 	if (lflag & ALT && *(p - 1) != '0') {
307 		if (base == 8) {
308 			*p++ = '0';
309 		} else if (base == 16) {
310 			*p++ = 'x';
311 			*p++ = '0';
312 		}
313 	}
314 	if (lflag & NEGATIVE)
315 		*p++ = '-';
316 	else if (lflag & SIGN)
317 		*p++ = '+';
318 	else if (lflag & SPACE)
319 		*p++ = ' ';
320 	width -= p - buf;
321 	if ((lflag & LADJUST) == 0) {
322 		while (p > q)
323 			put(*--p);
324 	}
325 #endif
326 	RPAD();
327 	RZERO();
328 	do {
329 		put(*--p);
330 	} while (p > buf);
331 	LPAD();
332 }
333