xref: /netbsd/sys/lib/libsa/subr_prf.c (revision 6550d01e)
1 /*	$NetBSD: subr_prf.c,v 1.17 2010/01/19 15:26:45 tsutsui 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[] = "0123456789ABCDEF";
70 const char hexdigits[] = "0123456789abcdef";
71 
72 #define LONG		0x01
73 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
74 #define LLONG		0x02
75 #endif
76 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
77 #define ALT		0x04
78 #define SPACE		0x08
79 #define LADJUST		0x10
80 #define SIGN		0x20
81 #define ZEROPAD		0x40
82 #define NEGATIVE	0x80
83 #define KPRINTN(base)	kprintn(put, ul, base, lflag, width)
84 #define RZERO()							\
85 do {								\
86 	if ((lflag & (ZEROPAD|LADJUST)) == ZEROPAD) {		\
87 		while (width-- > 0)				\
88 			put('0');				\
89 	}							\
90 } while (/*CONSTCOND*/0)
91 #define RPAD()							\
92 do {								\
93 	if (lflag & LADJUST) {					\
94 		while (width-- > 0)				\
95 			put(' ');				\
96 	}							\
97 } while (/*CONSTCOND*/0)
98 #define LPAD()							\
99 do {								\
100 	if ((lflag & (ZEROPAD|LADJUST)) == 0) {			\
101 		while (width-- > 0)				\
102 			put(' ');				\
103 	}							\
104 } while (/*CONSTCOND*/0)
105 #else	/* LIBSA_PRINTF_WIDTH_SUPPORT */
106 #define KPRINTN(base)	kprintn(put, ul, base)
107 #define RZERO()		/**/
108 #define RPAD()		/**/
109 #define LPAD()		/**/
110 #endif	/* LIBSA_PRINTF_WIDTH_SUPPORT */
111 
112 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
113 #define KPRINT(base)						\
114 do {								\
115 	ul = (lflag & LLONG)					\
116 	    ? va_arg(ap, u_longlong_t)				\
117 	    : (lflag & LONG)					\
118 		? va_arg(ap, u_long)				\
119 		: va_arg(ap, u_int);				\
120 	KPRINTN(base);						\
121 } while (/*CONSTCOND*/0)
122 #else	/* LIBSA_PRINTF_LONGLONG_SUPPORT */
123 #define KPRINT(base)						\
124 do {								\
125 	ul = (lflag & LONG)					\
126 	    ? va_arg(ap, u_long) : va_arg(ap, u_int);		\
127 	KPRINTN(base);						\
128 } while (/*CONSTCOND*/0)
129 #endif	/* LIBSA_PRINTF_LONGLONG_SUPPORT */
130 
131 static void
132 sputchar(int c)
133 {
134 
135 	if (sbuf < ebuf)
136 		*sbuf++ = c;
137 }
138 
139 void
140 vprintf(const char *fmt, va_list ap)
141 {
142 
143 	kdoprnt(putchar, fmt, ap);
144 }
145 
146 int
147 vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
148 {
149 
150 	sbuf = buf;
151 	ebuf = buf + size - 1;
152 	kdoprnt(sputchar, fmt, ap);
153 	*sbuf = '\0';
154 	return sbuf - buf;
155 }
156 
157 static void
158 kdoprnt(void (*put)(int), const char *fmt, va_list ap)
159 {
160 	char *p;
161 	int ch;
162 	UINTMAX_T ul;
163 	int lflag;
164 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
165 	int width;
166 	char *q;
167 #endif
168 
169 	for (;;) {
170 		while ((ch = *fmt++) != '%') {
171 			if (ch == '\0')
172 				return;
173 			put(ch);
174 		}
175 		lflag = 0;
176 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
177 		width = 0;
178 #endif
179 reswitch:
180 		switch (ch = *fmt++) {
181 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
182 		case '#':
183 			lflag |= ALT;
184 			goto reswitch;
185 		case ' ':
186 			lflag |= SPACE;
187 			goto reswitch;
188 		case '-':
189 			lflag |= LADJUST;
190 			goto reswitch;
191 		case '+':
192 			lflag |= SIGN;
193 			goto reswitch;
194 		case '0':
195 			lflag |= ZEROPAD;
196 			goto reswitch;
197 		case '1': case '2': case '3': case '4': case '5':
198 		case '6': case '7': case '8': case '9':
199 			for (;;) {
200 				width *= 10;
201 				width += ch - '0';
202 				ch = *fmt;
203 				if ((unsigned)ch - '0' > 9)
204 					break;
205 				++fmt;
206 			}
207 #endif
208 			goto reswitch;
209 		case 'l':
210 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
211 			if (*fmt == 'l') {
212 				++fmt;
213 				lflag |= LLONG;
214 			} else
215 #endif
216 				lflag |= LONG;
217 			goto reswitch;
218 		case 't':
219 			if (sizeof(PTRDIFF_T) == sizeof(long))
220 				lflag |= LONG;
221 			goto reswitch;
222 		case 'z':
223 			if (sizeof(ssize_t) == sizeof(long))
224 				lflag |= LONG;
225 			goto reswitch;
226 		case 'c':
227 			ch = va_arg(ap, int);
228 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
229 			--width;
230 #endif
231 			RPAD();
232 			put(ch & 0xFF);
233 			LPAD();
234 			break;
235 		case 's':
236 			p = va_arg(ap, char *);
237 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
238 			for (q = p; *q; ++q);
239 			width -= q - p;
240 #endif
241 			RPAD();
242 			while ((ch = (unsigned char)*p++))
243 				put(ch);
244 			LPAD();
245 			break;
246 		case 'd':
247 			ul =
248 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
249 			(lflag & LLONG) ? va_arg(ap, longlong_t) :
250 #endif
251 			(lflag & LONG) ? va_arg(ap, long) : va_arg(ap, int);
252 			if ((INTMAX_T)ul < 0) {
253 				ul = -(INTMAX_T)ul;
254 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
255 				lflag |= NEGATIVE;
256 #else
257 				put('-');
258 #endif
259 			}
260 			KPRINTN(10);
261 			break;
262 		case 'o':
263 			KPRINT(8);
264 			break;
265 		case 'u':
266 			KPRINT(10);
267 			break;
268 		case 'p':
269 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
270 			lflag |= (LONG|ALT);
271 #else
272 			put('0');
273 			put('x');
274 #endif
275 			/* FALLTHROUGH */
276 		case 'x':
277 			KPRINT(16);
278 			break;
279 		default:
280 			if (ch == '\0')
281 				return;
282 			put(ch);
283 			break;
284 		}
285 	}
286 }
287 
288 static void
289 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
290 kprintn(void (*put)(int), UINTMAX_T ul, int base, int lflag, int width)
291 #else
292 kprintn(void (*put)(int), UINTMAX_T ul, int base)
293 #endif
294 {
295 					/* hold a INTMAX_T in base 8 */
296 	char *p, buf[(sizeof(INTMAX_T) * NBBY / 3) + 1 + 2 /* ALT + SIGN */];
297 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
298 	char *q;
299 #endif
300 
301 	p = buf;
302 	do {
303 		*p++ = hexdigits[ul % base];
304 	} while (ul /= base);
305 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
306 	q = p;
307 	if (lflag & ALT && *(p - 1) != '0') {
308 		if (base == 8) {
309 			*p++ = '0';
310 		} else if (base == 16) {
311 			*p++ = 'x';
312 			*p++ = '0';
313 		}
314 	}
315 	if (lflag & NEGATIVE)
316 		*p++ = '-';
317 	else if (lflag & SIGN)
318 		*p++ = '+';
319 	else if (lflag & SPACE)
320 		*p++ = ' ';
321 	width -= p - buf;
322 	if ((lflag & LADJUST) == 0) {
323 		while (p > q)
324 			put(*--p);
325 	}
326 #endif
327 	RPAD();
328 	RZERO();
329 	do {
330 		put(*--p);
331 	} while (p > buf);
332 	LPAD();
333 }
334