xref: /openbsd-src/usr.sbin/btrace/printf.c (revision 6243ff0b1bb0d49cdafa445b5ba769db252722c7)
1*6243ff0bSmpi /*	$OpenBSD: printf.c,v 1.3 2024/09/06 07:58:50 mpi Exp $	*/
223160851Smpi 
323160851Smpi /*
423160851Smpi  * Copyright (c) 1989 The Regents of the University of California.
523160851Smpi  * All rights reserved.
623160851Smpi  *
723160851Smpi  * Redistribution and use in source and binary forms, with or without
823160851Smpi  * modification, are permitted provided that the following conditions
923160851Smpi  * are met:
1023160851Smpi  * 1. Redistributions of source code must retain the above copyright
1123160851Smpi  *    notice, this list of conditions and the following disclaimer.
1223160851Smpi  * 2. Redistributions in binary form must reproduce the above copyright
1323160851Smpi  *    notice, this list of conditions and the following disclaimer in the
1423160851Smpi  *    documentation and/or other materials provided with the distribution.
1523160851Smpi  * 3. Neither the name of the University nor the names of its contributors
1623160851Smpi  *    may be used to endorse or promote products derived from this software
1723160851Smpi  *    without specific prior written permission.
1823160851Smpi  *
1923160851Smpi  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2023160851Smpi  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2123160851Smpi  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2223160851Smpi  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2323160851Smpi  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2423160851Smpi  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2523160851Smpi  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2623160851Smpi  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2723160851Smpi  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2823160851Smpi  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2923160851Smpi  * SUCH DAMAGE.
3023160851Smpi  */
3123160851Smpi 
3223160851Smpi #include <sys/queue.h>
3323160851Smpi 
3423160851Smpi #include <ctype.h>
3523160851Smpi #include <err.h>
3623160851Smpi #include <errno.h>
3723160851Smpi #include <limits.h>
3823160851Smpi #include <stdio.h>
3923160851Smpi #include <stdlib.h>
4023160851Smpi #include <string.h>
4123160851Smpi #include <unistd.h>
4223160851Smpi 
4323160851Smpi #include <dev/dt/dtvar.h>
4423160851Smpi #include "bt_parser.h"
4523160851Smpi 
4623160851Smpi #include "btrace.h"
4723160851Smpi 
4823160851Smpi static int	 print_escape_str(const char *);
4923160851Smpi static int	 print_escape(const char *);
5023160851Smpi 
5123160851Smpi static int	 getchr(void);
5223160851Smpi static double	 getdouble(void);
5323160851Smpi static int	 getint(void);
5423160851Smpi static long	 getlong(void);
5523160851Smpi static unsigned long getulong(void);
5623160851Smpi static const char *getstr(void);
5723160851Smpi static char	*mklong(const char *, int);
5823160851Smpi static void      check_conversion(const char *, const char *);
5923160851Smpi 
6023160851Smpi static int		 rval;
6123160851Smpi static struct bt_arg	*gargv;
6223160851Smpi static struct dt_evt	*gdevt;
6323160851Smpi 
6423160851Smpi #define isodigit(c)	((c) >= '0' && (c) <= '7')
6523160851Smpi #define octtobin(c)	((c) - '0')
6623160851Smpi #define hextobin(c)	((c) >= 'A' && (c) <= 'F' ? c - 'A' + 10 : (c) >= 'a' && (c) <= 'f' ? c - 'a' + 10 : c - '0')
6723160851Smpi 
6823160851Smpi #define PF(f, func) { \
6923160851Smpi 	if (havefieldwidth) \
7023160851Smpi 		if (haveprecision) \
7123160851Smpi 			(void)printf(f, fieldwidth, precision, func); \
7223160851Smpi 		else \
7323160851Smpi 			(void)printf(f, fieldwidth, func); \
7423160851Smpi 	else if (haveprecision) \
7523160851Smpi 		(void)printf(f, precision, func); \
7623160851Smpi 	else \
7723160851Smpi 		(void)printf(f, func); \
7823160851Smpi }
7923160851Smpi 
8023160851Smpi int
8123160851Smpi stmt_printf(struct bt_stmt *bs, struct dt_evt *des)
8223160851Smpi {
8323160851Smpi 	struct bt_arg *ba = SLIST_FIRST(&bs->bs_args);
8423160851Smpi 	char *fmt, *start;
8523160851Smpi 	int havefieldwidth, haveprecision;
8623160851Smpi 	int fieldwidth, precision;
8723160851Smpi 	char convch, nextch;
8823160851Smpi 	char *format;
8923160851Smpi 
9023160851Smpi 	format = (char *)ba2str(ba, gdevt); // XXX modification?
9123160851Smpi 	gargv = SLIST_NEXT(ba, ba_next);
9223160851Smpi 	gdevt = des;
9323160851Smpi 
9423160851Smpi #define SKIP1	"#-+ 0"
9523160851Smpi #define SKIP2	"0123456789"
9623160851Smpi 	do {
9723160851Smpi 		/*
9823160851Smpi 		 * Basic algorithm is to scan the format string for conversion
9923160851Smpi 		 * specifications -- once one is found, find out if the field
10023160851Smpi 		 * width or precision is a '*'; if it is, gather up value.
10123160851Smpi 		 * Note, format strings are reused as necessary to use up the
10223160851Smpi 		 * provided arguments, arguments of zero/null string are
10323160851Smpi 		 * provided to use up the format string.
10423160851Smpi 		 */
10523160851Smpi 
10623160851Smpi 		/* find next format specification */
10723160851Smpi 		for (fmt = format; *fmt; fmt++) {
10823160851Smpi 			switch (*fmt) {
10923160851Smpi 			case '%':
11023160851Smpi 				start = fmt++;
11123160851Smpi 
11223160851Smpi 				if (*fmt == '%') {
11323160851Smpi 					putchar ('%');
11423160851Smpi 					break;
11523160851Smpi 				} else if (*fmt == 'b') {
11623160851Smpi 					const char *p = getstr();
11723160851Smpi 					if (print_escape_str(p)) {
11823160851Smpi 						return (rval);
11923160851Smpi 					}
12023160851Smpi 					break;
12123160851Smpi 				}
12223160851Smpi 
12323160851Smpi 				/* skip to field width */
12423160851Smpi 				for (; strchr(SKIP1, *fmt); ++fmt)
12523160851Smpi 					;
12623160851Smpi 				if (*fmt == '*') {
12723160851Smpi 					++fmt;
12823160851Smpi 					havefieldwidth = 1;
12923160851Smpi 					fieldwidth = getint();
13023160851Smpi 				} else
13123160851Smpi 					havefieldwidth = 0;
13223160851Smpi 
13323160851Smpi 				/* skip to field precision */
13423160851Smpi 				for (; strchr(SKIP2, *fmt); ++fmt)
13523160851Smpi 					;
13623160851Smpi 				haveprecision = 0;
13723160851Smpi 				if (*fmt == '.') {
13823160851Smpi 					++fmt;
13923160851Smpi 					if (*fmt == '*') {
14023160851Smpi 						++fmt;
14123160851Smpi 						haveprecision = 1;
14223160851Smpi 						precision = getint();
14323160851Smpi 					}
14423160851Smpi 					for (; strchr(SKIP2, *fmt); ++fmt)
14523160851Smpi 						;
14623160851Smpi 				}
14723160851Smpi 
14823160851Smpi 				if (!*fmt) {
14923160851Smpi 					warnx ("missing format character");
15023160851Smpi 					return(1);
15123160851Smpi 				}
15223160851Smpi 
15323160851Smpi 				convch = *fmt;
15423160851Smpi 				nextch = *(fmt + 1);
15523160851Smpi 				*(fmt + 1) = '\0';
15623160851Smpi 				switch(convch) {
15723160851Smpi 				case 'c': {
15823160851Smpi 					char p = getchr();
15923160851Smpi 					PF(start, p);
16023160851Smpi 					break;
16123160851Smpi 				}
16223160851Smpi 				case 's': {
16323160851Smpi 					const char *p = getstr();
16423160851Smpi 					PF(start, p);
16523160851Smpi 					break;
16623160851Smpi 				}
16723160851Smpi 				case 'd':
16823160851Smpi 				case 'i': {
16923160851Smpi 					long p;
17023160851Smpi 					char *f = mklong(start, convch);
17123160851Smpi 					if (!f) {
17223160851Smpi 						warnx("out of memory");
17323160851Smpi 						return (1);
17423160851Smpi 					}
17523160851Smpi 					p = getlong();
17623160851Smpi 					PF(f, p);
17723160851Smpi 					break;
17823160851Smpi 				}
17923160851Smpi 				case 'o':
18023160851Smpi 				case 'u':
18123160851Smpi 				case 'x':
18223160851Smpi 				case 'X': {
18323160851Smpi 					unsigned long p;
18423160851Smpi 					char *f = mklong(start, convch);
18523160851Smpi 					if (!f) {
18623160851Smpi 						warnx("out of memory");
18723160851Smpi 						return (1);
18823160851Smpi 					}
18923160851Smpi 					p = getulong();
19023160851Smpi 					PF(f, p);
19123160851Smpi 					break;
19223160851Smpi 				}
19323160851Smpi 				case 'a':
19423160851Smpi 				case 'A':
19523160851Smpi 				case 'e':
19623160851Smpi 				case 'E':
19723160851Smpi 				case 'f':
19823160851Smpi 				case 'F':
19923160851Smpi 				case 'g':
20023160851Smpi 				case 'G': {
20123160851Smpi 					double p = getdouble();
20223160851Smpi 					PF(start, p);
20323160851Smpi 					break;
20423160851Smpi 				}
20523160851Smpi 				default:
20623160851Smpi 					warnx ("%s: invalid directive", start);
20723160851Smpi 					return(1);
20823160851Smpi 				}
20923160851Smpi 				*(fmt + 1) = nextch;
21023160851Smpi 				break;
21123160851Smpi 
21223160851Smpi 			case '\\':
21323160851Smpi 				fmt += print_escape(fmt);
21423160851Smpi 				break;
21523160851Smpi 
21623160851Smpi 			default:
21723160851Smpi 				putchar (*fmt);
21823160851Smpi 				break;
21923160851Smpi 			}
22023160851Smpi 		}
22123160851Smpi 	} while (gargv != NULL);
22223160851Smpi 
22323160851Smpi 	return (rval);
22423160851Smpi }
22523160851Smpi 
22623160851Smpi 
22723160851Smpi /*
22823160851Smpi  * Print SysV echo(1) style escape string
22923160851Smpi  *	Halts processing string and returns 1 if a \c escape is encountered.
23023160851Smpi  */
23123160851Smpi static int
23223160851Smpi print_escape_str(const char *str)
23323160851Smpi {
23423160851Smpi 	int value;
23523160851Smpi 	int c;
23623160851Smpi 
23723160851Smpi 	while (*str) {
23823160851Smpi 		if (*str == '\\') {
23923160851Smpi 			str++;
24023160851Smpi 			/*
24123160851Smpi 			 * %b string octal constants are not like those in C.
24223160851Smpi 			 * They start with a \0, and are followed by 0, 1, 2,
24323160851Smpi 			 * or 3 octal digits.
24423160851Smpi 			 */
24523160851Smpi 			if (*str == '0') {
24623160851Smpi 				str++;
24723160851Smpi 				for (c = 3, value = 0; c-- && isodigit(*str); str++) {
24823160851Smpi 					value <<= 3;
24923160851Smpi 					value += octtobin(*str);
25023160851Smpi 				}
25123160851Smpi 				putchar (value);
25223160851Smpi 				str--;
25323160851Smpi 			} else if (*str == 'c') {
25423160851Smpi 				return 1;
25523160851Smpi 			} else {
25623160851Smpi 				str--;
25723160851Smpi 				str += print_escape(str);
25823160851Smpi 			}
25923160851Smpi 		} else {
26023160851Smpi 			putchar (*str);
26123160851Smpi 		}
26223160851Smpi 		str++;
26323160851Smpi 	}
26423160851Smpi 
26523160851Smpi 	return 0;
26623160851Smpi }
26723160851Smpi 
26823160851Smpi /*
26923160851Smpi  * Print "standard" escape characters
27023160851Smpi  */
27123160851Smpi static int
27223160851Smpi print_escape(const char *str)
27323160851Smpi {
27423160851Smpi 	const char *start = str;
27523160851Smpi 	int value;
27623160851Smpi 	int c;
27723160851Smpi 
27823160851Smpi 	str++;
27923160851Smpi 
28023160851Smpi 	switch (*str) {
28123160851Smpi 	case '0': case '1': case '2': case '3':
28223160851Smpi 	case '4': case '5': case '6': case '7':
28323160851Smpi 		for (c = 3, value = 0; c-- && isodigit(*str); str++) {
28423160851Smpi 			value <<= 3;
28523160851Smpi 			value += octtobin(*str);
28623160851Smpi 		}
28723160851Smpi 		putchar(value);
28823160851Smpi 		return str - start - 1;
28923160851Smpi 		/* NOTREACHED */
29023160851Smpi 
29123160851Smpi 	case 'x':
29223160851Smpi 		str++;
29323160851Smpi 		for (value = 0; isxdigit((unsigned char)*str); str++) {
29423160851Smpi 			value <<= 4;
29523160851Smpi 			value += hextobin(*str);
29623160851Smpi 		}
29723160851Smpi 		if (value > UCHAR_MAX) {
29823160851Smpi 			warnx ("escape sequence out of range for character");
29923160851Smpi 			rval = 1;
30023160851Smpi 		}
30123160851Smpi 		putchar (value);
30223160851Smpi 		return str - start - 1;
30323160851Smpi 		/* NOTREACHED */
30423160851Smpi 
30523160851Smpi 	case '\\':			/* backslash */
30623160851Smpi 		putchar('\\');
30723160851Smpi 		break;
30823160851Smpi 
30923160851Smpi 	case '\'':			/* single quote */
31023160851Smpi 		putchar('\'');
31123160851Smpi 		break;
31223160851Smpi 
31323160851Smpi 	case '"':			/* double quote */
31423160851Smpi 		putchar('"');
31523160851Smpi 		break;
31623160851Smpi 
31723160851Smpi 	case 'a':			/* alert */
31823160851Smpi 		putchar('\a');
31923160851Smpi 		break;
32023160851Smpi 
32123160851Smpi 	case 'b':			/* backspace */
32223160851Smpi 		putchar('\b');
32323160851Smpi 		break;
32423160851Smpi 
32523160851Smpi 	case 'e':			/* escape */
32623160851Smpi #ifdef __GNUC__
32723160851Smpi 		putchar('\e');
32823160851Smpi #else
32923160851Smpi 		putchar(033);
33023160851Smpi #endif
33123160851Smpi 		break;
33223160851Smpi 
33323160851Smpi 	case 'f':			/* form-feed */
33423160851Smpi 		putchar('\f');
33523160851Smpi 		break;
33623160851Smpi 
33723160851Smpi 	case 'n':			/* newline */
33823160851Smpi 		putchar('\n');
33923160851Smpi 		break;
34023160851Smpi 
34123160851Smpi 	case 'r':			/* carriage-return */
34223160851Smpi 		putchar('\r');
34323160851Smpi 		break;
34423160851Smpi 
34523160851Smpi 	case 't':			/* tab */
34623160851Smpi 		putchar('\t');
34723160851Smpi 		break;
34823160851Smpi 
34923160851Smpi 	case 'v':			/* vertical-tab */
35023160851Smpi 		putchar('\v');
35123160851Smpi 		break;
35223160851Smpi 
35323160851Smpi 	case '\0':
35423160851Smpi 		warnx("null escape sequence");
35523160851Smpi 		rval = 1;
35623160851Smpi 		return 0;
35723160851Smpi 
35823160851Smpi 	default:
35923160851Smpi 		putchar(*str);
36023160851Smpi 		warnx("unknown escape sequence `\\%c'", *str);
36123160851Smpi 		rval = 1;
36223160851Smpi 	}
36323160851Smpi 
36423160851Smpi 	return 1;
36523160851Smpi }
36623160851Smpi 
36723160851Smpi static char *
36823160851Smpi mklong(const char *str, int ch)
36923160851Smpi {
37023160851Smpi 	static char *copy;
37123160851Smpi 	static int copysize;
37223160851Smpi 	int len;
37323160851Smpi 
37423160851Smpi 	len = strlen(str) + 2;
37523160851Smpi 	if (copysize < len) {
37623160851Smpi 		char *newcopy;
37723160851Smpi 		copysize = len + 256;
37823160851Smpi 
37923160851Smpi 		newcopy = realloc(copy, copysize);
38023160851Smpi 		if (newcopy == NULL) {
38123160851Smpi 			copysize = 0;
38223160851Smpi 			free(copy);
38323160851Smpi 			copy = NULL;
38423160851Smpi 			return (NULL);
38523160851Smpi 		}
38623160851Smpi 		copy = newcopy;
38723160851Smpi 	}
38823160851Smpi 	(void) memmove(copy, str, len - 3);
38923160851Smpi 	copy[len - 3] = 'l';
39023160851Smpi 	copy[len - 2] = ch;
39123160851Smpi 	copy[len - 1] = '\0';
39223160851Smpi 	return (copy);
39323160851Smpi }
39423160851Smpi 
39523160851Smpi static int
39623160851Smpi getchr(void)
39723160851Smpi {
39823160851Smpi 	int c;
39923160851Smpi 
40023160851Smpi 	if (gargv == NULL)
40123160851Smpi 		return((int)'\0');
40223160851Smpi 
403*6243ff0bSmpi 	c = ba2long(gargv, gdevt);
40423160851Smpi 	gargv = SLIST_NEXT(gargv, ba_next);
40523160851Smpi 	return c;
40623160851Smpi }
40723160851Smpi 
40823160851Smpi static const char *
40923160851Smpi getstr(void)
41023160851Smpi {
41123160851Smpi 	const char *str;
41223160851Smpi 
41323160851Smpi 	if (gargv == NULL)
41423160851Smpi 		return "";
41523160851Smpi 
41623160851Smpi 	str = ba2str(gargv, gdevt);
41723160851Smpi 	gargv = SLIST_NEXT(gargv, ba_next);
41823160851Smpi 	return str;
41923160851Smpi }
42023160851Smpi 
42123160851Smpi static char *number = "+-.0123456789";
42223160851Smpi static int
42323160851Smpi getint(void)
42423160851Smpi {
42523160851Smpi 	const char *str;
42623160851Smpi 
42723160851Smpi 	if (gargv == NULL)
42823160851Smpi 		return 0;
42923160851Smpi 
43023160851Smpi 	str = ba2str(gargv, gdevt);
43123160851Smpi 	if (strchr(number, *str)) {
43223160851Smpi 		gargv = SLIST_NEXT(gargv, ba_next);
43323160851Smpi 		return atoi(str);
43423160851Smpi 	}
43523160851Smpi 
43623160851Smpi 	return 0;
43723160851Smpi }
43823160851Smpi 
43923160851Smpi static long
44023160851Smpi getlong(void)
44123160851Smpi {
44223160851Smpi 	const char *str;
44323160851Smpi 	long val;
44423160851Smpi 	char *ep;
44523160851Smpi 
44623160851Smpi 	if (gargv == NULL)
44723160851Smpi 		return 0UL;
44823160851Smpi 
44923160851Smpi 	str = ba2str(gargv, gdevt);
45023160851Smpi 	gargv = SLIST_NEXT(gargv, ba_next);
45123160851Smpi 
45223160851Smpi 	if (*str == '\"' || *str == '\'') {
45323160851Smpi 		unsigned char c = (unsigned char)str[1];
45423160851Smpi 		return c;
45523160851Smpi 	}
45623160851Smpi 
45723160851Smpi 	errno = 0;
45823160851Smpi 	val = strtol(str, &ep, 0);
45923160851Smpi 	check_conversion(str, ep);
46023160851Smpi 	return val;
46123160851Smpi }
46223160851Smpi 
46323160851Smpi static unsigned long
46423160851Smpi getulong(void)
46523160851Smpi {
46623160851Smpi 	const char *str;
46723160851Smpi 	unsigned long val;
46823160851Smpi 	char *ep;
46923160851Smpi 
47023160851Smpi 	if (gargv == NULL)
47123160851Smpi 		return 0UL;
47223160851Smpi 
47323160851Smpi 	str = ba2str(gargv, gdevt);
47423160851Smpi 	gargv = SLIST_NEXT(gargv, ba_next);
47523160851Smpi 
47623160851Smpi 	if (*str == '\"' || *str == '\'') {
47723160851Smpi 		unsigned char c = (unsigned char)str[1];
47823160851Smpi 		return c;
47923160851Smpi 	}
48023160851Smpi 
48123160851Smpi 	errno = 0;
48223160851Smpi 	val = strtoul(str, &ep, 0);
48323160851Smpi 	check_conversion(str, ep);
48423160851Smpi 	return val;
48523160851Smpi }
48623160851Smpi 
48723160851Smpi static double
48823160851Smpi getdouble(void)
48923160851Smpi {
49023160851Smpi 	const char *str;
49123160851Smpi 	double val;
49223160851Smpi 	char *ep;
49323160851Smpi 
49423160851Smpi 	if (gargv == NULL)
49523160851Smpi 		return 0.0;
49623160851Smpi 
49723160851Smpi 	str = ba2str(gargv, gdevt);
49823160851Smpi 	gargv = SLIST_NEXT(gargv, ba_next);
49923160851Smpi 
50023160851Smpi 	if (*str == '\"' || *str == '\'') {
50123160851Smpi 		unsigned char c = (unsigned char)str[1];
50223160851Smpi 		return c;
50323160851Smpi 	}
50423160851Smpi 
50523160851Smpi 	errno = 0;
50623160851Smpi 	val = strtod(str, &ep);
50723160851Smpi 	check_conversion(str, ep);
50823160851Smpi 	return val;
50923160851Smpi }
51023160851Smpi 
51123160851Smpi static void
51223160851Smpi check_conversion(const char *s, const char *ep)
51323160851Smpi {
51423160851Smpi 	if (*ep) {
51523160851Smpi 		if (ep == s)
51623160851Smpi 			warnx ("%s: expected numeric value", s);
51723160851Smpi 		else
51823160851Smpi 			warnx ("%s: not completely converted", s);
51923160851Smpi 		rval = 1;
52023160851Smpi 	} else if (errno == ERANGE) {
52123160851Smpi 		warnc(ERANGE, "%s", s);
52223160851Smpi 		rval = 1;
52323160851Smpi 	}
52423160851Smpi }
525