xref: /minix/sys/lib/libsa/subr_prf.c (revision 0a6a1f1d)
1 /*	$NetBSD: subr_prf.c,v 1.27 2014/08/30 14:24:02 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 
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 RADJUSTZEROPAD()					\
93 do {								\
94 	if ((lflag & (ZEROPAD|LADJUST)) == ZEROPAD) {		\
95 		while (width-- > 0)				\
96 			put('0');				\
97 	}							\
98 } while (/*CONSTCOND*/0)
99 #define LADJUSTPAD()						\
100 do {								\
101 	if (lflag & LADJUST) {					\
102 		while (width-- > 0)				\
103 			put(' ');				\
104 	}							\
105 } while (/*CONSTCOND*/0)
106 #define RADJUSTPAD()						\
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 RADJUSTZEROPAD()	/**/
116 #define LADJUSTPAD()		/**/
117 #define RADJUSTPAD()		/**/
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
sputchar(int c)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 
147 	if (sbuf < ebuf)
148 		*sbuf++ = c;
149 }
150 
151 void
vprintf(const char * fmt,va_list ap)152 vprintf(const char *fmt, va_list ap)
153 {
154 
155 	kdoprnt(putchar, fmt, ap);
156 #if defined(__minix) && defined(LIBSA_PRINTF_WIDTH_SUPPORT)
157 	/* BJG: our libminc kputc() relies on a 0 to flush the diag buffer. */
158 	putchar(0);
159 #endif /* defined(__minix) && defined(LIBSA_PRINTF_WIDTH_SUPPORT) */
160 }
161 
162 int
vsnprintf(char * buf,size_t size,const char * fmt,va_list ap)163 vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
164 {
165 
166 	sbuf = buf;
167 	ebuf = buf + size - 1;
168 #if defined(__minix)
169 	scount = 0; /* use scount to keep track of written items */
170 #endif /* defined(__minix) */
171 
172 	kdoprnt(sputchar, fmt, ap);
173 
174 #if defined(__minix)
175 	if (sbuf){ /* handle case where sbuf == NULL */
176 		*sbuf = '\0';
177 	}
178 #if defined(_MINIX_MAGIC)
179 	sbuf = ebuf = NULL; /* leave no dangling pointers */
180 #endif
181 	return scount;
182 #else /* __minix is not defined */
183 	*sbuf = '\0';
184 	return sbuf - buf;
185 #endif  /* defined(__minix) */
186 }
187 
188 static void
kdoprnt(void (* put)(int),const char * fmt,va_list ap)189 kdoprnt(void (*put)(int), const char *fmt, va_list ap)
190 {
191 	char *p;
192 	int ch;
193 	UINTMAX_T ul;
194 	int lflag;
195 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
196 	int width;
197 	char *q;
198 #if defined(__minix)
199 	int max;
200 #endif /* defined(__minix) */
201 #endif
202 
203 	for (;;) {
204 		while ((ch = *fmt++) != '%') {
205 			if (ch == '\0')
206 				return;
207 			put(ch);
208 		}
209 		lflag = 0;
210 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
211 		width = 0;
212 #if defined(__minix)
213 		max = -1;
214 #endif /* defined(__minix) */
215 #endif
216 reswitch:
217 		switch (ch = *fmt++) {
218 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
219 #if defined(__minix)
220 		case '.':
221 			if (*fmt == '*') {
222 				max = va_arg(ap, int);
223 				fmt++;
224 			} else for (max = 0; *fmt >= '0' && *fmt <= '9'; fmt++)
225 				max = max * 10 + *fmt - '0';
226 			goto reswitch;
227 #endif /* defined(__minix) */
228 		case '#':
229 			lflag |= ALT;
230 			goto reswitch;
231 		case ' ':
232 			lflag |= SPACE;
233 			goto reswitch;
234 		case '-':
235 			lflag |= LADJUST;
236 			goto reswitch;
237 		case '+':
238 			lflag |= SIGN;
239 			goto reswitch;
240 		case '0':
241 			lflag |= ZEROPAD;
242 			goto reswitch;
243 		case '1': case '2': case '3': case '4': case '5':
244 		case '6': case '7': case '8': case '9':
245 			for (;;) {
246 				width *= 10;
247 				width += ch - '0';
248 				ch = *fmt;
249 				if ((unsigned)ch - '0' > 9)
250 					break;
251 				++fmt;
252 			}
253 #endif
254 			goto reswitch;
255 		case 'l':
256 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
257 			if (*fmt == 'l') {
258 				++fmt;
259 				lflag |= LLONG;
260 			} else
261 #endif
262 				lflag |= LONG;
263 			goto reswitch;
264 		case 'j':
265 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
266 			if (sizeof(intmax_t) == sizeof(long long))
267 				lflag |= LLONG;
268 			else
269 #endif
270 			if (sizeof(intmax_t) == sizeof(long))
271 				lflag |= LONG;
272 			goto reswitch;
273 		case 't':
274 			if (sizeof(PTRDIFF_T) == sizeof(long))
275 				lflag |= LONG;
276 			goto reswitch;
277 		case 'z':
278 			if (sizeof(ssize_t) == sizeof(long))
279 				lflag |= LONG;
280 			goto reswitch;
281 		case 'c':
282 			ch = va_arg(ap, int);
283 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
284 			--width;
285 #endif
286 			RADJUSTPAD();
287 			put(ch & 0xFF);
288 			LADJUSTPAD();
289 			break;
290 		case 's':
291 			p = va_arg(ap, char *);
292 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
293 			for (q = p; *q != '\0'; ++q)
294 				continue;
295 #if defined(__minix)
296 			if (max >= 0 && q - p > max)
297 				q = &p[max];
298 #endif /* defined(__minix) */
299 			width -= q - p;
300 #endif
301 			RADJUSTPAD();
302 #if defined(LIBSA_PRINTF_WIDTH_SUPPORT) && defined(__minix)
303 			while ((max < 0 || max-- > 0) &&
304 			    (ch = (unsigned char)*p++))
305 #else /* !defined(LIBSA_PRINTF_WIDTH_SUPPORT) || !defined(__minix) */
306 			while ((ch = (unsigned char)*p++))
307 #endif /* !defined(LIBSA_PRINTF_WIDTH_SUPPORT) || !defined(__minix) */
308 				put(ch);
309 			LADJUSTPAD();
310 			break;
311 		case 'd':
312 			ul =
313 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
314 			(lflag & LLONG) ? va_arg(ap, longlong_t) :
315 #endif
316 			(lflag & LONG) ? va_arg(ap, long) : va_arg(ap, int);
317 			if ((INTMAX_T)ul < 0) {
318 				ul = -(INTMAX_T)ul;
319 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
320 				lflag |= NEGATIVE;
321 #else
322 				put('-');
323 #endif
324 			}
325 			KPRINTN(10);
326 			break;
327 		case 'o':
328 			KPRINT(8);
329 			break;
330 		case 'u':
331 			KPRINT(10);
332 			break;
333 		case 'p':
334 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
335 			lflag |= (LONG|ALT);
336 #else
337 			put('0');
338 			put('x');
339 #endif
340 			/* FALLTHROUGH */
341 		case 'x':
342 			KPRINT(16);
343 			break;
344 #if defined(__minix)
345 		case 'X':
346 			lflag |= HEXCAP;
347 			KPRINT(16);
348 			break;
349 #endif /* defined(__minix) */
350 		default:
351 			if (ch == '\0')
352 				return;
353 			put(ch);
354 			break;
355 		}
356 	}
357 }
358 
359 static void
360 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
kprintn(void (* put)(int),UINTMAX_T ul,int base,int lflag,int width)361 kprintn(void (*put)(int), UINTMAX_T ul, int base, int lflag, int width)
362 #else
363 kprintn(void (*put)(int), UINTMAX_T ul, int base)
364 #endif
365 {
366 					/* hold a INTMAX_T in base 8 */
367 	char *p, buf[(sizeof(INTMAX_T) * NBBY / 3) + 1 + 2 /* ALT + SIGN */];
368 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
369 	char *q;
370 #endif
371 
372 	p = buf;
373 	do {
374 		*p++ = hexdigits[ul % base];
375 #if defined(__minix)
376 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
377 		/* LSC: Quick hack to support capital hex printout. */
378 		if ((lflag & HEXCAP) && (*(p-1) >= 'a') && (*(p-1) <= 'z')) {
379 			*(p-1) -= 32;
380 		}
381 #endif
382 #endif /* defined(__minix) */
383 	} while (ul /= base);
384 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
385 	q = p;
386 	if (lflag & ALT && *(p - 1) != '0') {
387 		if (base == 8) {
388 			*p++ = '0';
389 		} else if (base == 16) {
390 			*p++ = 'x';
391 			*p++ = '0';
392 		}
393 	}
394 	if (lflag & NEGATIVE)
395 		*p++ = '-';
396 	else if (lflag & SIGN)
397 		*p++ = '+';
398 	else if (lflag & SPACE)
399 		*p++ = ' ';
400 	width -= p - buf;
401 	if (lflag & ZEROPAD) {
402 		while (p > q)
403 			put(*--p);
404 	}
405 #endif
406 	RADJUSTPAD();
407 	RADJUSTZEROPAD();
408 	do {
409 		put(*--p);
410 	} while (p > buf);
411 	LADJUSTPAD();
412 }
413