xref: /freebsd/libexec/rtld-elf/rtld_printf.c (revision 9b6dd12e5da17c85b9e93f1ed31ec8719e6f40db)
1 /*-
2  * Copyright (c) 1986, 1988, 1991, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  * Copyright (c) 2011 Konstantin Belousov <kib@FreeBSD.org>
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  * $FreeBSD$
36  */
37 
38 #include <sys/param.h>
39 #include <ctype.h>
40 #include <inttypes.h>
41 #include <stdarg.h>
42 #include <stddef.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include "rtld_printf.h"
46 
47 #define MAXNBUF	(sizeof(intmax_t) * NBBY + 1)
48 
49 struct snprintf_arg {
50 	char	*str;
51 	char	*buf;
52 	size_t	remain;
53 	size_t	buf_total;
54 	int	fd;
55 };
56 
57 static void
58 snprintf_func(int ch, struct snprintf_arg *const info)
59 {
60 
61 	if (info->remain >= 2) {
62 		*info->str++ = ch;
63 		info->remain--;
64 	}
65 }
66 
67 static void
68 printf_out(struct snprintf_arg *info)
69 {
70 
71 	if (info->remain == info->buf_total)
72 		return;
73 	write(info->fd, info->buf, info->buf_total - info->remain);
74 	info->str = info->buf;
75 	info->remain = info->buf_total;
76 }
77 
78 static void
79 printf_func(int ch, struct snprintf_arg *const info)
80 {
81 
82 	if (info->remain > 0) {
83 		*info->str++ = ch;
84 		info->remain--;
85 	} else
86 		printf_out(info);
87 }
88 
89 static char const hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz";
90 #define	hex2ascii(hex)	(hex2ascii_data[hex])
91 
92 static __inline int
93 imax(int a, int b)
94 {
95 
96 	return (a > b ? a : b);
97 }
98 
99 static char *
100 ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper)
101 {
102 	char *p, c;
103 
104 	p = nbuf;
105 	*p = '\0';
106 	do {
107 		c = hex2ascii(num % base);
108 		*++p = upper ? toupper(c) : c;
109 	} while (num /= base);
110 	if (lenp)
111 		*lenp = p - nbuf;
112 	return (p);
113 }
114 
115 static int
116 kvprintf(char const *fmt, void (*func)(int c, struct snprintf_arg *const arg),
117     struct snprintf_arg *arg, int radix, va_list ap)
118 {
119 #define PCHAR(c) func((c), arg)
120 	char nbuf[MAXNBUF];
121 	const char *p, *percent, *q;
122 	u_char *up;
123 	int ch, n;
124 	uintmax_t num;
125 	int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot;
126 	int cflag, hflag, jflag, tflag, zflag;
127 	int dwidth, upper;
128 	char padc;
129 	int stop = 0, retval = 0;
130 
131 	num = 0;
132 
133 	if (fmt == NULL)
134 		fmt = "(fmt null)\n";
135 
136 	if (radix < 2 || radix > 36)
137 		radix = 10;
138 
139 	for (;;) {
140 		padc = ' ';
141 		width = 0;
142 		while ((ch = (u_char)*fmt++) != '%' || stop) {
143 			if (ch == '\0')
144 				return (retval);
145 			PCHAR(ch);
146 		}
147 		percent = fmt - 1;
148 		qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0;
149 		sign = 0; dot = 0; dwidth = 0; upper = 0;
150 		cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0;
151 reswitch:	switch (ch = (u_char)*fmt++) {
152 		case '.':
153 			dot = 1;
154 			goto reswitch;
155 		case '#':
156 			sharpflag = 1;
157 			goto reswitch;
158 		case '+':
159 			sign = 1;
160 			goto reswitch;
161 		case '-':
162 			ladjust = 1;
163 			goto reswitch;
164 		case '%':
165 			PCHAR(ch);
166 			break;
167 		case '*':
168 			if (!dot) {
169 				width = va_arg(ap, int);
170 				if (width < 0) {
171 					ladjust = !ladjust;
172 					width = -width;
173 				}
174 			} else {
175 				dwidth = va_arg(ap, int);
176 			}
177 			goto reswitch;
178 		case '0':
179 			if (!dot) {
180 				padc = '0';
181 				goto reswitch;
182 			}
183 		case '1': case '2': case '3': case '4':
184 		case '5': case '6': case '7': case '8': case '9':
185 				for (n = 0;; ++fmt) {
186 					n = n * 10 + ch - '0';
187 					ch = *fmt;
188 					if (ch < '0' || ch > '9')
189 						break;
190 				}
191 			if (dot)
192 				dwidth = n;
193 			else
194 				width = n;
195 			goto reswitch;
196 		case 'b':
197 			num = (u_int)va_arg(ap, int);
198 			p = va_arg(ap, char *);
199 			for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;)
200 				PCHAR(*q--);
201 
202 			if (num == 0)
203 				break;
204 
205 			for (tmp = 0; *p;) {
206 				n = *p++;
207 				if (num & (1 << (n - 1))) {
208 					PCHAR(tmp ? ',' : '<');
209 					for (; (n = *p) > ' '; ++p)
210 						PCHAR(n);
211 					tmp = 1;
212 				} else
213 					for (; *p > ' '; ++p)
214 						continue;
215 			}
216 			if (tmp)
217 				PCHAR('>');
218 			break;
219 		case 'c':
220 			PCHAR(va_arg(ap, int));
221 			break;
222 		case 'D':
223 			up = va_arg(ap, u_char *);
224 			p = va_arg(ap, char *);
225 			if (!width)
226 				width = 16;
227 			while(width--) {
228 				PCHAR(hex2ascii(*up >> 4));
229 				PCHAR(hex2ascii(*up & 0x0f));
230 				up++;
231 				if (width)
232 					for (q=p;*q;q++)
233 						PCHAR(*q);
234 			}
235 			break;
236 		case 'd':
237 		case 'i':
238 			base = 10;
239 			sign = 1;
240 			goto handle_sign;
241 		case 'h':
242 			if (hflag) {
243 				hflag = 0;
244 				cflag = 1;
245 			} else
246 				hflag = 1;
247 			goto reswitch;
248 		case 'j':
249 			jflag = 1;
250 			goto reswitch;
251 		case 'l':
252 			if (lflag) {
253 				lflag = 0;
254 				qflag = 1;
255 			} else
256 				lflag = 1;
257 			goto reswitch;
258 		case 'n':
259 			if (jflag)
260 				*(va_arg(ap, intmax_t *)) = retval;
261 			else if (qflag)
262 				*(va_arg(ap, quad_t *)) = retval;
263 			else if (lflag)
264 				*(va_arg(ap, long *)) = retval;
265 			else if (zflag)
266 				*(va_arg(ap, size_t *)) = retval;
267 			else if (hflag)
268 				*(va_arg(ap, short *)) = retval;
269 			else if (cflag)
270 				*(va_arg(ap, char *)) = retval;
271 			else
272 				*(va_arg(ap, int *)) = retval;
273 			break;
274 		case 'o':
275 			base = 8;
276 			goto handle_nosign;
277 		case 'p':
278 			base = 16;
279 			sharpflag = (width == 0);
280 			sign = 0;
281 			num = (uintptr_t)va_arg(ap, void *);
282 			goto number;
283 		case 'q':
284 			qflag = 1;
285 			goto reswitch;
286 		case 'r':
287 			base = radix;
288 			if (sign)
289 				goto handle_sign;
290 			goto handle_nosign;
291 		case 's':
292 			p = va_arg(ap, char *);
293 			if (p == NULL)
294 				p = "(null)";
295 			if (!dot)
296 				n = strlen (p);
297 			else
298 				for (n = 0; n < dwidth && p[n]; n++)
299 					continue;
300 
301 			width -= n;
302 
303 			if (!ladjust && width > 0)
304 				while (width--)
305 					PCHAR(padc);
306 			while (n--)
307 				PCHAR(*p++);
308 			if (ladjust && width > 0)
309 				while (width--)
310 					PCHAR(padc);
311 			break;
312 		case 't':
313 			tflag = 1;
314 			goto reswitch;
315 		case 'u':
316 			base = 10;
317 			goto handle_nosign;
318 		case 'X':
319 			upper = 1;
320 		case 'x':
321 			base = 16;
322 			goto handle_nosign;
323 		case 'y':
324 			base = 16;
325 			sign = 1;
326 			goto handle_sign;
327 		case 'z':
328 			zflag = 1;
329 			goto reswitch;
330 handle_nosign:
331 			sign = 0;
332 			if (jflag)
333 				num = va_arg(ap, uintmax_t);
334 			else if (qflag)
335 				num = va_arg(ap, u_quad_t);
336 			else if (tflag)
337 				num = va_arg(ap, ptrdiff_t);
338 			else if (lflag)
339 				num = va_arg(ap, u_long);
340 			else if (zflag)
341 				num = va_arg(ap, size_t);
342 			else if (hflag)
343 				num = (u_short)va_arg(ap, int);
344 			else if (cflag)
345 				num = (u_char)va_arg(ap, int);
346 			else
347 				num = va_arg(ap, u_int);
348 			goto number;
349 handle_sign:
350 			if (jflag)
351 				num = va_arg(ap, intmax_t);
352 			else if (qflag)
353 				num = va_arg(ap, quad_t);
354 			else if (tflag)
355 				num = va_arg(ap, ptrdiff_t);
356 			else if (lflag)
357 				num = va_arg(ap, long);
358 			else if (zflag)
359 				num = va_arg(ap, ssize_t);
360 			else if (hflag)
361 				num = (short)va_arg(ap, int);
362 			else if (cflag)
363 				num = (char)va_arg(ap, int);
364 			else
365 				num = va_arg(ap, int);
366 number:
367 			if (sign && (intmax_t)num < 0) {
368 				neg = 1;
369 				num = -(intmax_t)num;
370 			}
371 			p = ksprintn(nbuf, num, base, &n, upper);
372 			tmp = 0;
373 			if (sharpflag && num != 0) {
374 				if (base == 8)
375 					tmp++;
376 				else if (base == 16)
377 					tmp += 2;
378 			}
379 			if (neg)
380 				tmp++;
381 
382 			if (!ladjust && padc == '0')
383 				dwidth = width - tmp;
384 			width -= tmp + imax(dwidth, n);
385 			dwidth -= n;
386 			if (!ladjust)
387 				while (width-- > 0)
388 					PCHAR(' ');
389 			if (neg)
390 				PCHAR('-');
391 			if (sharpflag && num != 0) {
392 				if (base == 8) {
393 					PCHAR('0');
394 				} else if (base == 16) {
395 					PCHAR('0');
396 					PCHAR('x');
397 				}
398 			}
399 			while (dwidth-- > 0)
400 				PCHAR('0');
401 
402 			while (*p)
403 				PCHAR(*p--);
404 
405 			if (ladjust)
406 				while (width-- > 0)
407 					PCHAR(' ');
408 
409 			break;
410 		default:
411 			while (percent < fmt)
412 				PCHAR(*percent++);
413 			/*
414 			 * Since we ignore an formatting argument it is no
415 			 * longer safe to obey the remaining formatting
416 			 * arguments as the arguments will no longer match
417 			 * the format specs.
418 			 */
419 			stop = 1;
420 			break;
421 		}
422 	}
423 #undef PCHAR
424 }
425 
426 int
427 rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap)
428 {
429 	struct snprintf_arg info;
430 	int retval;
431 
432 	info.buf = info.str = buf;
433 	info.buf_total = info.remain = bufsize;
434 	info.fd = -1;
435 	retval = kvprintf(fmt, snprintf_func, &info, 10, ap);
436 	if (info.remain >= 1)
437 		*info.str++ = '\0';
438 	return (retval);
439 }
440 
441 int
442 rtld_vfdprintf(int fd, const char *fmt, va_list ap)
443 {
444 	char buf[512];
445 	struct snprintf_arg info;
446 	int retval;
447 
448 	info.buf = info.str = buf;
449 	info.buf_total = info.remain = sizeof(buf);
450 	info.fd = fd;
451 	retval = kvprintf(fmt, printf_func, &info, 10, ap);
452 	printf_out(&info);
453 	return (retval);
454 }
455 
456 int
457 rtld_fdprintf(int fd, const char *fmt, ...)
458 {
459 	va_list ap;
460 	int retval;
461 
462 	va_start(ap, fmt);
463 	retval = rtld_vfdprintf(fd, fmt, ap);
464 	va_end(ap);
465 	return (retval);
466 }
467 
468 void
469 rtld_fdputstr(int fd, const char *str)
470 {
471 
472 	write(fd, str, strlen(str));
473 }
474 
475 void
476 rtld_fdputchar(int fd, int c)
477 {
478 	char c1;
479 
480 	c1 = c;
481 	write(fd, &c1, 1);
482 }
483