xref: /minix3/sys/lib/libsa/subr_prf.c (revision c9ea9e7af84fcba485b32ccd2c85edb945b1f323)
1 /*	$NetBSD: subr_prf.c,v 1.21 2011/07/17 20:54:52 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 
42 #include "stand.h"
43 
44 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
45 #define INTMAX_T	longlong_t
46 #define UINTMAX_T	u_longlong_t
47 #else
48 #define INTMAX_T	long
49 #define UINTMAX_T	u_long
50 #endif
51 
52 #if 0 /* XXX: abuse intptr_t until the situation with ptrdiff_t is clear */
53 #define PTRDIFF_T	ptrdiff_t
54 #else
55 #define PTRDIFF_T	intptr_t
56 #endif
57 
58 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
59 static void kprintn(void (*)(int), UINTMAX_T, int, int, int);
60 #else
61 static void kprintn(void (*)(int), UINTMAX_T, int);
62 #endif
63 static void sputchar(int);
64 static void kdoprnt(void (*)(int), const char *, va_list);
65 
66 static char *sbuf, *ebuf;
67 
68 const char hexdigits[16] = "0123456789abcdef";
69 
70 #define LONG		0x01
71 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
72 #define LLONG		0x02
73 #endif
74 
75 #if defined(__minix)
76 #define HEXCAP		0x100
77 #endif /* defined(__minix) */
78 
79 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
80 #define ALT		0x04
81 #define SPACE		0x08
82 #define LADJUST		0x10
83 #define SIGN		0x20
84 #define ZEROPAD		0x40
85 #define NEGATIVE	0x80
86 #define KPRINTN(base)	kprintn(put, ul, base, lflag, width)
87 #define RZERO()							\
88 do {								\
89 	if ((lflag & (ZEROPAD|LADJUST)) == ZEROPAD) {		\
90 		while (width-- > 0)				\
91 			put('0');				\
92 	}							\
93 } while (/*CONSTCOND*/0)
94 #define RPAD()							\
95 do {								\
96 	if (lflag & LADJUST) {					\
97 		while (width-- > 0)				\
98 			put(' ');				\
99 	}							\
100 } while (/*CONSTCOND*/0)
101 #define LPAD()							\
102 do {								\
103 	if ((lflag & (ZEROPAD|LADJUST)) == 0) {			\
104 		while (width-- > 0)				\
105 			put(' ');				\
106 	}							\
107 } while (/*CONSTCOND*/0)
108 #else	/* LIBSA_PRINTF_WIDTH_SUPPORT */
109 #define KPRINTN(base)	kprintn(put, ul, base)
110 #define RZERO()		/**/
111 #define RPAD()		/**/
112 #define LPAD()		/**/
113 #endif	/* LIBSA_PRINTF_WIDTH_SUPPORT */
114 
115 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
116 #define KPRINT(base)						\
117 do {								\
118 	ul = (lflag & LLONG)					\
119 	    ? va_arg(ap, u_longlong_t)				\
120 	    : (lflag & LONG)					\
121 		? va_arg(ap, u_long)				\
122 		: va_arg(ap, u_int);				\
123 	KPRINTN(base);						\
124 } while (/*CONSTCOND*/0)
125 #else	/* LIBSA_PRINTF_LONGLONG_SUPPORT */
126 #define KPRINT(base)						\
127 do {								\
128 	ul = (lflag & LONG)					\
129 	    ? va_arg(ap, u_long) : va_arg(ap, u_int);		\
130 	KPRINTN(base);						\
131 } while (/*CONSTCOND*/0)
132 #endif	/* LIBSA_PRINTF_LONGLONG_SUPPORT */
133 
134 static void
135 sputchar(int c)
136 {
137 
138 	if (sbuf < ebuf)
139 		*sbuf++ = c;
140 }
141 
142 void
143 vprintf(const char *fmt, va_list ap)
144 {
145 
146 	kdoprnt(putchar, fmt, ap);
147 #if defined(__minix) && defined(LIBSA_PRINTF_WIDTH_SUPPORT)
148 	/* BJG: our libminc kputc() relies on a 0 to flush the diag buffer. */
149 	putchar(0);
150 #endif /* defined(__minix) && defined(LIBSA_PRINTF_WIDTH_SUPPORT) */
151 }
152 
153 int
154 vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
155 {
156 
157 	sbuf = buf;
158 	ebuf = buf + size - 1;
159 	kdoprnt(sputchar, fmt, ap);
160 	*sbuf = '\0';
161 	return sbuf - buf;
162 }
163 
164 static void
165 kdoprnt(void (*put)(int), const char *fmt, va_list ap)
166 {
167 	char *p;
168 	int ch;
169 	UINTMAX_T ul;
170 	int lflag;
171 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
172 	int width;
173 	char *q;
174 #endif
175 
176 	for (;;) {
177 		while ((ch = *fmt++) != '%') {
178 			if (ch == '\0')
179 				return;
180 			put(ch);
181 		}
182 		lflag = 0;
183 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
184 		width = 0;
185 #endif
186 reswitch:
187 		switch (ch = *fmt++) {
188 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
189 #if defined(__minix)
190 		/* LSC: FIXME: this is a simple hack which ignores the thing for now. */
191 		case '.':
192 			/* eat up digits */
193 			while( ((('1' >= *fmt) && ( *fmt <= '9'))
194 				 || (*fmt == '*')) )
195 				 fmt++;
196 			fmt++;
197 			goto reswitch;
198 #endif /* defined(__minix) */
199 		case '#':
200 			lflag |= ALT;
201 			goto reswitch;
202 		case ' ':
203 			lflag |= SPACE;
204 			goto reswitch;
205 		case '-':
206 			lflag |= LADJUST;
207 			goto reswitch;
208 		case '+':
209 			lflag |= SIGN;
210 			goto reswitch;
211 		case '0':
212 			lflag |= ZEROPAD;
213 			goto reswitch;
214 		case '1': case '2': case '3': case '4': case '5':
215 		case '6': case '7': case '8': case '9':
216 			for (;;) {
217 				width *= 10;
218 				width += ch - '0';
219 				ch = *fmt;
220 				if ((unsigned)ch - '0' > 9)
221 					break;
222 				++fmt;
223 			}
224 #endif
225 			goto reswitch;
226 		case 'l':
227 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
228 			if (*fmt == 'l') {
229 				++fmt;
230 				lflag |= LLONG;
231 			} else
232 #endif
233 				lflag |= LONG;
234 			goto reswitch;
235 		case 't':
236 			if (sizeof(PTRDIFF_T) == sizeof(long))
237 				lflag |= LONG;
238 			goto reswitch;
239 		case 'z':
240 			if (sizeof(ssize_t) == sizeof(long))
241 				lflag |= LONG;
242 			goto reswitch;
243 		case 'c':
244 			ch = va_arg(ap, int);
245 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
246 			--width;
247 #endif
248 			RPAD();
249 			put(ch & 0xFF);
250 			LPAD();
251 			break;
252 		case 's':
253 			p = va_arg(ap, char *);
254 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
255 			for (q = p; *q != '\0'; ++q)
256 				continue;
257 			width -= q - p;
258 #endif
259 			RPAD();
260 			while ((ch = (unsigned char)*p++))
261 				put(ch);
262 			LPAD();
263 			break;
264 		case 'd':
265 			ul =
266 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
267 			(lflag & LLONG) ? va_arg(ap, longlong_t) :
268 #endif
269 			(lflag & LONG) ? va_arg(ap, long) : va_arg(ap, int);
270 			if ((INTMAX_T)ul < 0) {
271 				ul = -(INTMAX_T)ul;
272 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
273 				lflag |= NEGATIVE;
274 #else
275 				put('-');
276 #endif
277 			}
278 			KPRINTN(10);
279 			break;
280 		case 'o':
281 			KPRINT(8);
282 			break;
283 		case 'u':
284 			KPRINT(10);
285 			break;
286 		case 'p':
287 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
288 			lflag |= (LONG|ALT);
289 #else
290 			put('0');
291 			put('x');
292 #endif
293 			/* FALLTHROUGH */
294 		case 'x':
295 			KPRINT(16);
296 			break;
297 #if defined(__minix)
298 		case 'X':
299 			lflag |= HEXCAP;
300 			KPRINT(16);
301 			break;
302 #endif /* defined(__minix) */
303 		default:
304 			if (ch == '\0')
305 				return;
306 			put(ch);
307 			break;
308 		}
309 	}
310 }
311 
312 static void
313 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
314 kprintn(void (*put)(int), UINTMAX_T ul, int base, int lflag, int width)
315 #else
316 kprintn(void (*put)(int), UINTMAX_T ul, int base)
317 #endif
318 {
319 					/* hold a INTMAX_T in base 8 */
320 	char *p, buf[(sizeof(INTMAX_T) * NBBY / 3) + 1 + 2 /* ALT + SIGN */];
321 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
322 	char *q;
323 #endif
324 
325 	p = buf;
326 	do {
327 		*p++ = hexdigits[ul % base];
328 #if defined(__minix)
329 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
330 		/* LSC: Quick hack to support capital hex printout. */
331 		if ((lflag & HEXCAP) && (*(p-1) >= 'a') && (*(p-1) <= 'z')) {
332 			*(p-1) -= 32;
333 		}
334 #endif
335 #endif /* defined(__minix) */
336 	} while (ul /= base);
337 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
338 	q = p;
339 	if (lflag & ALT && *(p - 1) != '0') {
340 		if (base == 8) {
341 			*p++ = '0';
342 		} else if (base == 16) {
343 			*p++ = 'x';
344 			*p++ = '0';
345 		}
346 	}
347 	if (lflag & NEGATIVE)
348 		*p++ = '-';
349 	else if (lflag & SIGN)
350 		*p++ = '+';
351 	else if (lflag & SPACE)
352 		*p++ = ' ';
353 	width -= p - buf;
354 	if ((lflag & LADJUST) == 0) {
355 		while (p > q)
356 			put(*--p);
357 	}
358 #endif
359 	RPAD();
360 	RZERO();
361 	do {
362 		put(*--p);
363 	} while (p > buf);
364 	LPAD();
365 }
366