xref: /minix3/sys/lib/libsa/subr_prf.c (revision 7c48de6cc4c6d56f2277d378dba01dbac8a8c3b9)
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 #if defined(_MINIX_MAGIC)
178 	sbuf = ebuf = NULL; /* leave no dangling pointers */
179 #endif
180 	return scount;
181 #else /* __minix is not defined */
182 	*sbuf = '\0';
183 	return sbuf - buf;
184 #endif  /* defined(__minix) */
185 }
186 
187 static void
188 kdoprnt(void (*put)(int), const char *fmt, va_list ap)
189 {
190 	char *p;
191 	int ch;
192 	UINTMAX_T ul;
193 	int lflag;
194 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
195 	int width;
196 	char *q;
197 #if defined(__minix)
198 	int max;
199 #endif /* defined(__minix) */
200 #endif
201 
202 	for (;;) {
203 		while ((ch = *fmt++) != '%') {
204 			if (ch == '\0')
205 				return;
206 			put(ch);
207 		}
208 		lflag = 0;
209 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
210 		width = 0;
211 #if defined(__minix)
212 		max = -1;
213 #endif /* defined(__minix) */
214 #endif
215 reswitch:
216 		switch (ch = *fmt++) {
217 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
218 #if defined(__minix)
219 		case '.':
220 			if (*fmt == '*') {
221 				max = va_arg(ap, int);
222 				fmt++;
223 			} else for (max = 0; *fmt >= '0' && *fmt <= '9'; fmt++)
224 				max = max * 10 + *fmt - '0';
225 			goto reswitch;
226 #endif /* defined(__minix) */
227 		case '#':
228 			lflag |= ALT;
229 			goto reswitch;
230 		case ' ':
231 			lflag |= SPACE;
232 			goto reswitch;
233 		case '-':
234 			lflag |= LADJUST;
235 			goto reswitch;
236 		case '+':
237 			lflag |= SIGN;
238 			goto reswitch;
239 		case '0':
240 			lflag |= ZEROPAD;
241 			goto reswitch;
242 		case '1': case '2': case '3': case '4': case '5':
243 		case '6': case '7': case '8': case '9':
244 			for (;;) {
245 				width *= 10;
246 				width += ch - '0';
247 				ch = *fmt;
248 				if ((unsigned)ch - '0' > 9)
249 					break;
250 				++fmt;
251 			}
252 #endif
253 			goto reswitch;
254 		case 'l':
255 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
256 			if (*fmt == 'l') {
257 				++fmt;
258 				lflag |= LLONG;
259 			} else
260 #endif
261 				lflag |= LONG;
262 			goto reswitch;
263 		case 't':
264 			if (sizeof(PTRDIFF_T) == sizeof(long))
265 				lflag |= LONG;
266 			goto reswitch;
267 		case 'z':
268 			if (sizeof(ssize_t) == sizeof(long))
269 				lflag |= LONG;
270 			goto reswitch;
271 		case 'c':
272 			ch = va_arg(ap, int);
273 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
274 			--width;
275 #endif
276 			RPAD();
277 			put(ch & 0xFF);
278 			LPAD();
279 			break;
280 		case 's':
281 			p = va_arg(ap, char *);
282 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
283 			for (q = p; *q != '\0'; ++q)
284 				continue;
285 #if defined(__minix)
286 			if (max >= 0 && q - p > max)
287 				q = &p[max];
288 #endif /* defined(__minix) */
289 			width -= q - p;
290 #endif
291 			RPAD();
292 #if defined(LIBSA_PRINTF_WIDTH_SUPPORT) && defined(__minix)
293 			while ((max < 0 || max-- > 0) &&
294 			    (ch = (unsigned char)*p++))
295 #else /* !defined(LIBSA_PRINTF_WIDTH_SUPPORT) || !defined(__minix) */
296 			while ((ch = (unsigned char)*p++))
297 #endif /* !defined(LIBSA_PRINTF_WIDTH_SUPPORT) || !defined(__minix) */
298 				put(ch);
299 			LPAD();
300 			break;
301 		case 'd':
302 			ul =
303 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
304 			(lflag & LLONG) ? va_arg(ap, longlong_t) :
305 #endif
306 			(lflag & LONG) ? va_arg(ap, long) : va_arg(ap, int);
307 			if ((INTMAX_T)ul < 0) {
308 				ul = -(INTMAX_T)ul;
309 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
310 				lflag |= NEGATIVE;
311 #else
312 				put('-');
313 #endif
314 			}
315 			KPRINTN(10);
316 			break;
317 		case 'o':
318 			KPRINT(8);
319 			break;
320 		case 'u':
321 			KPRINT(10);
322 			break;
323 		case 'p':
324 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
325 			lflag |= (LONG|ALT);
326 #else
327 			put('0');
328 			put('x');
329 #endif
330 			/* FALLTHROUGH */
331 		case 'x':
332 			KPRINT(16);
333 			break;
334 #if defined(__minix)
335 		case 'X':
336 			lflag |= HEXCAP;
337 			KPRINT(16);
338 			break;
339 #endif /* defined(__minix) */
340 		default:
341 			if (ch == '\0')
342 				return;
343 			put(ch);
344 			break;
345 		}
346 	}
347 }
348 
349 static void
350 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
351 kprintn(void (*put)(int), UINTMAX_T ul, int base, int lflag, int width)
352 #else
353 kprintn(void (*put)(int), UINTMAX_T ul, int base)
354 #endif
355 {
356 					/* hold a INTMAX_T in base 8 */
357 	char *p, buf[(sizeof(INTMAX_T) * NBBY / 3) + 1 + 2 /* ALT + SIGN */];
358 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
359 	char *q;
360 #endif
361 
362 	p = buf;
363 	do {
364 		*p++ = hexdigits[ul % base];
365 #if defined(__minix)
366 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
367 		/* LSC: Quick hack to support capital hex printout. */
368 		if ((lflag & HEXCAP) && (*(p-1) >= 'a') && (*(p-1) <= 'z')) {
369 			*(p-1) -= 32;
370 		}
371 #endif
372 #endif /* defined(__minix) */
373 	} while (ul /= base);
374 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
375 	q = p;
376 	if (lflag & ALT && *(p - 1) != '0') {
377 		if (base == 8) {
378 			*p++ = '0';
379 		} else if (base == 16) {
380 			*p++ = 'x';
381 			*p++ = '0';
382 		}
383 	}
384 	if (lflag & NEGATIVE)
385 		*p++ = '-';
386 	else if (lflag & SIGN)
387 		*p++ = '+';
388 	else if (lflag & SPACE)
389 		*p++ = ' ';
390 	width -= p - buf;
391 	if ((lflag & LADJUST) == 0) {
392 		while (p > q)
393 			put(*--p);
394 	}
395 #endif
396 	RPAD();
397 	RZERO();
398 	do {
399 		put(*--p);
400 	} while (p > buf);
401 	LPAD();
402 }
403