brintos

brintos / linux-shallow public Read only

0
0
Text · 11.8 KiB · 71c71c2 Raw
565 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* -*- linux-c -*- ------------------------------------------------------- *3 *4 *   Copyright (C) 1991, 1992 Linus Torvalds5 *   Copyright 2007 rPath, Inc. - All Rights Reserved6 *7 * ----------------------------------------------------------------------- */8 9/*10 * Oh, it's a waste of space, but oh-so-yummy for debugging.11 */12 13#include <linux/stdarg.h>14 15#include <linux/compiler.h>16#include <linux/ctype.h>17#include <linux/kernel.h>18#include <linux/limits.h>19#include <linux/string.h>20#include <linux/types.h>21 22static23int skip_atoi(const char **s)24{25	int i = 0;26 27	while (isdigit(**s))28		i = i * 10 + *((*s)++) - '0';29	return i;30}31 32/*33 * put_dec_full4 handles numbers in the range 0 <= r < 10000.34 * The multiplier 0xccd is round(2^15/10), and the approximation35 * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389.36 */37static38void put_dec_full4(char *end, unsigned int r)39{40	int i;41 42	for (i = 0; i < 3; i++) {43		unsigned int q = (r * 0xccd) >> 15;44		*--end = '0' + (r - q * 10);45		r = q;46	}47	*--end = '0' + r;48}49 50/* put_dec is copied from lib/vsprintf.c with small modifications */51 52/*53 * Call put_dec_full4 on x % 10000, return x / 10000.54 * The approximation x/10000 == (x * 0x346DC5D7) >> 4355 * holds for all x < 1,128,869,999.  The largest value this56 * helper will ever be asked to convert is 1,125,520,955.57 * (second call in the put_dec code, assuming n is all-ones).58 */59static60unsigned int put_dec_helper4(char *end, unsigned int x)61{62	unsigned int q = (x * 0x346DC5D7ULL) >> 43;63 64	put_dec_full4(end, x - q * 10000);65	return q;66}67 68/* Based on code by Douglas W. Jones found at69 * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>70 * (with permission from the author).71 * Performs no 64-bit division and hence should be fast on 32-bit machines.72 */73static74char *put_dec(char *end, unsigned long long n)75{76	unsigned int d3, d2, d1, q, h;77	char *p = end;78 79	d1  = ((unsigned int)n >> 16); /* implicit "& 0xffff" */80	h   = (n >> 32);81	d2  = (h      ) & 0xffff;82	d3  = (h >> 16); /* implicit "& 0xffff" */83 84	/* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d085	     = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */86	q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff);87	q = put_dec_helper4(p, q);88	p -= 4;89 90	q += 7671 * d3 + 9496 * d2 + 6 * d1;91	q = put_dec_helper4(p, q);92	p -= 4;93 94	q += 4749 * d3 + 42 * d2;95	q = put_dec_helper4(p, q);96	p -= 4;97 98	q += 281 * d3;99	q = put_dec_helper4(p, q);100	p -= 4;101 102	put_dec_full4(p, q);103	p -= 4;104 105	/* strip off the extra 0's we printed */106	while (p < end && *p == '0')107		++p;108 109	return p;110}111 112static113char *number(char *end, unsigned long long num, int base, char locase)114{115	/*116	 * locase = 0 or 0x20. ORing digits or letters with 'locase'117	 * produces same digits or (maybe lowercased) letters118	 */119 120	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */121	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */122 123	switch (base) {124	case 10:125		if (num != 0)126			end = put_dec(end, num);127		break;128	case 8:129		for (; num != 0; num >>= 3)130			*--end = '0' + (num & 07);131		break;132	case 16:133		for (; num != 0; num >>= 4)134			*--end = digits[num & 0xf] | locase;135		break;136	default:137		unreachable();138	}139 140	return end;141}142 143#define ZEROPAD	1		/* pad with zero */144#define SIGN	2		/* unsigned/signed long */145#define PLUS	4		/* show plus */146#define SPACE	8		/* space if plus */147#define LEFT	16		/* left justified */148#define SMALL	32		/* Must be 32 == 0x20 */149#define SPECIAL	64		/* 0x */150#define WIDE	128		/* UTF-16 string */151 152static153int get_flags(const char **fmt)154{155	int flags = 0;156 157	do {158		switch (**fmt) {159		case '-':160			flags |= LEFT;161			break;162		case '+':163			flags |= PLUS;164			break;165		case ' ':166			flags |= SPACE;167			break;168		case '#':169			flags |= SPECIAL;170			break;171		case '0':172			flags |= ZEROPAD;173			break;174		default:175			return flags;176		}177		++(*fmt);178	} while (1);179}180 181static182int get_int(const char **fmt, va_list *ap)183{184	if (isdigit(**fmt))185		return skip_atoi(fmt);186	if (**fmt == '*') {187		++(*fmt);188		/* it's the next argument */189		return va_arg(*ap, int);190	}191	return 0;192}193 194static195unsigned long long get_number(int sign, int qualifier, va_list *ap)196{197	if (sign) {198		switch (qualifier) {199		case 'L':200			return va_arg(*ap, long long);201		case 'l':202			return va_arg(*ap, long);203		case 'h':204			return (short)va_arg(*ap, int);205		case 'H':206			return (signed char)va_arg(*ap, int);207		default:208			return va_arg(*ap, int);209		};210	} else {211		switch (qualifier) {212		case 'L':213			return va_arg(*ap, unsigned long long);214		case 'l':215			return va_arg(*ap, unsigned long);216		case 'h':217			return (unsigned short)va_arg(*ap, int);218		case 'H':219			return (unsigned char)va_arg(*ap, int);220		default:221			return va_arg(*ap, unsigned int);222		}223	}224}225 226static227char get_sign(long long *num, int flags)228{229	if (!(flags & SIGN))230		return 0;231	if (*num < 0) {232		*num = -(*num);233		return '-';234	}235	if (flags & PLUS)236		return '+';237	if (flags & SPACE)238		return ' ';239	return 0;240}241 242static243size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen)244{245	size_t len, clen;246 247	for (len = 0; len < maxlen && *s16; len += clen) {248		u16 c0 = *s16++;249 250		/* First, get the length for a BMP character */251		clen = 1 + (c0 >= 0x80) + (c0 >= 0x800);252		if (len + clen > maxlen)253			break;254		/*255		 * If this is a high surrogate, and we're already at maxlen, we256		 * can't include the character if it's a valid surrogate pair.257		 * Avoid accessing one extra word just to check if it's valid258		 * or not.259		 */260		if ((c0 & 0xfc00) == 0xd800) {261			if (len + clen == maxlen)262				break;263			if ((*s16 & 0xfc00) == 0xdc00) {264				++s16;265				++clen;266			}267		}268	}269 270	return len;271}272 273static274u32 utf16_to_utf32(const u16 **s16)275{276	u16 c0, c1;277 278	c0 = *(*s16)++;279	/* not a surrogate */280	if ((c0 & 0xf800) != 0xd800)281		return c0;282	/* invalid: low surrogate instead of high */283	if (c0 & 0x0400)284		return 0xfffd;285	c1 = **s16;286	/* invalid: missing low surrogate */287	if ((c1 & 0xfc00) != 0xdc00)288		return 0xfffd;289	/* valid surrogate pair */290	++(*s16);291	return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;292}293 294#define PUTC(c) \295do {				\296	if (pos < size)		\297		buf[pos] = (c);	\298	++pos;			\299} while (0);300 301int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap)302{303	/* The maximum space required is to print a 64-bit number in octal */304	char tmp[(sizeof(unsigned long long) * 8 + 2) / 3];305	char *tmp_end = &tmp[ARRAY_SIZE(tmp)];306	long long num;307	int base;308	const char *s;309	size_t len, pos;310	char sign;311 312	int flags;		/* flags to number() */313 314	int field_width;	/* width of output field */315	int precision;		/* min. # of digits for integers; max316				   number of chars for from string */317	int qualifier;		/* 'h', 'hh', 'l' or 'll' for integer fields */318 319	va_list args;320 321	/*322	 * We want to pass our input va_list to helper functions by reference,323	 * but there's an annoying edge case. If va_list was originally passed324	 * to us by value, we could just pass &ap down to the helpers. This is325	 * the case on, for example, X86_32.326	 * However, on X86_64 (and possibly others), va_list is actually a327	 * size-1 array containing a structure. Our function parameter ap has328	 * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],329	 * which is what will be expected by a function taking a va_list *330	 * parameter.331	 * One standard way to solve this mess is by creating a copy in a local332	 * variable of type va_list and then passing a pointer to that local333	 * copy instead, which is what we do here.334	 */335	va_copy(args, ap);336 337	for (pos = 0; *fmt; ++fmt) {338		if (*fmt != '%' || *++fmt == '%') {339			PUTC(*fmt);340			continue;341		}342 343		/* process flags */344		flags = get_flags(&fmt);345 346		/* get field width */347		field_width = get_int(&fmt, &args);348		if (field_width < 0) {349			field_width = -field_width;350			flags |= LEFT;351		}352 353		if (flags & LEFT)354			flags &= ~ZEROPAD;355 356		/* get the precision */357		precision = -1;358		if (*fmt == '.') {359			++fmt;360			precision = get_int(&fmt, &args);361			if (precision >= 0)362				flags &= ~ZEROPAD;363		}364 365		/* get the conversion qualifier */366		qualifier = -1;367		if (*fmt == 'h' || *fmt == 'l') {368			qualifier = *fmt;369			++fmt;370			if (qualifier == *fmt) {371				qualifier -= 'a'-'A';372				++fmt;373			}374		}375 376		sign = 0;377 378		switch (*fmt) {379		case 'c':380			flags &= LEFT;381			s = tmp;382			if (qualifier == 'l') {383				((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int);384				((u16 *)tmp)[1] = L'\0';385				precision = INT_MAX;386				goto wstring;387			} else {388				tmp[0] = (unsigned char)va_arg(args, int);389				precision = len = 1;390			}391			goto output;392 393		case 's':394			flags &= LEFT;395			if (precision < 0)396				precision = INT_MAX;397			s = va_arg(args, void *);398			if (!s)399				s = precision < 6 ? "" : "(null)";400			else if (qualifier == 'l') {401		wstring:402				flags |= WIDE;403				precision = len = utf16s_utf8nlen((const u16 *)s, precision);404				goto output;405			}406			precision = len = strnlen(s, precision);407			goto output;408 409			/* integer number formats - set up the flags and "break" */410		case 'o':411			base = 8;412			break;413 414		case 'p':415			if (precision < 0)416				precision = 2 * sizeof(void *);417			fallthrough;418		case 'x':419			flags |= SMALL;420			fallthrough;421		case 'X':422			base = 16;423			break;424 425		case 'd':426		case 'i':427			flags |= SIGN;428			fallthrough;429		case 'u':430			flags &= ~SPECIAL;431			base = 10;432			break;433 434		default:435			/*436			 * Bail out if the conversion specifier is invalid.437			 * There's probably a typo in the format string and the438			 * remaining specifiers are unlikely to match up with439			 * the arguments.440			 */441			goto fail;442		}443		if (*fmt == 'p') {444			num = (unsigned long)va_arg(args, void *);445		} else {446			num = get_number(flags & SIGN, qualifier, &args);447		}448 449		sign = get_sign(&num, flags);450		if (sign)451			--field_width;452 453		s = number(tmp_end, num, base, flags & SMALL);454		len = tmp_end - s;455		/* default precision is 1 */456		if (precision < 0)457			precision = 1;458		/* precision is minimum number of digits to print */459		if (precision < len)460			precision = len;461		if (flags & SPECIAL) {462			/*463			 * For octal, a leading 0 is printed only if necessary,464			 * i.e. if it's not already there because of the465			 * precision.466			 */467			if (base == 8 && precision == len)468				++precision;469			/*470			 * For hexadecimal, the leading 0x is skipped if the471			 * output is empty, i.e. both the number and the472			 * precision are 0.473			 */474			if (base == 16 && precision > 0)475				field_width -= 2;476			else477				flags &= ~SPECIAL;478		}479		/*480		 * For zero padding, increase the precision to fill the field481		 * width.482		 */483		if ((flags & ZEROPAD) && field_width > precision)484			precision = field_width;485 486output:487		/* Calculate the padding necessary */488		field_width -= precision;489		/* Leading padding with ' ' */490		if (!(flags & LEFT))491			while (field_width-- > 0)492				PUTC(' ');493		/* sign */494		if (sign)495			PUTC(sign);496		/* 0x/0X for hexadecimal */497		if (flags & SPECIAL) {498			PUTC('0');499			PUTC( 'X' | (flags & SMALL));500		}501		/* Zero padding and excess precision */502		while (precision-- > len)503			PUTC('0');504		/* Actual output */505		if (flags & WIDE) {506			const u16 *ws = (const u16 *)s;507 508			while (len-- > 0) {509				u32 c32 = utf16_to_utf32(&ws);510				u8 *s8;511				size_t clen;512 513				if (c32 < 0x80) {514					PUTC(c32);515					continue;516				}517 518				/* Number of trailing octets */519				clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000);520 521				len -= clen;522				s8 = (u8 *)&buf[pos];523 524				/* Avoid writing partial character */525				PUTC('\0');526				pos += clen;527				if (pos >= size)528					continue;529 530				/* Set high bits of leading octet */531				*s8 = (0xf00 >> 1) >> clen;532				/* Write trailing octets in reverse order */533				for (s8 += clen; clen; --clen, c32 >>= 6)534					*s8-- = 0x80 | (c32 & 0x3f);535				/* Set low bits of leading octet */536				*s8 |= c32;537			}538		} else {539			while (len-- > 0)540				PUTC(*s++);541		}542		/* Trailing padding with ' ' */543		while (field_width-- > 0)544			PUTC(' ');545	}546fail:547	va_end(args);548 549	if (size)550		buf[min(pos, size-1)] = '\0';551 552	return pos;553}554 555int snprintf(char *buf, size_t size, const char *fmt, ...)556{557	va_list args;558	int i;559 560	va_start(args, fmt);561	i = vsnprintf(buf, size, fmt, args);562	va_end(args);563	return i;564}565