xref: /netbsd-src/libexec/ld.elf_so/xprintf.c (revision d42a77727be99633563b3ae4d52fbeb25623d1c3)
1*d42a7772Schristos /*	$NetBSD: xprintf.c,v 1.23 2021/03/06 20:09:39 christos Exp $	 */
241fe218bScgd 
341fe218bScgd /*
441fe218bScgd  * Copyright 1996 Matt Thomas <matt@3am-software.com>
541fe218bScgd  * All rights reserved.
641fe218bScgd  *
741fe218bScgd  * Redistribution and use in source and binary forms, with or without
841fe218bScgd  * modification, are permitted provided that the following conditions
941fe218bScgd  * are met:
1041fe218bScgd  * 1. Redistributions of source code must retain the above copyright
1141fe218bScgd  *    notice, this list of conditions and the following disclaimer.
1241fe218bScgd  * 2. Redistributions in binary form must reproduce the above copyright
1341fe218bScgd  *    notice, this list of conditions and the following disclaimer in the
1441fe218bScgd  *    documentation and/or other materials provided with the distribution.
1541fe218bScgd  * 3. The name of the author may not be used to endorse or promote products
1641fe218bScgd  *    derived from this software without specific prior written permission.
1741fe218bScgd  *
1841fe218bScgd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1941fe218bScgd  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2041fe218bScgd  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2141fe218bScgd  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2241fe218bScgd  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2341fe218bScgd  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2441fe218bScgd  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2541fe218bScgd  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2641fe218bScgd  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2741fe218bScgd  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2841fe218bScgd  */
2941fe218bScgd 
3026475619Schristos #include <sys/cdefs.h>
312728318eSskrll #ifndef lint
32*d42a7772Schristos __RCSID("$NetBSD: xprintf.c,v 1.23 2021/03/06 20:09:39 christos Exp $");
332728318eSskrll #endif /* not lint */
342728318eSskrll 
3541fe218bScgd #include <string.h>
36c72bbb2cSmatt #include <stdlib.h>
3741fe218bScgd #include <unistd.h>
3841fe218bScgd #include <errno.h>
3926475619Schristos #include <stdarg.h>
4041fe218bScgd 
41fa344cccSmycroft #include "rtldenv.h"
42fa344cccSmycroft 
43ec2edc19Schristos #ifdef RTLD_LOADER
44bddb0d02Smycroft #define	SZ_LONG		0x01
45bddb0d02Smycroft #define	SZ_UNSIGNED	0x02
46496f9be3Schristos #define SZ_SIZE_T	0x04
47fed65863Schristos 
4841fe218bScgd /*
4941fe218bScgd  * Non-mallocing printf, for use by malloc and rtld itself.
5041fe218bScgd  * This avoids putting in most of stdio.
5141fe218bScgd  *
521cea4da7Suwe  * deals with formats %x, %p, %s, and %d.
5341fe218bScgd  */
5441fe218bScgd size_t
xvsnprintf(char * buf,size_t buflen,const char * fmt,va_list ap)555f573ab6Sskrll xvsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap)
5641fe218bScgd {
5741fe218bScgd 	char *bp = buf;
5841fe218bScgd 	char *const ep = buf + buflen - 4;
59f1740378Senami 	int size, prec;
6041fe218bScgd 
61f4edff94Seeh 	while (*fmt != '\0' && bp < ep) {
6241fe218bScgd 		switch (*fmt) {
6341fe218bScgd 		case '\\':{
6441fe218bScgd 			if (fmt[1] != '\0')
6541fe218bScgd 				*bp++ = *++fmt;
6641fe218bScgd 			continue;
6741fe218bScgd 		}
6841fe218bScgd 		case '%':{
69bddb0d02Smycroft 			size = 0;
70f1740378Senami 			prec = -1;
71fed65863Schristos 	rflag:		switch (fmt[1]) {
72f1740378Senami 			case '*':
73f1740378Senami 				prec = va_arg(ap, int);
74f1740378Senami 				/* FALLTHROUGH */
75f1740378Senami 			case '.':
76*d42a7772Schristos 			case '#':
77f1740378Senami 				fmt++;
78f1740378Senami 				goto rflag;
79fed65863Schristos 			case 'l':
80*d42a7772Schristos 			case 'j':
81bddb0d02Smycroft 				size |= SZ_LONG;
82fed65863Schristos 				fmt++;
83fed65863Schristos 				goto rflag;
84496f9be3Schristos 			case 'z':
85496f9be3Schristos 				size |= SZ_SIZE_T;
86496f9be3Schristos 				fmt++;
87496f9be3Schristos 				goto rflag;
88fed65863Schristos 			case 'u':
89fed65863Schristos 				size |= SZ_UNSIGNED;
90fed65863Schristos 				/* FALLTHROUGH */
91fed65863Schristos 			case 'd':{
92fa344cccSmycroft 				long sval;
93fa344cccSmycroft 				unsigned long uval;
9441fe218bScgd 				char digits[sizeof(int) * 3], *dp = digits;
952b907375Sscottr #define	SARG() \
96bddb0d02Smycroft (size & SZ_LONG ? va_arg(ap, long) : \
97fa64a5bfSchristos ((size & SZ_SIZE_T ? (long)va_arg(ap, size_t) : \
98496f9be3Schristos va_arg(ap, int))))
992b907375Sscottr #define	UARG() \
100bddb0d02Smycroft (size & SZ_LONG ? va_arg(ap, unsigned long) : \
101496f9be3Schristos ((size & SZ_SIZE_T ? va_arg(ap, size_t) : \
102496f9be3Schristos va_arg(ap, unsigned int))))
10341fe218bScgd 
10441fe218bScgd 				if (fmt[1] == 'd') {
105fa64a5bfSchristos 					if (size & SZ_UNSIGNED)
106fa64a5bfSchristos 						sval = UARG();
107fa64a5bfSchristos 					else
108fa64a5bfSchristos 						sval = SARG();
109fed65863Schristos 					if (sval < 0) {
110fed65863Schristos 						if ((sval << 1) == 0) {
11141fe218bScgd 							/*
11226475619Schristos 							 * We can't flip the
11326475619Schristos 							 * sign of this since
11426475619Schristos 							 * it can't be
11526475619Schristos 							 * represented as a
11626475619Schristos 							 * positive number in
11726475619Schristos 							 * two complement,
11826475619Schristos 							 * handle the first
11926475619Schristos 							 * digit. After that,
12026475619Schristos 							 * it can be flipped
12126475619Schristos 							 * since it is now not
12226475619Schristos 							 * 2^(n-1).
12341fe218bScgd 							 */
124fed65863Schristos 							*dp++ = '0'-(sval % 10);
125fed65863Schristos 							sval /= 10;
12641fe218bScgd 						}
12741fe218bScgd 						*bp++ = '-';
128fed65863Schristos 						uval = -sval;
12941fe218bScgd 					} else {
130fed65863Schristos 						uval = sval;
13141fe218bScgd 					}
13241fe218bScgd 				} else {
133fa64a5bfSchristos 					if (size & SZ_UNSIGNED)
134fa64a5bfSchristos 						uval = UARG();
135fa64a5bfSchristos 					else
136fa64a5bfSchristos 						uval = SARG();
13741fe218bScgd 				}
13841fe218bScgd 				do {
13941fe218bScgd 					*dp++ = '0' + (uval % 10);
14041fe218bScgd 					uval /= 10;
14141fe218bScgd 				} while (uval != 0);
14241fe218bScgd 				do {
14341fe218bScgd 					*bp++ = *--dp;
14441fe218bScgd 				} while (dp != digits && bp < ep);
14541fe218bScgd 				fmt += 2;
14641fe218bScgd 				break;
14741fe218bScgd 			}
14826475619Schristos 			case 'x':
14926475619Schristos 			case 'p':{
15041fe218bScgd 				unsigned long val = va_arg(ap, unsigned long);
15141fe218bScgd 				unsigned long mask = ~(~0UL >> 4);
15241fe218bScgd 				int bits = sizeof(val) * 8 - 4;
15341fe218bScgd 				const char hexdigits[] = "0123456789abcdef";
15441fe218bScgd 				if (fmt[1] == 'p') {
15541fe218bScgd 					*bp++ = '0';
15641fe218bScgd 					*bp++ = 'x';
15741fe218bScgd 				}
15841fe218bScgd 				/* handle the border case */
15941fe218bScgd 				if (val == 0) {
16041fe218bScgd 					*bp++ = '0';
16141fe218bScgd 					fmt += 2;
16241fe218bScgd 					break;
16341fe218bScgd 				}
16441fe218bScgd 				/* suppress 0s */
16541fe218bScgd 				while ((val & mask) == 0)
16641fe218bScgd 					bits -= 4, mask >>= 4;
16741fe218bScgd 
16841fe218bScgd 				/* emit the hex digits */
16941fe218bScgd 				while (bits >= 0 && bp < ep) {
17041fe218bScgd 					*bp++ = hexdigits[(val & mask) >> bits];
17141fe218bScgd 					bits -= 4, mask >>= 4;
17241fe218bScgd 				}
17341fe218bScgd 				fmt += 2;
17441fe218bScgd 				break;
17541fe218bScgd 			}
17641fe218bScgd 			case 's':{
17741fe218bScgd 				const char *str = va_arg(ap, const char *);
17841fe218bScgd 				int len;
17926475619Schristos 
18041fe218bScgd 				if (str == NULL)
18141fe218bScgd 					str = "(null)";
18241fe218bScgd 
183f1740378Senami 				if (prec < 0)
18441fe218bScgd 					len = strlen(str);
185f1740378Senami 				else
186f1740378Senami 					len = prec;
18741fe218bScgd 				if (ep - bp < len)
18841fe218bScgd 					len = ep - bp;
18941fe218bScgd 				memcpy(bp, str, len);
19041fe218bScgd 				bp += len;
19141fe218bScgd 				fmt += 2;
19241fe218bScgd 				break;
19341fe218bScgd 			}
19401592c7cSchristos 			case 'c':{
19501592c7cSchristos 				int c = va_arg(ap, int);
19601592c7cSchristos 				*bp++ = (char)c;
19701592c7cSchristos 				fmt += 2;
19801592c7cSchristos 				break;
19901592c7cSchristos 			}
20041fe218bScgd 			default:
20141fe218bScgd 				*bp++ = *fmt;
20241fe218bScgd 				break;
20341fe218bScgd 			}
20441fe218bScgd 			break;
20541fe218bScgd 		}
20641fe218bScgd 		default:
20741fe218bScgd 			*bp++ = *fmt++;
20841fe218bScgd 			break;
20941fe218bScgd 		}
21041fe218bScgd 	}
21141fe218bScgd 
21241fe218bScgd 	*bp = '\0';
21341fe218bScgd 	return bp - buf;
21441fe218bScgd }
21541fe218bScgd 
21641fe218bScgd void
xvprintf(const char * fmt,va_list ap)2175f573ab6Sskrll xvprintf(const char *fmt, va_list ap)
21841fe218bScgd {
21941fe218bScgd 	char buf[256];
220a9f5b3f8Ssimonb 
22141fe218bScgd 	(void) write(2, buf, xvsnprintf(buf, sizeof(buf), fmt, ap));
22241fe218bScgd }
22341fe218bScgd 
22441fe218bScgd void
xprintf(const char * fmt,...)22526475619Schristos xprintf(const char *fmt, ...)
22641fe218bScgd {
22741fe218bScgd 	va_list ap;
22826475619Schristos 
22941fe218bScgd 	va_start(ap, fmt);
23041fe218bScgd 
23141fe218bScgd 	xvprintf(fmt, ap);
23241fe218bScgd 
23341fe218bScgd 	va_end(ap);
23441fe218bScgd }
23541fe218bScgd 
23641fe218bScgd void
xsnprintf(char * buf,size_t buflen,const char * fmt,...)23726475619Schristos xsnprintf(char *buf, size_t buflen, const char *fmt, ...)
23841fe218bScgd {
23941fe218bScgd 	va_list ap;
24041fe218bScgd 
241bf840df2Swiz 	va_start(ap, fmt);
242bf840df2Swiz 
24326475619Schristos 	xvsnprintf(buf, buflen, fmt, ap);
24441fe218bScgd 
24541fe218bScgd 	va_end(ap);
24641fe218bScgd }
24741fe218bScgd 
248c4120e32Sjoerg #include "errlist_concat.h"
249c4120e32Sjoerg 
25041fe218bScgd const char *
xstrerror(int error)2515f573ab6Sskrll xstrerror(int error)
25241fe218bScgd {
253a9f5b3f8Ssimonb 
254c4120e32Sjoerg 	if (error >= concat_nerr || error < 0) {
25541fe218bScgd 		static char buf[128];
25641fe218bScgd 		xsnprintf(buf, sizeof(buf), "Unknown error: %d", error);
25741fe218bScgd 		return buf;
25841fe218bScgd 	}
259c4120e32Sjoerg 	return concat_errlist + concat_offset[error];
26041fe218bScgd }
26141fe218bScgd 
26241fe218bScgd void
xerrx(int eval,const char * fmt,...)26326475619Schristos xerrx(int eval, const char *fmt, ...)
26441fe218bScgd {
26541fe218bScgd 	va_list ap;
266bf840df2Swiz 
26741fe218bScgd 	va_start(ap, fmt);
26841fe218bScgd 	xvprintf(fmt, ap);
26941fe218bScgd 	va_end(ap);
2703b473ec2Slukem 	(void) write(2, "\n", 1);
27141fe218bScgd 
27241fe218bScgd 	exit(eval);
27341fe218bScgd }
27441fe218bScgd 
27541fe218bScgd void
xerr(int eval,const char * fmt,...)27626475619Schristos xerr(int eval, const char *fmt, ...)
27741fe218bScgd {
27841fe218bScgd 	int saved_errno = errno;
27941fe218bScgd 	va_list ap;
28026475619Schristos 
281bf840df2Swiz 	va_start(ap, fmt);
28241fe218bScgd 	xvprintf(fmt, ap);
28341fe218bScgd 	va_end(ap);
28441fe218bScgd 
28541fe218bScgd 	xprintf(": %s\n", xstrerror(saved_errno));
28641fe218bScgd 	exit(eval);
28741fe218bScgd }
28841fe218bScgd 
28941fe218bScgd void
xwarn(const char * fmt,...)29026475619Schristos xwarn(const char *fmt, ...)
29141fe218bScgd {
29241fe218bScgd 	int saved_errno = errno;
29341fe218bScgd 	va_list ap;
29426475619Schristos 
295bf840df2Swiz 	va_start(ap, fmt);
29641fe218bScgd 	xvprintf(fmt, ap);
29741fe218bScgd 	va_end(ap);
29841fe218bScgd 
29941fe218bScgd 	xprintf(": %s\n", xstrerror(saved_errno));
30041fe218bScgd 	errno = saved_errno;
30141fe218bScgd }
30241fe218bScgd 
30341fe218bScgd void
xwarnx(const char * fmt,...)30426475619Schristos xwarnx(const char *fmt, ...)
30541fe218bScgd {
30641fe218bScgd 	va_list ap;
30726475619Schristos 
308bf840df2Swiz 	va_start(ap, fmt);
30941fe218bScgd 	xvprintf(fmt, ap);
31041fe218bScgd 	va_end(ap);
3113b473ec2Slukem 	(void) write(2, "\n", 1);
31241fe218bScgd }
31341fe218bScgd 
314fa5df294Smycroft #ifdef DEBUG
31541fe218bScgd void
xassert(const char * file,int line,const char * failedexpr)3165f573ab6Sskrll xassert(const char *file, int line, const char *failedexpr)
31741fe218bScgd {
318a9f5b3f8Ssimonb 
31941fe218bScgd 	xprintf("assertion \"%s\" failed: file \"%s\", line %d\n",
32041fe218bScgd 		failedexpr, file, line);
32141fe218bScgd 	abort();
32241fe218bScgd 	/* NOTREACHED */
32341fe218bScgd }
324ec2edc19Schristos #endif
325fa5df294Smycroft #endif
326