xref: /netbsd-src/sys/lib/libsa/subr_prf.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1 /*	$NetBSD: subr_prf.c,v 1.31 2024/08/31 07:12:57 mlelstv 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 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
75 #define ALT		0x04
76 #define SPACE		0x08
77 #define LADJUST		0x10
78 #define SIGN		0x20
79 #define ZEROPAD		0x40
80 #define NEGATIVE	0x80
81 #define KPRINTN(base)	kprintn(put, ul, base, lflag, width)
82 #define RADJUSTZEROPAD()					\
83 do {								\
84 	if ((lflag & (ZEROPAD|LADJUST)) == ZEROPAD) {		\
85 		while (width-- > 0)				\
86 			put('0');				\
87 	}							\
88 } while (/*CONSTCOND*/0)
89 #define LADJUSTPAD()						\
90 do {								\
91 	if (lflag & LADJUST) {					\
92 		while (width-- > 0)				\
93 			put(' ');				\
94 	}							\
95 } while (/*CONSTCOND*/0)
96 #define RADJUSTPAD()						\
97 do {								\
98 	if ((lflag & (ZEROPAD|LADJUST)) == 0) {			\
99 		while (width-- > 0)				\
100 			put(' ');				\
101 	}							\
102 } while (/*CONSTCOND*/0)
103 #else	/* LIBSA_PRINTF_WIDTH_SUPPORT */
104 #define KPRINTN(base)	kprintn(put, ul, base)
105 #define RADJUSTZEROPAD()	/**/
106 #define LADJUSTPAD()		/**/
107 #define RADJUSTPAD()		/**/
108 #endif	/* LIBSA_PRINTF_WIDTH_SUPPORT */
109 
110 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
111 #define KPRINT(base)						\
112 do {								\
113 	ul = (lflag & LLONG)					\
114 	    ? va_arg(ap, u_longlong_t)				\
115 	    : (lflag & LONG)					\
116 		? va_arg(ap, u_long)				\
117 		: va_arg(ap, u_int);				\
118 	KPRINTN(base);						\
119 } while (/*CONSTCOND*/0)
120 #else	/* LIBSA_PRINTF_LONGLONG_SUPPORT */
121 #define KPRINT(base)						\
122 do {								\
123 	ul = (lflag & LONG)					\
124 	    ? va_arg(ap, u_long) : va_arg(ap, u_int);		\
125 	KPRINTN(base);						\
126 } while (/*CONSTCOND*/0)
127 #endif	/* LIBSA_PRINTF_LONGLONG_SUPPORT */
128 
129 static void
130 null_sputchar(int c)
131 {
132 	sbuf++;
133 }
134 
135 static void
136 sputchar(int c)
137 {
138 
139 	if (sbuf < ebuf)
140 		*sbuf++ = c;
141 }
142 
143 void
144 vprintf(const char *fmt, va_list ap)
145 {
146 
147 	kdoprnt(putchar, fmt, ap);
148 }
149 
150 int
151 vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
152 {
153 
154 	sbuf = buf;
155 	ebuf = buf + size - 1;
156 	kdoprnt(buf == NULL ? null_sputchar : sputchar, fmt, ap);
157 	if (buf != NULL)
158 		*sbuf = '\0';
159 	return sbuf - buf;
160 }
161 
162 static void
163 kdoprnt(void (*put)(int), const char *fmt, va_list ap)
164 {
165 	char *p;
166 	int ch;
167 	UINTMAX_T ul;
168 	int lflag;
169 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
170 	int width;
171 	char *q;
172 #endif
173 
174 	for (;;) {
175 		while ((ch = *fmt++) != '%') {
176 			if (ch == '\0')
177 				return;
178 			put(ch);
179 		}
180 		lflag = 0;
181 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
182 		width = 0;
183 #endif
184 reswitch:
185 		switch (ch = *fmt++) {
186 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
187 		case '#':
188 			lflag |= ALT;
189 			goto reswitch;
190 		case ' ':
191 			lflag |= SPACE;
192 			goto reswitch;
193 		case '-':
194 			lflag |= LADJUST;
195 			goto reswitch;
196 		case '+':
197 			lflag |= SIGN;
198 			goto reswitch;
199 		case '0':
200 			if (width == 0) {
201 				lflag |= ZEROPAD;
202 				goto reswitch;
203 			}
204 			/* FALLTHROUGH */
205 		case '1': case '2': case '3': case '4': case '5':
206 		case '6': case '7': case '8': case '9':
207 			for (;;) {
208 				width *= 10;
209 				width += ch - '0';
210 				ch = *fmt;
211 				if ((unsigned)ch - '0' > 9)
212 					break;
213 				++fmt;
214 			}
215 			goto reswitch;
216 #endif
217 		case 'l':
218 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
219 			if (*fmt == 'l') {
220 				++fmt;
221 				lflag |= LLONG;
222 			} else
223 #endif
224 				lflag |= LONG;
225 			goto reswitch;
226 		case 'j':
227 #ifdef LIBSA_PRINTF_LONGLONG_SUPPORT
228 			if (sizeof(intmax_t) == sizeof(long long))
229 				lflag |= LLONG;
230 			else
231 #endif
232 			if (sizeof(intmax_t) == sizeof(long))
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 			RADJUSTPAD();
249 			put(ch & 0xFF);
250 			LADJUSTPAD();
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 			RADJUSTPAD();
260 			while ((ch = (unsigned char)*p++))
261 				put(ch);
262 			LADJUSTPAD();
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 		default:
298 			if (ch == '\0')
299 				return;
300 			put(ch);
301 			break;
302 		}
303 	}
304 }
305 
306 static void
307 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
308 kprintn(void (*put)(int), UINTMAX_T ul, int base, int lflag, int width)
309 #else
310 kprintn(void (*put)(int), UINTMAX_T ul, int base)
311 #endif
312 {
313 					/* hold a INTMAX_T in base 8 */
314 	char *p, buf[(sizeof(INTMAX_T) * NBBY / 3) + 1 + 2 /* ALT + SIGN */];
315 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
316 	char *q;
317 #endif
318 
319 	p = buf;
320 	do {
321 		*p++ = hexdigits[ul % base];
322 	} while (ul /= base);
323 #ifdef LIBSA_PRINTF_WIDTH_SUPPORT
324 	q = p;
325 	if (lflag & ALT && *(p - 1) != '0') {
326 		if (base == 8) {
327 			*p++ = '0';
328 		} else if (base == 16) {
329 			*p++ = 'x';
330 			*p++ = '0';
331 		}
332 	}
333 	if (lflag & NEGATIVE)
334 		*p++ = '-';
335 	else if (lflag & SIGN)
336 		*p++ = '+';
337 	else if (lflag & SPACE)
338 		*p++ = ' ';
339 	width -= p - buf;
340 	if (lflag & ZEROPAD) {
341 		while (p > q)
342 			put(*--p);
343 	}
344 #endif
345 	RADJUSTPAD();
346 	RADJUSTZEROPAD();
347 	do {
348 		put(*--p);
349 	} while (p > buf);
350 	LADJUSTPAD();
351 }
352