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.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <stdarg.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <linux/compiler.h>
168c2ecf20Sopenharmony_ci#include <linux/ctype.h>
178c2ecf20Sopenharmony_ci#include <linux/kernel.h>
188c2ecf20Sopenharmony_ci#include <linux/limits.h>
198c2ecf20Sopenharmony_ci#include <linux/string.h>
208c2ecf20Sopenharmony_ci#include <linux/types.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic
238c2ecf20Sopenharmony_ciint skip_atoi(const char **s)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	int i = 0;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	while (isdigit(**s))
288c2ecf20Sopenharmony_ci		i = i * 10 + *((*s)++) - '0';
298c2ecf20Sopenharmony_ci	return i;
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/*
338c2ecf20Sopenharmony_ci * put_dec_full4 handles numbers in the range 0 <= r < 10000.
348c2ecf20Sopenharmony_ci * The multiplier 0xccd is round(2^15/10), and the approximation
358c2ecf20Sopenharmony_ci * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_cistatic
388c2ecf20Sopenharmony_civoid put_dec_full4(char *end, unsigned int r)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int i;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
438c2ecf20Sopenharmony_ci		unsigned int q = (r * 0xccd) >> 15;
448c2ecf20Sopenharmony_ci		*--end = '0' + (r - q * 10);
458c2ecf20Sopenharmony_ci		r = q;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci	*--end = '0' + r;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* put_dec is copied from lib/vsprintf.c with small modifications */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * Call put_dec_full4 on x % 10000, return x / 10000.
548c2ecf20Sopenharmony_ci * The approximation x/10000 == (x * 0x346DC5D7) >> 43
558c2ecf20Sopenharmony_ci * holds for all x < 1,128,869,999.  The largest value this
568c2ecf20Sopenharmony_ci * helper will ever be asked to convert is 1,125,520,955.
578c2ecf20Sopenharmony_ci * (second call in the put_dec code, assuming n is all-ones).
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_cistatic
608c2ecf20Sopenharmony_ciunsigned int put_dec_helper4(char *end, unsigned int x)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	unsigned int q = (x * 0x346DC5D7ULL) >> 43;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	put_dec_full4(end, x - q * 10000);
658c2ecf20Sopenharmony_ci	return q;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Based on code by Douglas W. Jones found at
698c2ecf20Sopenharmony_ci * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
708c2ecf20Sopenharmony_ci * (with permission from the author).
718c2ecf20Sopenharmony_ci * Performs no 64-bit division and hence should be fast on 32-bit machines.
728c2ecf20Sopenharmony_ci */
738c2ecf20Sopenharmony_cistatic
748c2ecf20Sopenharmony_cichar *put_dec(char *end, unsigned long long n)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	unsigned int d3, d2, d1, q, h;
778c2ecf20Sopenharmony_ci	char *p = end;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	d1  = ((unsigned int)n >> 16); /* implicit "& 0xffff" */
808c2ecf20Sopenharmony_ci	h   = (n >> 32);
818c2ecf20Sopenharmony_ci	d2  = (h      ) & 0xffff;
828c2ecf20Sopenharmony_ci	d3  = (h >> 16); /* implicit "& 0xffff" */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0
858c2ecf20Sopenharmony_ci	     = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */
868c2ecf20Sopenharmony_ci	q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff);
878c2ecf20Sopenharmony_ci	q = put_dec_helper4(p, q);
888c2ecf20Sopenharmony_ci	p -= 4;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	q += 7671 * d3 + 9496 * d2 + 6 * d1;
918c2ecf20Sopenharmony_ci	q = put_dec_helper4(p, q);
928c2ecf20Sopenharmony_ci	p -= 4;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	q += 4749 * d3 + 42 * d2;
958c2ecf20Sopenharmony_ci	q = put_dec_helper4(p, q);
968c2ecf20Sopenharmony_ci	p -= 4;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	q += 281 * d3;
998c2ecf20Sopenharmony_ci	q = put_dec_helper4(p, q);
1008c2ecf20Sopenharmony_ci	p -= 4;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	put_dec_full4(p, q);
1038c2ecf20Sopenharmony_ci	p -= 4;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* strip off the extra 0's we printed */
1068c2ecf20Sopenharmony_ci	while (p < end && *p == '0')
1078c2ecf20Sopenharmony_ci		++p;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return p;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic
1138c2ecf20Sopenharmony_cichar *number(char *end, unsigned long long num, int base, char locase)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	/*
1168c2ecf20Sopenharmony_ci	 * locase = 0 or 0x20. ORing digits or letters with 'locase'
1178c2ecf20Sopenharmony_ci	 * produces same digits or (maybe lowercased) letters
1188c2ecf20Sopenharmony_ci	 */
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
1218c2ecf20Sopenharmony_ci	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	switch (base) {
1248c2ecf20Sopenharmony_ci	case 10:
1258c2ecf20Sopenharmony_ci		if (num != 0)
1268c2ecf20Sopenharmony_ci			end = put_dec(end, num);
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	case 8:
1298c2ecf20Sopenharmony_ci		for (; num != 0; num >>= 3)
1308c2ecf20Sopenharmony_ci			*--end = '0' + (num & 07);
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	case 16:
1338c2ecf20Sopenharmony_ci		for (; num != 0; num >>= 4)
1348c2ecf20Sopenharmony_ci			*--end = digits[num & 0xf] | locase;
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci	default:
1378c2ecf20Sopenharmony_ci		unreachable();
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return end;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci#define ZEROPAD	1		/* pad with zero */
1448c2ecf20Sopenharmony_ci#define SIGN	2		/* unsigned/signed long */
1458c2ecf20Sopenharmony_ci#define PLUS	4		/* show plus */
1468c2ecf20Sopenharmony_ci#define SPACE	8		/* space if plus */
1478c2ecf20Sopenharmony_ci#define LEFT	16		/* left justified */
1488c2ecf20Sopenharmony_ci#define SMALL	32		/* Must be 32 == 0x20 */
1498c2ecf20Sopenharmony_ci#define SPECIAL	64		/* 0x */
1508c2ecf20Sopenharmony_ci#define WIDE	128		/* UTF-16 string */
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic
1538c2ecf20Sopenharmony_ciint get_flags(const char **fmt)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	int flags = 0;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	do {
1588c2ecf20Sopenharmony_ci		switch (**fmt) {
1598c2ecf20Sopenharmony_ci		case '-':
1608c2ecf20Sopenharmony_ci			flags |= LEFT;
1618c2ecf20Sopenharmony_ci			break;
1628c2ecf20Sopenharmony_ci		case '+':
1638c2ecf20Sopenharmony_ci			flags |= PLUS;
1648c2ecf20Sopenharmony_ci			break;
1658c2ecf20Sopenharmony_ci		case ' ':
1668c2ecf20Sopenharmony_ci			flags |= SPACE;
1678c2ecf20Sopenharmony_ci			break;
1688c2ecf20Sopenharmony_ci		case '#':
1698c2ecf20Sopenharmony_ci			flags |= SPECIAL;
1708c2ecf20Sopenharmony_ci			break;
1718c2ecf20Sopenharmony_ci		case '0':
1728c2ecf20Sopenharmony_ci			flags |= ZEROPAD;
1738c2ecf20Sopenharmony_ci			break;
1748c2ecf20Sopenharmony_ci		default:
1758c2ecf20Sopenharmony_ci			return flags;
1768c2ecf20Sopenharmony_ci		}
1778c2ecf20Sopenharmony_ci		++(*fmt);
1788c2ecf20Sopenharmony_ci	} while (1);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic
1828c2ecf20Sopenharmony_ciint get_int(const char **fmt, va_list *ap)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	if (isdigit(**fmt))
1858c2ecf20Sopenharmony_ci		return skip_atoi(fmt);
1868c2ecf20Sopenharmony_ci	if (**fmt == '*') {
1878c2ecf20Sopenharmony_ci		++(*fmt);
1888c2ecf20Sopenharmony_ci		/* it's the next argument */
1898c2ecf20Sopenharmony_ci		return va_arg(*ap, int);
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	return 0;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic
1958c2ecf20Sopenharmony_ciunsigned long long get_number(int sign, int qualifier, va_list *ap)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	if (sign) {
1988c2ecf20Sopenharmony_ci		switch (qualifier) {
1998c2ecf20Sopenharmony_ci		case 'L':
2008c2ecf20Sopenharmony_ci			return va_arg(*ap, long long);
2018c2ecf20Sopenharmony_ci		case 'l':
2028c2ecf20Sopenharmony_ci			return va_arg(*ap, long);
2038c2ecf20Sopenharmony_ci		case 'h':
2048c2ecf20Sopenharmony_ci			return (short)va_arg(*ap, int);
2058c2ecf20Sopenharmony_ci		case 'H':
2068c2ecf20Sopenharmony_ci			return (signed char)va_arg(*ap, int);
2078c2ecf20Sopenharmony_ci		default:
2088c2ecf20Sopenharmony_ci			return va_arg(*ap, int);
2098c2ecf20Sopenharmony_ci		};
2108c2ecf20Sopenharmony_ci	} else {
2118c2ecf20Sopenharmony_ci		switch (qualifier) {
2128c2ecf20Sopenharmony_ci		case 'L':
2138c2ecf20Sopenharmony_ci			return va_arg(*ap, unsigned long long);
2148c2ecf20Sopenharmony_ci		case 'l':
2158c2ecf20Sopenharmony_ci			return va_arg(*ap, unsigned long);
2168c2ecf20Sopenharmony_ci		case 'h':
2178c2ecf20Sopenharmony_ci			return (unsigned short)va_arg(*ap, int);
2188c2ecf20Sopenharmony_ci		case 'H':
2198c2ecf20Sopenharmony_ci			return (unsigned char)va_arg(*ap, int);
2208c2ecf20Sopenharmony_ci		default:
2218c2ecf20Sopenharmony_ci			return va_arg(*ap, unsigned int);
2228c2ecf20Sopenharmony_ci		}
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic
2278c2ecf20Sopenharmony_cichar get_sign(long long *num, int flags)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	if (!(flags & SIGN))
2308c2ecf20Sopenharmony_ci		return 0;
2318c2ecf20Sopenharmony_ci	if (*num < 0) {
2328c2ecf20Sopenharmony_ci		*num = -(*num);
2338c2ecf20Sopenharmony_ci		return '-';
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	if (flags & PLUS)
2368c2ecf20Sopenharmony_ci		return '+';
2378c2ecf20Sopenharmony_ci	if (flags & SPACE)
2388c2ecf20Sopenharmony_ci		return ' ';
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic
2438c2ecf20Sopenharmony_cisize_t utf16s_utf8nlen(const u16 *s16, size_t maxlen)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	size_t len, clen;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	for (len = 0; len < maxlen && *s16; len += clen) {
2488c2ecf20Sopenharmony_ci		u16 c0 = *s16++;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		/* First, get the length for a BMP character */
2518c2ecf20Sopenharmony_ci		clen = 1 + (c0 >= 0x80) + (c0 >= 0x800);
2528c2ecf20Sopenharmony_ci		if (len + clen > maxlen)
2538c2ecf20Sopenharmony_ci			break;
2548c2ecf20Sopenharmony_ci		/*
2558c2ecf20Sopenharmony_ci		 * If this is a high surrogate, and we're already at maxlen, we
2568c2ecf20Sopenharmony_ci		 * can't include the character if it's a valid surrogate pair.
2578c2ecf20Sopenharmony_ci		 * Avoid accessing one extra word just to check if it's valid
2588c2ecf20Sopenharmony_ci		 * or not.
2598c2ecf20Sopenharmony_ci		 */
2608c2ecf20Sopenharmony_ci		if ((c0 & 0xfc00) == 0xd800) {
2618c2ecf20Sopenharmony_ci			if (len + clen == maxlen)
2628c2ecf20Sopenharmony_ci				break;
2638c2ecf20Sopenharmony_ci			if ((*s16 & 0xfc00) == 0xdc00) {
2648c2ecf20Sopenharmony_ci				++s16;
2658c2ecf20Sopenharmony_ci				++clen;
2668c2ecf20Sopenharmony_ci			}
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return len;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic
2748c2ecf20Sopenharmony_ciu32 utf16_to_utf32(const u16 **s16)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	u16 c0, c1;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	c0 = *(*s16)++;
2798c2ecf20Sopenharmony_ci	/* not a surrogate */
2808c2ecf20Sopenharmony_ci	if ((c0 & 0xf800) != 0xd800)
2818c2ecf20Sopenharmony_ci		return c0;
2828c2ecf20Sopenharmony_ci	/* invalid: low surrogate instead of high */
2838c2ecf20Sopenharmony_ci	if (c0 & 0x0400)
2848c2ecf20Sopenharmony_ci		return 0xfffd;
2858c2ecf20Sopenharmony_ci	c1 = **s16;
2868c2ecf20Sopenharmony_ci	/* invalid: missing low surrogate */
2878c2ecf20Sopenharmony_ci	if ((c1 & 0xfc00) != 0xdc00)
2888c2ecf20Sopenharmony_ci		return 0xfffd;
2898c2ecf20Sopenharmony_ci	/* valid surrogate pair */
2908c2ecf20Sopenharmony_ci	++(*s16);
2918c2ecf20Sopenharmony_ci	return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci#define PUTC(c) \
2958c2ecf20Sopenharmony_cido {				\
2968c2ecf20Sopenharmony_ci	if (pos < size)		\
2978c2ecf20Sopenharmony_ci		buf[pos] = (c);	\
2988c2ecf20Sopenharmony_ci	++pos;			\
2998c2ecf20Sopenharmony_ci} while (0);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ciint vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	/* The maximum space required is to print a 64-bit number in octal */
3048c2ecf20Sopenharmony_ci	char tmp[(sizeof(unsigned long long) * 8 + 2) / 3];
3058c2ecf20Sopenharmony_ci	char *tmp_end = &tmp[ARRAY_SIZE(tmp)];
3068c2ecf20Sopenharmony_ci	long long num;
3078c2ecf20Sopenharmony_ci	int base;
3088c2ecf20Sopenharmony_ci	const char *s;
3098c2ecf20Sopenharmony_ci	size_t len, pos;
3108c2ecf20Sopenharmony_ci	char sign;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	int flags;		/* flags to number() */
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	int field_width;	/* width of output field */
3158c2ecf20Sopenharmony_ci	int precision;		/* min. # of digits for integers; max
3168c2ecf20Sopenharmony_ci				   number of chars for from string */
3178c2ecf20Sopenharmony_ci	int qualifier;		/* 'h', 'hh', 'l' or 'll' for integer fields */
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	va_list args;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/*
3228c2ecf20Sopenharmony_ci	 * We want to pass our input va_list to helper functions by reference,
3238c2ecf20Sopenharmony_ci	 * but there's an annoying edge case. If va_list was originally passed
3248c2ecf20Sopenharmony_ci	 * to us by value, we could just pass &ap down to the helpers. This is
3258c2ecf20Sopenharmony_ci	 * the case on, for example, X86_32.
3268c2ecf20Sopenharmony_ci	 * However, on X86_64 (and possibly others), va_list is actually a
3278c2ecf20Sopenharmony_ci	 * size-1 array containing a structure. Our function parameter ap has
3288c2ecf20Sopenharmony_ci	 * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],
3298c2ecf20Sopenharmony_ci	 * which is what will be expected by a function taking a va_list *
3308c2ecf20Sopenharmony_ci	 * parameter.
3318c2ecf20Sopenharmony_ci	 * One standard way to solve this mess is by creating a copy in a local
3328c2ecf20Sopenharmony_ci	 * variable of type va_list and then passing a pointer to that local
3338c2ecf20Sopenharmony_ci	 * copy instead, which is what we do here.
3348c2ecf20Sopenharmony_ci	 */
3358c2ecf20Sopenharmony_ci	va_copy(args, ap);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	for (pos = 0; *fmt; ++fmt) {
3388c2ecf20Sopenharmony_ci		if (*fmt != '%' || *++fmt == '%') {
3398c2ecf20Sopenharmony_ci			PUTC(*fmt);
3408c2ecf20Sopenharmony_ci			continue;
3418c2ecf20Sopenharmony_ci		}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		/* process flags */
3448c2ecf20Sopenharmony_ci		flags = get_flags(&fmt);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci		/* get field width */
3478c2ecf20Sopenharmony_ci		field_width = get_int(&fmt, &args);
3488c2ecf20Sopenharmony_ci		if (field_width < 0) {
3498c2ecf20Sopenharmony_ci			field_width = -field_width;
3508c2ecf20Sopenharmony_ci			flags |= LEFT;
3518c2ecf20Sopenharmony_ci		}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		if (flags & LEFT)
3548c2ecf20Sopenharmony_ci			flags &= ~ZEROPAD;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci		/* get the precision */
3578c2ecf20Sopenharmony_ci		precision = -1;
3588c2ecf20Sopenharmony_ci		if (*fmt == '.') {
3598c2ecf20Sopenharmony_ci			++fmt;
3608c2ecf20Sopenharmony_ci			precision = get_int(&fmt, &args);
3618c2ecf20Sopenharmony_ci			if (precision >= 0)
3628c2ecf20Sopenharmony_ci				flags &= ~ZEROPAD;
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		/* get the conversion qualifier */
3668c2ecf20Sopenharmony_ci		qualifier = -1;
3678c2ecf20Sopenharmony_ci		if (*fmt == 'h' || *fmt == 'l') {
3688c2ecf20Sopenharmony_ci			qualifier = *fmt;
3698c2ecf20Sopenharmony_ci			++fmt;
3708c2ecf20Sopenharmony_ci			if (qualifier == *fmt) {
3718c2ecf20Sopenharmony_ci				qualifier -= 'a'-'A';
3728c2ecf20Sopenharmony_ci				++fmt;
3738c2ecf20Sopenharmony_ci			}
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		sign = 0;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci		switch (*fmt) {
3798c2ecf20Sopenharmony_ci		case 'c':
3808c2ecf20Sopenharmony_ci			flags &= LEFT;
3818c2ecf20Sopenharmony_ci			s = tmp;
3828c2ecf20Sopenharmony_ci			if (qualifier == 'l') {
3838c2ecf20Sopenharmony_ci				((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int);
3848c2ecf20Sopenharmony_ci				((u16 *)tmp)[1] = L'\0';
3858c2ecf20Sopenharmony_ci				precision = INT_MAX;
3868c2ecf20Sopenharmony_ci				goto wstring;
3878c2ecf20Sopenharmony_ci			} else {
3888c2ecf20Sopenharmony_ci				tmp[0] = (unsigned char)va_arg(args, int);
3898c2ecf20Sopenharmony_ci				precision = len = 1;
3908c2ecf20Sopenharmony_ci			}
3918c2ecf20Sopenharmony_ci			goto output;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		case 's':
3948c2ecf20Sopenharmony_ci			flags &= LEFT;
3958c2ecf20Sopenharmony_ci			if (precision < 0)
3968c2ecf20Sopenharmony_ci				precision = INT_MAX;
3978c2ecf20Sopenharmony_ci			s = va_arg(args, void *);
3988c2ecf20Sopenharmony_ci			if (!s)
3998c2ecf20Sopenharmony_ci				s = precision < 6 ? "" : "(null)";
4008c2ecf20Sopenharmony_ci			else if (qualifier == 'l') {
4018c2ecf20Sopenharmony_ci		wstring:
4028c2ecf20Sopenharmony_ci				flags |= WIDE;
4038c2ecf20Sopenharmony_ci				precision = len = utf16s_utf8nlen((const u16 *)s, precision);
4048c2ecf20Sopenharmony_ci				goto output;
4058c2ecf20Sopenharmony_ci			}
4068c2ecf20Sopenharmony_ci			precision = len = strnlen(s, precision);
4078c2ecf20Sopenharmony_ci			goto output;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci			/* integer number formats - set up the flags and "break" */
4108c2ecf20Sopenharmony_ci		case 'o':
4118c2ecf20Sopenharmony_ci			base = 8;
4128c2ecf20Sopenharmony_ci			break;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci		case 'p':
4158c2ecf20Sopenharmony_ci			if (precision < 0)
4168c2ecf20Sopenharmony_ci				precision = 2 * sizeof(void *);
4178c2ecf20Sopenharmony_ci			fallthrough;
4188c2ecf20Sopenharmony_ci		case 'x':
4198c2ecf20Sopenharmony_ci			flags |= SMALL;
4208c2ecf20Sopenharmony_ci			fallthrough;
4218c2ecf20Sopenharmony_ci		case 'X':
4228c2ecf20Sopenharmony_ci			base = 16;
4238c2ecf20Sopenharmony_ci			break;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		case 'd':
4268c2ecf20Sopenharmony_ci		case 'i':
4278c2ecf20Sopenharmony_ci			flags |= SIGN;
4288c2ecf20Sopenharmony_ci			fallthrough;
4298c2ecf20Sopenharmony_ci		case 'u':
4308c2ecf20Sopenharmony_ci			flags &= ~SPECIAL;
4318c2ecf20Sopenharmony_ci			base = 10;
4328c2ecf20Sopenharmony_ci			break;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		default:
4358c2ecf20Sopenharmony_ci			/*
4368c2ecf20Sopenharmony_ci			 * Bail out if the conversion specifier is invalid.
4378c2ecf20Sopenharmony_ci			 * There's probably a typo in the format string and the
4388c2ecf20Sopenharmony_ci			 * remaining specifiers are unlikely to match up with
4398c2ecf20Sopenharmony_ci			 * the arguments.
4408c2ecf20Sopenharmony_ci			 */
4418c2ecf20Sopenharmony_ci			goto fail;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci		if (*fmt == 'p') {
4448c2ecf20Sopenharmony_ci			num = (unsigned long)va_arg(args, void *);
4458c2ecf20Sopenharmony_ci		} else {
4468c2ecf20Sopenharmony_ci			num = get_number(flags & SIGN, qualifier, &args);
4478c2ecf20Sopenharmony_ci		}
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		sign = get_sign(&num, flags);
4508c2ecf20Sopenharmony_ci		if (sign)
4518c2ecf20Sopenharmony_ci			--field_width;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		s = number(tmp_end, num, base, flags & SMALL);
4548c2ecf20Sopenharmony_ci		len = tmp_end - s;
4558c2ecf20Sopenharmony_ci		/* default precision is 1 */
4568c2ecf20Sopenharmony_ci		if (precision < 0)
4578c2ecf20Sopenharmony_ci			precision = 1;
4588c2ecf20Sopenharmony_ci		/* precision is minimum number of digits to print */
4598c2ecf20Sopenharmony_ci		if (precision < len)
4608c2ecf20Sopenharmony_ci			precision = len;
4618c2ecf20Sopenharmony_ci		if (flags & SPECIAL) {
4628c2ecf20Sopenharmony_ci			/*
4638c2ecf20Sopenharmony_ci			 * For octal, a leading 0 is printed only if necessary,
4648c2ecf20Sopenharmony_ci			 * i.e. if it's not already there because of the
4658c2ecf20Sopenharmony_ci			 * precision.
4668c2ecf20Sopenharmony_ci			 */
4678c2ecf20Sopenharmony_ci			if (base == 8 && precision == len)
4688c2ecf20Sopenharmony_ci				++precision;
4698c2ecf20Sopenharmony_ci			/*
4708c2ecf20Sopenharmony_ci			 * For hexadecimal, the leading 0x is skipped if the
4718c2ecf20Sopenharmony_ci			 * output is empty, i.e. both the number and the
4728c2ecf20Sopenharmony_ci			 * precision are 0.
4738c2ecf20Sopenharmony_ci			 */
4748c2ecf20Sopenharmony_ci			if (base == 16 && precision > 0)
4758c2ecf20Sopenharmony_ci				field_width -= 2;
4768c2ecf20Sopenharmony_ci			else
4778c2ecf20Sopenharmony_ci				flags &= ~SPECIAL;
4788c2ecf20Sopenharmony_ci		}
4798c2ecf20Sopenharmony_ci		/*
4808c2ecf20Sopenharmony_ci		 * For zero padding, increase the precision to fill the field
4818c2ecf20Sopenharmony_ci		 * width.
4828c2ecf20Sopenharmony_ci		 */
4838c2ecf20Sopenharmony_ci		if ((flags & ZEROPAD) && field_width > precision)
4848c2ecf20Sopenharmony_ci			precision = field_width;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cioutput:
4878c2ecf20Sopenharmony_ci		/* Calculate the padding necessary */
4888c2ecf20Sopenharmony_ci		field_width -= precision;
4898c2ecf20Sopenharmony_ci		/* Leading padding with ' ' */
4908c2ecf20Sopenharmony_ci		if (!(flags & LEFT))
4918c2ecf20Sopenharmony_ci			while (field_width-- > 0)
4928c2ecf20Sopenharmony_ci				PUTC(' ');
4938c2ecf20Sopenharmony_ci		/* sign */
4948c2ecf20Sopenharmony_ci		if (sign)
4958c2ecf20Sopenharmony_ci			PUTC(sign);
4968c2ecf20Sopenharmony_ci		/* 0x/0X for hexadecimal */
4978c2ecf20Sopenharmony_ci		if (flags & SPECIAL) {
4988c2ecf20Sopenharmony_ci			PUTC('0');
4998c2ecf20Sopenharmony_ci			PUTC( 'X' | (flags & SMALL));
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci		/* Zero padding and excess precision */
5028c2ecf20Sopenharmony_ci		while (precision-- > len)
5038c2ecf20Sopenharmony_ci			PUTC('0');
5048c2ecf20Sopenharmony_ci		/* Actual output */
5058c2ecf20Sopenharmony_ci		if (flags & WIDE) {
5068c2ecf20Sopenharmony_ci			const u16 *ws = (const u16 *)s;
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci			while (len-- > 0) {
5098c2ecf20Sopenharmony_ci				u32 c32 = utf16_to_utf32(&ws);
5108c2ecf20Sopenharmony_ci				u8 *s8;
5118c2ecf20Sopenharmony_ci				size_t clen;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci				if (c32 < 0x80) {
5148c2ecf20Sopenharmony_ci					PUTC(c32);
5158c2ecf20Sopenharmony_ci					continue;
5168c2ecf20Sopenharmony_ci				}
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci				/* Number of trailing octets */
5198c2ecf20Sopenharmony_ci				clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci				len -= clen;
5228c2ecf20Sopenharmony_ci				s8 = (u8 *)&buf[pos];
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci				/* Avoid writing partial character */
5258c2ecf20Sopenharmony_ci				PUTC('\0');
5268c2ecf20Sopenharmony_ci				pos += clen;
5278c2ecf20Sopenharmony_ci				if (pos >= size)
5288c2ecf20Sopenharmony_ci					continue;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci				/* Set high bits of leading octet */
5318c2ecf20Sopenharmony_ci				*s8 = (0xf00 >> 1) >> clen;
5328c2ecf20Sopenharmony_ci				/* Write trailing octets in reverse order */
5338c2ecf20Sopenharmony_ci				for (s8 += clen; clen; --clen, c32 >>= 6)
5348c2ecf20Sopenharmony_ci					*s8-- = 0x80 | (c32 & 0x3f);
5358c2ecf20Sopenharmony_ci				/* Set low bits of leading octet */
5368c2ecf20Sopenharmony_ci				*s8 |= c32;
5378c2ecf20Sopenharmony_ci			}
5388c2ecf20Sopenharmony_ci		} else {
5398c2ecf20Sopenharmony_ci			while (len-- > 0)
5408c2ecf20Sopenharmony_ci				PUTC(*s++);
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci		/* Trailing padding with ' ' */
5438c2ecf20Sopenharmony_ci		while (field_width-- > 0)
5448c2ecf20Sopenharmony_ci			PUTC(' ');
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_cifail:
5478c2ecf20Sopenharmony_ci	va_end(args);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (size)
5508c2ecf20Sopenharmony_ci		buf[min(pos, size-1)] = '\0';
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	return pos;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ciint snprintf(char *buf, size_t size, const char *fmt, ...)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	va_list args;
5588c2ecf20Sopenharmony_ci	int i;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	va_start(args, fmt);
5618c2ecf20Sopenharmony_ci	i = vsnprintf(buf, size, fmt, args);
5628c2ecf20Sopenharmony_ci	va_end(args);
5638c2ecf20Sopenharmony_ci	return i;
5648c2ecf20Sopenharmony_ci}
565