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