564 lines · 11.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* -*- linux-c -*- ------------------------------------------------------- * * *   Copyright (C) 1991, 1992 Linus Torvalds *   Copyright 2007 rPath, Inc. - All Rights Reserved * * ----------------------------------------------------------------------- */ /* * Oh, it's a waste of space, but oh-so-yummy for debugging. */ #include <linux/stdarg.h> #include <linux/compiler.h>#include <linux/ctype.h>#include <linux/kernel.h>#include <linux/limits.h>#include <linux/string.h>#include <linux/types.h> staticint skip_atoi(const char **s){	int i = 0; 	while (isdigit(**s))		i = i * 10 + *((*s)++) - '0';	return i;} /* * put_dec_full4 handles numbers in the range 0 <= r < 10000. * The multiplier 0xccd is round(2^15/10), and the approximation * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389. */staticvoid put_dec_full4(char *end, unsigned int r){	int i; 	for (i = 0; i < 3; i++) {		unsigned int q = (r * 0xccd) >> 15;		*--end = '0' + (r - q * 10);		r = q;	}	*--end = '0' + r;} /* put_dec is copied from lib/vsprintf.c with small modifications */ /* * Call put_dec_full4 on x % 10000, return x / 10000. * The approximation x/10000 == (x * 0x346DC5D7) >> 43 * holds for all x < 1,128,869,999.  The largest value this * helper will ever be asked to convert is 1,125,520,955. * (second call in the put_dec code, assuming n is all-ones). */staticunsigned int put_dec_helper4(char *end, unsigned int x){	unsigned int q = (x * 0x346DC5D7ULL) >> 43; 	put_dec_full4(end, x - q * 10000);	return q;} /* Based on code by Douglas W. Jones found at * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour> * (with permission from the author). * Performs no 64-bit division and hence should be fast on 32-bit machines. */staticchar *put_dec(char *end, unsigned long long n){	unsigned int d3, d2, d1, q, h;	char *p = end; 	d1  = ((unsigned int)n >> 16); /* implicit "& 0xffff" */	h   = (n >> 32);	d2  = (h      ) & 0xffff;	d3  = (h >> 16); /* implicit "& 0xffff" */ 	/* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0	     = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */	q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff);	q = put_dec_helper4(p, q);	p -= 4; 	q += 7671 * d3 + 9496 * d2 + 6 * d1;	q = put_dec_helper4(p, q);	p -= 4; 	q += 4749 * d3 + 42 * d2;	q = put_dec_helper4(p, q);	p -= 4; 	q += 281 * d3;	q = put_dec_helper4(p, q);	p -= 4; 	put_dec_full4(p, q);	p -= 4; 	/* strip off the extra 0's we printed */	while (p < end && *p == '0')		++p; 	return p;} staticchar *number(char *end, unsigned long long num, int base, char locase){	/*	 * locase = 0 or 0x20. ORing digits or letters with 'locase'	 * produces same digits or (maybe lowercased) letters	 */ 	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ 	switch (base) {	case 10:		if (num != 0)			end = put_dec(end, num);		break;	case 8:		for (; num != 0; num >>= 3)			*--end = '0' + (num & 07);		break;	case 16:		for (; num != 0; num >>= 4)			*--end = digits[num & 0xf] | locase;		break;	default:		unreachable();	} 	return end;} #define ZEROPAD	1		/* pad with zero */#define SIGN	2		/* unsigned/signed long */#define PLUS	4		/* show plus */#define SPACE	8		/* space if plus */#define LEFT	16		/* left justified */#define SMALL	32		/* Must be 32 == 0x20 */#define SPECIAL	64		/* 0x */#define WIDE	128		/* UTF-16 string */ staticint get_flags(const char **fmt){	int flags = 0; 	do {		switch (**fmt) {		case '-':			flags |= LEFT;			break;		case '+':			flags |= PLUS;			break;		case ' ':			flags |= SPACE;			break;		case '#':			flags |= SPECIAL;			break;		case '0':			flags |= ZEROPAD;			break;		default:			return flags;		}		++(*fmt);	} while (1);} staticint get_int(const char **fmt, va_list *ap){	if (isdigit(**fmt))		return skip_atoi(fmt);	if (**fmt == '*') {		++(*fmt);		/* it's the next argument */		return va_arg(*ap, int);	}	return 0;} staticunsigned long long get_number(int sign, int qualifier, va_list *ap){	if (sign) {		switch (qualifier) {		case 'L':			return va_arg(*ap, long long);		case 'l':			return va_arg(*ap, long);		case 'h':			return (short)va_arg(*ap, int);		case 'H':			return (signed char)va_arg(*ap, int);		default:			return va_arg(*ap, int);		};	} else {		switch (qualifier) {		case 'L':			return va_arg(*ap, unsigned long long);		case 'l':			return va_arg(*ap, unsigned long);		case 'h':			return (unsigned short)va_arg(*ap, int);		case 'H':			return (unsigned char)va_arg(*ap, int);		default:			return va_arg(*ap, unsigned int);		}	}} staticchar get_sign(long long *num, int flags){	if (!(flags & SIGN))		return 0;	if (*num < 0) {		*num = -(*num);		return '-';	}	if (flags & PLUS)		return '+';	if (flags & SPACE)		return ' ';	return 0;} staticsize_t utf16s_utf8nlen(const u16 *s16, size_t maxlen){	size_t len, clen; 	for (len = 0; len < maxlen && *s16; len += clen) {		u16 c0 = *s16++; 		/* First, get the length for a BMP character */		clen = 1 + (c0 >= 0x80) + (c0 >= 0x800);		if (len + clen > maxlen)			break;		/*		 * If this is a high surrogate, and we're already at maxlen, we		 * can't include the character if it's a valid surrogate pair.		 * Avoid accessing one extra word just to check if it's valid		 * or not.		 */		if ((c0 & 0xfc00) == 0xd800) {			if (len + clen == maxlen)				break;			if ((*s16 & 0xfc00) == 0xdc00) {				++s16;				++clen;			}		}	} 	return len;} staticu32 utf16_to_utf32(const u16 **s16){	u16 c0, c1; 	c0 = *(*s16)++;	/* not a surrogate */	if ((c0 & 0xf800) != 0xd800)		return c0;	/* invalid: low surrogate instead of high */	if (c0 & 0x0400)		return 0xfffd;	c1 = **s16;	/* invalid: missing low surrogate */	if ((c1 & 0xfc00) != 0xdc00)		return 0xfffd;	/* valid surrogate pair */	++(*s16);	return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;} #define PUTC(c) \do {				\	if (pos < size)		\		buf[pos] = (c);	\	++pos;			\} while (0); int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap){	/* The maximum space required is to print a 64-bit number in octal */	char tmp[(sizeof(unsigned long long) * 8 + 2) / 3];	char *tmp_end = &tmp[ARRAY_SIZE(tmp)];	long long num;	int base;	const char *s;	size_t len, pos;	char sign; 	int flags;		/* flags to number() */ 	int field_width;	/* width of output field */	int precision;		/* min. # of digits for integers; max				   number of chars for from string */	int qualifier;		/* 'h', 'hh', 'l' or 'll' for integer fields */ 	va_list args; 	/*	 * We want to pass our input va_list to helper functions by reference,	 * but there's an annoying edge case. If va_list was originally passed	 * to us by value, we could just pass &ap down to the helpers. This is	 * the case on, for example, X86_32.	 * However, on X86_64 (and possibly others), va_list is actually a	 * size-1 array containing a structure. Our function parameter ap has	 * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1],	 * which is what will be expected by a function taking a va_list *	 * parameter.	 * One standard way to solve this mess is by creating a copy in a local	 * variable of type va_list and then passing a pointer to that local	 * copy instead, which is what we do here.	 */	va_copy(args, ap); 	for (pos = 0; *fmt; ++fmt) {		if (*fmt != '%' || *++fmt == '%') {			PUTC(*fmt);			continue;		} 		/* process flags */		flags = get_flags(&fmt); 		/* get field width */		field_width = get_int(&fmt, &args);		if (field_width < 0) {			field_width = -field_width;			flags |= LEFT;		} 		if (flags & LEFT)			flags &= ~ZEROPAD; 		/* get the precision */		precision = -1;		if (*fmt == '.') {			++fmt;			precision = get_int(&fmt, &args);			if (precision >= 0)				flags &= ~ZEROPAD;		} 		/* get the conversion qualifier */		qualifier = -1;		if (*fmt == 'h' || *fmt == 'l') {			qualifier = *fmt;			++fmt;			if (qualifier == *fmt) {				qualifier -= 'a'-'A';				++fmt;			}		} 		sign = 0; 		switch (*fmt) {		case 'c':			flags &= LEFT;			s = tmp;			if (qualifier == 'l') {				((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int);				((u16 *)tmp)[1] = L'\0';				precision = INT_MAX;				goto wstring;			} else {				tmp[0] = (unsigned char)va_arg(args, int);				precision = len = 1;			}			goto output; 		case 's':			flags &= LEFT;			if (precision < 0)				precision = INT_MAX;			s = va_arg(args, void *);			if (!s)				s = precision < 6 ? "" : "(null)";			else if (qualifier == 'l') {		wstring:				flags |= WIDE;				precision = len = utf16s_utf8nlen((const u16 *)s, precision);				goto output;			}			precision = len = strnlen(s, precision);			goto output; 			/* integer number formats - set up the flags and "break" */		case 'o':			base = 8;			break; 		case 'p':			if (precision < 0)				precision = 2 * sizeof(void *);			fallthrough;		case 'x':			flags |= SMALL;			fallthrough;		case 'X':			base = 16;			break; 		case 'd':		case 'i':			flags |= SIGN;			fallthrough;		case 'u':			flags &= ~SPECIAL;			base = 10;			break; 		default:			/*			 * Bail out if the conversion specifier is invalid.			 * There's probably a typo in the format string and the			 * remaining specifiers are unlikely to match up with			 * the arguments.			 */			goto fail;		}		if (*fmt == 'p') {			num = (unsigned long)va_arg(args, void *);		} else {			num = get_number(flags & SIGN, qualifier, &args);		} 		sign = get_sign(&num, flags);		if (sign)			--field_width; 		s = number(tmp_end, num, base, flags & SMALL);		len = tmp_end - s;		/* default precision is 1 */		if (precision < 0)			precision = 1;		/* precision is minimum number of digits to print */		if (precision < len)			precision = len;		if (flags & SPECIAL) {			/*			 * For octal, a leading 0 is printed only if necessary,			 * i.e. if it's not already there because of the			 * precision.			 */			if (base == 8 && precision == len)				++precision;			/*			 * For hexadecimal, the leading 0x is skipped if the			 * output is empty, i.e. both the number and the			 * precision are 0.			 */			if (base == 16 && precision > 0)				field_width -= 2;			else				flags &= ~SPECIAL;		}		/*		 * For zero padding, increase the precision to fill the field		 * width.		 */		if ((flags & ZEROPAD) && field_width > precision)			precision = field_width; output:		/* Calculate the padding necessary */		field_width -= precision;		/* Leading padding with ' ' */		if (!(flags & LEFT))			while (field_width-- > 0)				PUTC(' ');		/* sign */		if (sign)			PUTC(sign);		/* 0x/0X for hexadecimal */		if (flags & SPECIAL) {			PUTC('0');			PUTC( 'X' | (flags & SMALL));		}

Lines 1–500 of 564.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t