12c7d1e30SArvind Sankar // SPDX-License-Identifier: GPL-2.0-only
22c7d1e30SArvind Sankar /* -*- linux-c -*- ------------------------------------------------------- *
32c7d1e30SArvind Sankar  *
42c7d1e30SArvind Sankar  *   Copyright (C) 1991, 1992 Linus Torvalds
52c7d1e30SArvind Sankar  *   Copyright 2007 rPath, Inc. - All Rights Reserved
62c7d1e30SArvind Sankar  *
72c7d1e30SArvind Sankar  * ----------------------------------------------------------------------- */
82c7d1e30SArvind Sankar 
92c7d1e30SArvind Sankar /*
102c7d1e30SArvind Sankar  * Oh, it's a waste of space, but oh-so-yummy for debugging.  This
112c7d1e30SArvind Sankar  * version of printf() does not include 64-bit support.  "Live with
122c7d1e30SArvind Sankar  * it."
132c7d1e30SArvind Sankar  *
142c7d1e30SArvind Sankar  */
152c7d1e30SArvind Sankar 
162c7d1e30SArvind Sankar #include <stdarg.h>
172c7d1e30SArvind Sankar 
182c7d1e30SArvind Sankar #include <linux/compiler.h>
192c7d1e30SArvind Sankar #include <linux/ctype.h>
202c7d1e30SArvind Sankar #include <linux/string.h>
212c7d1e30SArvind Sankar 
222c7d1e30SArvind Sankar static int skip_atoi(const char **s)
232c7d1e30SArvind Sankar {
242c7d1e30SArvind Sankar 	int i = 0;
252c7d1e30SArvind Sankar 
262c7d1e30SArvind Sankar 	while (isdigit(**s))
272c7d1e30SArvind Sankar 		i = i * 10 + *((*s)++) - '0';
282c7d1e30SArvind Sankar 	return i;
292c7d1e30SArvind Sankar }
302c7d1e30SArvind Sankar 
312c7d1e30SArvind Sankar #define ZEROPAD	1		/* pad with zero */
322c7d1e30SArvind Sankar #define SIGN	2		/* unsigned/signed long */
332c7d1e30SArvind Sankar #define PLUS	4		/* show plus */
342c7d1e30SArvind Sankar #define SPACE	8		/* space if plus */
352c7d1e30SArvind Sankar #define LEFT	16		/* left justified */
362c7d1e30SArvind Sankar #define SMALL	32		/* Must be 32 == 0x20 */
372c7d1e30SArvind Sankar #define SPECIAL	64		/* 0x */
382c7d1e30SArvind Sankar 
392c7d1e30SArvind Sankar #define __do_div(n, base) ({ \
402c7d1e30SArvind Sankar int __res; \
412c7d1e30SArvind Sankar __res = ((unsigned long) n) % (unsigned) base; \
422c7d1e30SArvind Sankar n = ((unsigned long) n) / (unsigned) base; \
432c7d1e30SArvind Sankar __res; })
442c7d1e30SArvind Sankar 
452c7d1e30SArvind Sankar static char *number(char *str, long num, int base, int size, int precision,
462c7d1e30SArvind Sankar 		    int type)
472c7d1e30SArvind Sankar {
482c7d1e30SArvind Sankar 	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
492c7d1e30SArvind Sankar 	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
502c7d1e30SArvind Sankar 
512c7d1e30SArvind Sankar 	char tmp[66];
522c7d1e30SArvind Sankar 	char c, sign, locase;
532c7d1e30SArvind Sankar 	int i;
542c7d1e30SArvind Sankar 
552c7d1e30SArvind Sankar 	/* locase = 0 or 0x20. ORing digits or letters with 'locase'
562c7d1e30SArvind Sankar 	 * produces same digits or (maybe lowercased) letters */
572c7d1e30SArvind Sankar 	locase = (type & SMALL);
582c7d1e30SArvind Sankar 	if (type & LEFT)
592c7d1e30SArvind Sankar 		type &= ~ZEROPAD;
602c7d1e30SArvind Sankar 	if (base < 2 || base > 16)
612c7d1e30SArvind Sankar 		return NULL;
622c7d1e30SArvind Sankar 	c = (type & ZEROPAD) ? '0' : ' ';
632c7d1e30SArvind Sankar 	sign = 0;
642c7d1e30SArvind Sankar 	if (type & SIGN) {
652c7d1e30SArvind Sankar 		if (num < 0) {
662c7d1e30SArvind Sankar 			sign = '-';
672c7d1e30SArvind Sankar 			num = -num;
682c7d1e30SArvind Sankar 			size--;
692c7d1e30SArvind Sankar 		} else if (type & PLUS) {
702c7d1e30SArvind Sankar 			sign = '+';
712c7d1e30SArvind Sankar 			size--;
722c7d1e30SArvind Sankar 		} else if (type & SPACE) {
732c7d1e30SArvind Sankar 			sign = ' ';
742c7d1e30SArvind Sankar 			size--;
752c7d1e30SArvind Sankar 		}
762c7d1e30SArvind Sankar 	}
772c7d1e30SArvind Sankar 	if (type & SPECIAL) {
782c7d1e30SArvind Sankar 		if (base == 16)
792c7d1e30SArvind Sankar 			size -= 2;
802c7d1e30SArvind Sankar 		else if (base == 8)
812c7d1e30SArvind Sankar 			size--;
822c7d1e30SArvind Sankar 	}
832c7d1e30SArvind Sankar 	i = 0;
842c7d1e30SArvind Sankar 	if (num == 0)
852c7d1e30SArvind Sankar 		tmp[i++] = '0';
862c7d1e30SArvind Sankar 	else
872c7d1e30SArvind Sankar 		while (num != 0)
882c7d1e30SArvind Sankar 			tmp[i++] = (digits[__do_div(num, base)] | locase);
892c7d1e30SArvind Sankar 	if (i > precision)
902c7d1e30SArvind Sankar 		precision = i;
912c7d1e30SArvind Sankar 	size -= precision;
922c7d1e30SArvind Sankar 	if (!(type & (ZEROPAD + LEFT)))
932c7d1e30SArvind Sankar 		while (size-- > 0)
942c7d1e30SArvind Sankar 			*str++ = ' ';
952c7d1e30SArvind Sankar 	if (sign)
962c7d1e30SArvind Sankar 		*str++ = sign;
972c7d1e30SArvind Sankar 	if (type & SPECIAL) {
982c7d1e30SArvind Sankar 		if (base == 8) {
992c7d1e30SArvind Sankar 			*str++ = '0';
1002c7d1e30SArvind Sankar 		} else if (base == 16) {
1012c7d1e30SArvind Sankar 			*str++ = '0';
1022c7d1e30SArvind Sankar 			*str++ = ('X' | locase);
1032c7d1e30SArvind Sankar 		}
1042c7d1e30SArvind Sankar 	}
1052c7d1e30SArvind Sankar 	if (!(type & LEFT))
1062c7d1e30SArvind Sankar 		while (size-- > 0)
1072c7d1e30SArvind Sankar 			*str++ = c;
1082c7d1e30SArvind Sankar 	while (i < precision--)
1092c7d1e30SArvind Sankar 		*str++ = '0';
1102c7d1e30SArvind Sankar 	while (i-- > 0)
1112c7d1e30SArvind Sankar 		*str++ = tmp[i];
1122c7d1e30SArvind Sankar 	while (size-- > 0)
1132c7d1e30SArvind Sankar 		*str++ = ' ';
1142c7d1e30SArvind Sankar 	return str;
1152c7d1e30SArvind Sankar }
1162c7d1e30SArvind Sankar 
1172c7d1e30SArvind Sankar int vsprintf(char *buf, const char *fmt, va_list args)
1182c7d1e30SArvind Sankar {
1192c7d1e30SArvind Sankar 	int len;
1202c7d1e30SArvind Sankar 	unsigned long num;
1212c7d1e30SArvind Sankar 	int i, base;
1222c7d1e30SArvind Sankar 	char *str;
1232c7d1e30SArvind Sankar 	const char *s;
1242c7d1e30SArvind Sankar 
1252c7d1e30SArvind Sankar 	int flags;		/* flags to number() */
1262c7d1e30SArvind Sankar 
1272c7d1e30SArvind Sankar 	int field_width;	/* width of output field */
1282c7d1e30SArvind Sankar 	int precision;		/* min. # of digits for integers; max
1292c7d1e30SArvind Sankar 				   number of chars for from string */
1302c7d1e30SArvind Sankar 	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
1312c7d1e30SArvind Sankar 
1322c7d1e30SArvind Sankar 	for (str = buf; *fmt; ++fmt) {
1332c7d1e30SArvind Sankar 		if (*fmt != '%') {
1342c7d1e30SArvind Sankar 			*str++ = *fmt;
1352c7d1e30SArvind Sankar 			continue;
1362c7d1e30SArvind Sankar 		}
1372c7d1e30SArvind Sankar 
1382c7d1e30SArvind Sankar 		/* process flags */
1392c7d1e30SArvind Sankar 		flags = 0;
1402c7d1e30SArvind Sankar 	      repeat:
1412c7d1e30SArvind Sankar 		++fmt;		/* this also skips first '%' */
1422c7d1e30SArvind Sankar 		switch (*fmt) {
1432c7d1e30SArvind Sankar 		case '-':
1442c7d1e30SArvind Sankar 			flags |= LEFT;
1452c7d1e30SArvind Sankar 			goto repeat;
1462c7d1e30SArvind Sankar 		case '+':
1472c7d1e30SArvind Sankar 			flags |= PLUS;
1482c7d1e30SArvind Sankar 			goto repeat;
1492c7d1e30SArvind Sankar 		case ' ':
1502c7d1e30SArvind Sankar 			flags |= SPACE;
1512c7d1e30SArvind Sankar 			goto repeat;
1522c7d1e30SArvind Sankar 		case '#':
1532c7d1e30SArvind Sankar 			flags |= SPECIAL;
1542c7d1e30SArvind Sankar 			goto repeat;
1552c7d1e30SArvind Sankar 		case '0':
1562c7d1e30SArvind Sankar 			flags |= ZEROPAD;
1572c7d1e30SArvind Sankar 			goto repeat;
1582c7d1e30SArvind Sankar 		}
1592c7d1e30SArvind Sankar 
1602c7d1e30SArvind Sankar 		/* get field width */
1612c7d1e30SArvind Sankar 		field_width = -1;
1622c7d1e30SArvind Sankar 		if (isdigit(*fmt)) {
1632c7d1e30SArvind Sankar 			field_width = skip_atoi(&fmt);
1642c7d1e30SArvind Sankar 		} else if (*fmt == '*') {
1652c7d1e30SArvind Sankar 			++fmt;
1662c7d1e30SArvind Sankar 			/* it's the next argument */
1672c7d1e30SArvind Sankar 			field_width = va_arg(args, int);
1682c7d1e30SArvind Sankar 			if (field_width < 0) {
1692c7d1e30SArvind Sankar 				field_width = -field_width;
1702c7d1e30SArvind Sankar 				flags |= LEFT;
1712c7d1e30SArvind Sankar 			}
1722c7d1e30SArvind Sankar 		}
1732c7d1e30SArvind Sankar 
1742c7d1e30SArvind Sankar 		/* get the precision */
1752c7d1e30SArvind Sankar 		precision = -1;
1762c7d1e30SArvind Sankar 		if (*fmt == '.') {
1772c7d1e30SArvind Sankar 			++fmt;
1782c7d1e30SArvind Sankar 			if (isdigit(*fmt)) {
1792c7d1e30SArvind Sankar 				precision = skip_atoi(&fmt);
1802c7d1e30SArvind Sankar 			} else if (*fmt == '*') {
1812c7d1e30SArvind Sankar 				++fmt;
1822c7d1e30SArvind Sankar 				/* it's the next argument */
1832c7d1e30SArvind Sankar 				precision = va_arg(args, int);
1842c7d1e30SArvind Sankar 			}
1852c7d1e30SArvind Sankar 			if (precision < 0)
1862c7d1e30SArvind Sankar 				precision = 0;
1872c7d1e30SArvind Sankar 		}
1882c7d1e30SArvind Sankar 
1892c7d1e30SArvind Sankar 		/* get the conversion qualifier */
1902c7d1e30SArvind Sankar 		qualifier = -1;
1912c7d1e30SArvind Sankar 		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
1922c7d1e30SArvind Sankar 			qualifier = *fmt;
1932c7d1e30SArvind Sankar 			++fmt;
1942c7d1e30SArvind Sankar 		}
1952c7d1e30SArvind Sankar 
1962c7d1e30SArvind Sankar 		/* default base */
1972c7d1e30SArvind Sankar 		base = 10;
1982c7d1e30SArvind Sankar 
1992c7d1e30SArvind Sankar 		switch (*fmt) {
2002c7d1e30SArvind Sankar 		case 'c':
2012c7d1e30SArvind Sankar 			if (!(flags & LEFT))
2022c7d1e30SArvind Sankar 				while (--field_width > 0)
2032c7d1e30SArvind Sankar 					*str++ = ' ';
2042c7d1e30SArvind Sankar 			*str++ = (unsigned char)va_arg(args, int);
2052c7d1e30SArvind Sankar 			while (--field_width > 0)
2062c7d1e30SArvind Sankar 				*str++ = ' ';
2072c7d1e30SArvind Sankar 			continue;
2082c7d1e30SArvind Sankar 
2092c7d1e30SArvind Sankar 		case 's':
2102c7d1e30SArvind Sankar 			s = va_arg(args, char *);
2112c7d1e30SArvind Sankar 			len = strnlen(s, precision);
2122c7d1e30SArvind Sankar 
2132c7d1e30SArvind Sankar 			if (!(flags & LEFT))
2142c7d1e30SArvind Sankar 				while (len < field_width--)
2152c7d1e30SArvind Sankar 					*str++ = ' ';
2162c7d1e30SArvind Sankar 			for (i = 0; i < len; ++i)
2172c7d1e30SArvind Sankar 				*str++ = *s++;
2182c7d1e30SArvind Sankar 			while (len < field_width--)
2192c7d1e30SArvind Sankar 				*str++ = ' ';
2202c7d1e30SArvind Sankar 			continue;
2212c7d1e30SArvind Sankar 
2222c7d1e30SArvind Sankar 		case 'p':
2232c7d1e30SArvind Sankar 			if (field_width == -1) {
2242c7d1e30SArvind Sankar 				field_width = 2 * sizeof(void *);
2252c7d1e30SArvind Sankar 				flags |= ZEROPAD;
2262c7d1e30SArvind Sankar 			}
2272c7d1e30SArvind Sankar 			str = number(str,
2282c7d1e30SArvind Sankar 				     (unsigned long)va_arg(args, void *), 16,
2292c7d1e30SArvind Sankar 				     field_width, precision, flags);
2302c7d1e30SArvind Sankar 			continue;
2312c7d1e30SArvind Sankar 
2322c7d1e30SArvind Sankar 		case 'n':
2332c7d1e30SArvind Sankar 			if (qualifier == 'l') {
2342c7d1e30SArvind Sankar 				long *ip = va_arg(args, long *);
2352c7d1e30SArvind Sankar 				*ip = (str - buf);
2362c7d1e30SArvind Sankar 			} else {
2372c7d1e30SArvind Sankar 				int *ip = va_arg(args, int *);
2382c7d1e30SArvind Sankar 				*ip = (str - buf);
2392c7d1e30SArvind Sankar 			}
2402c7d1e30SArvind Sankar 			continue;
2412c7d1e30SArvind Sankar 
2422c7d1e30SArvind Sankar 		case '%':
2432c7d1e30SArvind Sankar 			*str++ = '%';
2442c7d1e30SArvind Sankar 			continue;
2452c7d1e30SArvind Sankar 
2462c7d1e30SArvind Sankar 			/* integer number formats - set up the flags and "break" */
2472c7d1e30SArvind Sankar 		case 'o':
2482c7d1e30SArvind Sankar 			base = 8;
2492c7d1e30SArvind Sankar 			break;
2502c7d1e30SArvind Sankar 
2512c7d1e30SArvind Sankar 		case 'x':
2522c7d1e30SArvind Sankar 			flags |= SMALL;
2532c7d1e30SArvind Sankar 			fallthrough;
2542c7d1e30SArvind Sankar 		case 'X':
2552c7d1e30SArvind Sankar 			base = 16;
2562c7d1e30SArvind Sankar 			break;
2572c7d1e30SArvind Sankar 
2582c7d1e30SArvind Sankar 		case 'd':
2592c7d1e30SArvind Sankar 		case 'i':
2602c7d1e30SArvind Sankar 			flags |= SIGN;
2612c7d1e30SArvind Sankar 			fallthrough;
2622c7d1e30SArvind Sankar 		case 'u':
2632c7d1e30SArvind Sankar 			break;
2642c7d1e30SArvind Sankar 
2652c7d1e30SArvind Sankar 		default:
2662c7d1e30SArvind Sankar 			*str++ = '%';
2672c7d1e30SArvind Sankar 			if (*fmt)
2682c7d1e30SArvind Sankar 				*str++ = *fmt;
2692c7d1e30SArvind Sankar 			else
2702c7d1e30SArvind Sankar 				--fmt;
2712c7d1e30SArvind Sankar 			continue;
2722c7d1e30SArvind Sankar 		}
2732c7d1e30SArvind Sankar 		if (qualifier == 'l') {
2742c7d1e30SArvind Sankar 			num = va_arg(args, unsigned long);
2752c7d1e30SArvind Sankar 		} else if (qualifier == 'h') {
2762c7d1e30SArvind Sankar 			num = (unsigned short)va_arg(args, int);
2772c7d1e30SArvind Sankar 			if (flags & SIGN)
2782c7d1e30SArvind Sankar 				num = (short)num;
2792c7d1e30SArvind Sankar 		} else if (flags & SIGN) {
2802c7d1e30SArvind Sankar 			num = va_arg(args, int);
2812c7d1e30SArvind Sankar 		} else {
2822c7d1e30SArvind Sankar 			num = va_arg(args, unsigned int);
2832c7d1e30SArvind Sankar 		}
2842c7d1e30SArvind Sankar 		str = number(str, num, base, field_width, precision, flags);
2852c7d1e30SArvind Sankar 	}
2862c7d1e30SArvind Sankar 	*str = '\0';
2872c7d1e30SArvind Sankar 	return str - buf;
2882c7d1e30SArvind Sankar }
2892c7d1e30SArvind Sankar 
2902c7d1e30SArvind Sankar int sprintf(char *buf, const char *fmt, ...)
2912c7d1e30SArvind Sankar {
2922c7d1e30SArvind Sankar 	va_list args;
2932c7d1e30SArvind Sankar 	int i;
2942c7d1e30SArvind Sankar 
2952c7d1e30SArvind Sankar 	va_start(args, fmt);
2962c7d1e30SArvind Sankar 	i = vsprintf(buf, fmt, args);
2972c7d1e30SArvind Sankar 	va_end(args);
2982c7d1e30SArvind Sankar 	return i;
2992c7d1e30SArvind Sankar }
300