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