18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* -*- linux-c -*- ------------------------------------------------------- *
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *   Copyright (C) 1991, 1992 Linus Torvalds
58c2ecf20Sopenharmony_ci *   Copyright 2007 rPath, Inc. - All Rights Reserved
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * ----------------------------------------------------------------------- */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * Oh, it's a waste of space, but oh-so-yummy for debugging.  This
118c2ecf20Sopenharmony_ci * version of printf() does not include 64-bit support.  "Live with
128c2ecf20Sopenharmony_ci * it."
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "boot.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic int skip_atoi(const char **s)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	int i = 0;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	while (isdigit(**s))
238c2ecf20Sopenharmony_ci		i = i * 10 + *((*s)++) - '0';
248c2ecf20Sopenharmony_ci	return i;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define ZEROPAD	1		/* pad with zero */
288c2ecf20Sopenharmony_ci#define SIGN	2		/* unsigned/signed long */
298c2ecf20Sopenharmony_ci#define PLUS	4		/* show plus */
308c2ecf20Sopenharmony_ci#define SPACE	8		/* space if plus */
318c2ecf20Sopenharmony_ci#define LEFT	16		/* left justified */
328c2ecf20Sopenharmony_ci#define SMALL	32		/* Must be 32 == 0x20 */
338c2ecf20Sopenharmony_ci#define SPECIAL	64		/* 0x */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define __do_div(n, base) ({ \
368c2ecf20Sopenharmony_ciint __res; \
378c2ecf20Sopenharmony_ci__res = ((unsigned long) n) % (unsigned) base; \
388c2ecf20Sopenharmony_cin = ((unsigned long) n) / (unsigned) base; \
398c2ecf20Sopenharmony_ci__res; })
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic char *number(char *str, long num, int base, int size, int precision,
428c2ecf20Sopenharmony_ci		    int type)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
458c2ecf20Sopenharmony_ci	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	char tmp[66];
488c2ecf20Sopenharmony_ci	char c, sign, locase;
498c2ecf20Sopenharmony_ci	int i;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* locase = 0 or 0x20. ORing digits or letters with 'locase'
528c2ecf20Sopenharmony_ci	 * produces same digits or (maybe lowercased) letters */
538c2ecf20Sopenharmony_ci	locase = (type & SMALL);
548c2ecf20Sopenharmony_ci	if (type & LEFT)
558c2ecf20Sopenharmony_ci		type &= ~ZEROPAD;
568c2ecf20Sopenharmony_ci	if (base < 2 || base > 16)
578c2ecf20Sopenharmony_ci		return NULL;
588c2ecf20Sopenharmony_ci	c = (type & ZEROPAD) ? '0' : ' ';
598c2ecf20Sopenharmony_ci	sign = 0;
608c2ecf20Sopenharmony_ci	if (type & SIGN) {
618c2ecf20Sopenharmony_ci		if (num < 0) {
628c2ecf20Sopenharmony_ci			sign = '-';
638c2ecf20Sopenharmony_ci			num = -num;
648c2ecf20Sopenharmony_ci			size--;
658c2ecf20Sopenharmony_ci		} else if (type & PLUS) {
668c2ecf20Sopenharmony_ci			sign = '+';
678c2ecf20Sopenharmony_ci			size--;
688c2ecf20Sopenharmony_ci		} else if (type & SPACE) {
698c2ecf20Sopenharmony_ci			sign = ' ';
708c2ecf20Sopenharmony_ci			size--;
718c2ecf20Sopenharmony_ci		}
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	if (type & SPECIAL) {
748c2ecf20Sopenharmony_ci		if (base == 16)
758c2ecf20Sopenharmony_ci			size -= 2;
768c2ecf20Sopenharmony_ci		else if (base == 8)
778c2ecf20Sopenharmony_ci			size--;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci	i = 0;
808c2ecf20Sopenharmony_ci	if (num == 0)
818c2ecf20Sopenharmony_ci		tmp[i++] = '0';
828c2ecf20Sopenharmony_ci	else
838c2ecf20Sopenharmony_ci		while (num != 0)
848c2ecf20Sopenharmony_ci			tmp[i++] = (digits[__do_div(num, base)] | locase);
858c2ecf20Sopenharmony_ci	if (i > precision)
868c2ecf20Sopenharmony_ci		precision = i;
878c2ecf20Sopenharmony_ci	size -= precision;
888c2ecf20Sopenharmony_ci	if (!(type & (ZEROPAD + LEFT)))
898c2ecf20Sopenharmony_ci		while (size-- > 0)
908c2ecf20Sopenharmony_ci			*str++ = ' ';
918c2ecf20Sopenharmony_ci	if (sign)
928c2ecf20Sopenharmony_ci		*str++ = sign;
938c2ecf20Sopenharmony_ci	if (type & SPECIAL) {
948c2ecf20Sopenharmony_ci		if (base == 8)
958c2ecf20Sopenharmony_ci			*str++ = '0';
968c2ecf20Sopenharmony_ci		else if (base == 16) {
978c2ecf20Sopenharmony_ci			*str++ = '0';
988c2ecf20Sopenharmony_ci			*str++ = ('X' | locase);
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	if (!(type & LEFT))
1028c2ecf20Sopenharmony_ci		while (size-- > 0)
1038c2ecf20Sopenharmony_ci			*str++ = c;
1048c2ecf20Sopenharmony_ci	while (i < precision--)
1058c2ecf20Sopenharmony_ci		*str++ = '0';
1068c2ecf20Sopenharmony_ci	while (i-- > 0)
1078c2ecf20Sopenharmony_ci		*str++ = tmp[i];
1088c2ecf20Sopenharmony_ci	while (size-- > 0)
1098c2ecf20Sopenharmony_ci		*str++ = ' ';
1108c2ecf20Sopenharmony_ci	return str;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciint vsprintf(char *buf, const char *fmt, va_list args)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	int len;
1168c2ecf20Sopenharmony_ci	unsigned long num;
1178c2ecf20Sopenharmony_ci	int i, base;
1188c2ecf20Sopenharmony_ci	char *str;
1198c2ecf20Sopenharmony_ci	const char *s;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	int flags;		/* flags to number() */
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	int field_width;	/* width of output field */
1248c2ecf20Sopenharmony_ci	int precision;		/* min. # of digits for integers; max
1258c2ecf20Sopenharmony_ci				   number of chars for from string */
1268c2ecf20Sopenharmony_ci	int qualifier;		/* 'h', 'l', or 'L' for integer fields */
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	for (str = buf; *fmt; ++fmt) {
1298c2ecf20Sopenharmony_ci		if (*fmt != '%') {
1308c2ecf20Sopenharmony_ci			*str++ = *fmt;
1318c2ecf20Sopenharmony_ci			continue;
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		/* process flags */
1358c2ecf20Sopenharmony_ci		flags = 0;
1368c2ecf20Sopenharmony_ci	      repeat:
1378c2ecf20Sopenharmony_ci		++fmt;		/* this also skips first '%' */
1388c2ecf20Sopenharmony_ci		switch (*fmt) {
1398c2ecf20Sopenharmony_ci		case '-':
1408c2ecf20Sopenharmony_ci			flags |= LEFT;
1418c2ecf20Sopenharmony_ci			goto repeat;
1428c2ecf20Sopenharmony_ci		case '+':
1438c2ecf20Sopenharmony_ci			flags |= PLUS;
1448c2ecf20Sopenharmony_ci			goto repeat;
1458c2ecf20Sopenharmony_ci		case ' ':
1468c2ecf20Sopenharmony_ci			flags |= SPACE;
1478c2ecf20Sopenharmony_ci			goto repeat;
1488c2ecf20Sopenharmony_ci		case '#':
1498c2ecf20Sopenharmony_ci			flags |= SPECIAL;
1508c2ecf20Sopenharmony_ci			goto repeat;
1518c2ecf20Sopenharmony_ci		case '0':
1528c2ecf20Sopenharmony_ci			flags |= ZEROPAD;
1538c2ecf20Sopenharmony_ci			goto repeat;
1548c2ecf20Sopenharmony_ci		}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		/* get field width */
1578c2ecf20Sopenharmony_ci		field_width = -1;
1588c2ecf20Sopenharmony_ci		if (isdigit(*fmt))
1598c2ecf20Sopenharmony_ci			field_width = skip_atoi(&fmt);
1608c2ecf20Sopenharmony_ci		else if (*fmt == '*') {
1618c2ecf20Sopenharmony_ci			++fmt;
1628c2ecf20Sopenharmony_ci			/* it's the next argument */
1638c2ecf20Sopenharmony_ci			field_width = va_arg(args, int);
1648c2ecf20Sopenharmony_ci			if (field_width < 0) {
1658c2ecf20Sopenharmony_ci				field_width = -field_width;
1668c2ecf20Sopenharmony_ci				flags |= LEFT;
1678c2ecf20Sopenharmony_ci			}
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		/* get the precision */
1718c2ecf20Sopenharmony_ci		precision = -1;
1728c2ecf20Sopenharmony_ci		if (*fmt == '.') {
1738c2ecf20Sopenharmony_ci			++fmt;
1748c2ecf20Sopenharmony_ci			if (isdigit(*fmt))
1758c2ecf20Sopenharmony_ci				precision = skip_atoi(&fmt);
1768c2ecf20Sopenharmony_ci			else if (*fmt == '*') {
1778c2ecf20Sopenharmony_ci				++fmt;
1788c2ecf20Sopenharmony_ci				/* it's the next argument */
1798c2ecf20Sopenharmony_ci				precision = va_arg(args, int);
1808c2ecf20Sopenharmony_ci			}
1818c2ecf20Sopenharmony_ci			if (precision < 0)
1828c2ecf20Sopenharmony_ci				precision = 0;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		/* get the conversion qualifier */
1868c2ecf20Sopenharmony_ci		qualifier = -1;
1878c2ecf20Sopenharmony_ci		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
1888c2ecf20Sopenharmony_ci			qualifier = *fmt;
1898c2ecf20Sopenharmony_ci			++fmt;
1908c2ecf20Sopenharmony_ci		}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		/* default base */
1938c2ecf20Sopenharmony_ci		base = 10;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		switch (*fmt) {
1968c2ecf20Sopenharmony_ci		case 'c':
1978c2ecf20Sopenharmony_ci			if (!(flags & LEFT))
1988c2ecf20Sopenharmony_ci				while (--field_width > 0)
1998c2ecf20Sopenharmony_ci					*str++ = ' ';
2008c2ecf20Sopenharmony_ci			*str++ = (unsigned char)va_arg(args, int);
2018c2ecf20Sopenharmony_ci			while (--field_width > 0)
2028c2ecf20Sopenharmony_ci				*str++ = ' ';
2038c2ecf20Sopenharmony_ci			continue;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		case 's':
2068c2ecf20Sopenharmony_ci			s = va_arg(args, char *);
2078c2ecf20Sopenharmony_ci			len = strnlen(s, precision);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci			if (!(flags & LEFT))
2108c2ecf20Sopenharmony_ci				while (len < field_width--)
2118c2ecf20Sopenharmony_ci					*str++ = ' ';
2128c2ecf20Sopenharmony_ci			for (i = 0; i < len; ++i)
2138c2ecf20Sopenharmony_ci				*str++ = *s++;
2148c2ecf20Sopenharmony_ci			while (len < field_width--)
2158c2ecf20Sopenharmony_ci				*str++ = ' ';
2168c2ecf20Sopenharmony_ci			continue;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		case 'p':
2198c2ecf20Sopenharmony_ci			if (field_width == -1) {
2208c2ecf20Sopenharmony_ci				field_width = 2 * sizeof(void *);
2218c2ecf20Sopenharmony_ci				flags |= ZEROPAD;
2228c2ecf20Sopenharmony_ci			}
2238c2ecf20Sopenharmony_ci			str = number(str,
2248c2ecf20Sopenharmony_ci				     (unsigned long)va_arg(args, void *), 16,
2258c2ecf20Sopenharmony_ci				     field_width, precision, flags);
2268c2ecf20Sopenharmony_ci			continue;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		case 'n':
2298c2ecf20Sopenharmony_ci			if (qualifier == 'l') {
2308c2ecf20Sopenharmony_ci				long *ip = va_arg(args, long *);
2318c2ecf20Sopenharmony_ci				*ip = (str - buf);
2328c2ecf20Sopenharmony_ci			} else {
2338c2ecf20Sopenharmony_ci				int *ip = va_arg(args, int *);
2348c2ecf20Sopenharmony_ci				*ip = (str - buf);
2358c2ecf20Sopenharmony_ci			}
2368c2ecf20Sopenharmony_ci			continue;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		case '%':
2398c2ecf20Sopenharmony_ci			*str++ = '%';
2408c2ecf20Sopenharmony_ci			continue;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci			/* integer number formats - set up the flags and "break" */
2438c2ecf20Sopenharmony_ci		case 'o':
2448c2ecf20Sopenharmony_ci			base = 8;
2458c2ecf20Sopenharmony_ci			break;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		case 'x':
2488c2ecf20Sopenharmony_ci			flags |= SMALL;
2498c2ecf20Sopenharmony_ci		case 'X':
2508c2ecf20Sopenharmony_ci			base = 16;
2518c2ecf20Sopenharmony_ci			break;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		case 'd':
2548c2ecf20Sopenharmony_ci		case 'i':
2558c2ecf20Sopenharmony_ci			flags |= SIGN;
2568c2ecf20Sopenharmony_ci		case 'u':
2578c2ecf20Sopenharmony_ci			break;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		default:
2608c2ecf20Sopenharmony_ci			*str++ = '%';
2618c2ecf20Sopenharmony_ci			if (*fmt)
2628c2ecf20Sopenharmony_ci				*str++ = *fmt;
2638c2ecf20Sopenharmony_ci			else
2648c2ecf20Sopenharmony_ci				--fmt;
2658c2ecf20Sopenharmony_ci			continue;
2668c2ecf20Sopenharmony_ci		}
2678c2ecf20Sopenharmony_ci		if (qualifier == 'l')
2688c2ecf20Sopenharmony_ci			num = va_arg(args, unsigned long);
2698c2ecf20Sopenharmony_ci		else if (qualifier == 'h') {
2708c2ecf20Sopenharmony_ci			num = (unsigned short)va_arg(args, int);
2718c2ecf20Sopenharmony_ci			if (flags & SIGN)
2728c2ecf20Sopenharmony_ci				num = (short)num;
2738c2ecf20Sopenharmony_ci		} else if (flags & SIGN)
2748c2ecf20Sopenharmony_ci			num = va_arg(args, int);
2758c2ecf20Sopenharmony_ci		else
2768c2ecf20Sopenharmony_ci			num = va_arg(args, unsigned int);
2778c2ecf20Sopenharmony_ci		str = number(str, num, base, field_width, precision, flags);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	*str = '\0';
2808c2ecf20Sopenharmony_ci	return str - buf;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ciint sprintf(char *buf, const char *fmt, ...)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	va_list args;
2868c2ecf20Sopenharmony_ci	int i;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	va_start(args, fmt);
2898c2ecf20Sopenharmony_ci	i = vsprintf(buf, fmt, args);
2908c2ecf20Sopenharmony_ci	va_end(args);
2918c2ecf20Sopenharmony_ci	return i;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ciint printf(const char *fmt, ...)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	char printf_buf[1024];
2978c2ecf20Sopenharmony_ci	va_list args;
2988c2ecf20Sopenharmony_ci	int printed;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	va_start(args, fmt);
3018c2ecf20Sopenharmony_ci	printed = vsprintf(printf_buf, fmt, args);
3028c2ecf20Sopenharmony_ci	va_end(args);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	puts(printf_buf);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return printed;
3078c2ecf20Sopenharmony_ci}
308