Skip to content
Snippets Groups Projects
vsprintf.c 53.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Linus Torvalds's avatar
    Linus Torvalds committed
    /*
     *  linux/lib/vsprintf.c
     *
     *  Copyright (C) 1991, 1992  Linus Torvalds
     */
    
    /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
    /*
     * Wirzenius wrote this portably, Torvalds fucked it up :-)
     */
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
     * - changed to provide snprintf and vsnprintf functions
     * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
     * - scnprintf and vscnprintf
     */
    
    #include <stdarg.h>
    
    #include <linux/module.h>	/* for KSYM_SYMBOL_LEN */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #include <linux/types.h>
    #include <linux/string.h>
    #include <linux/ctype.h>
    #include <linux/kernel.h>
    
    #include <linux/kallsyms.h>
    
    #include <linux/math64.h>
    
    #include <linux/uaccess.h>
    
    #include <linux/ioport.h>
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    #include <asm/page.h>		/* for PAGE_SIZE */
    
    #include <asm/sections.h>	/* for dereference_function_descriptor() */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    /**
    
     * simple_strtoull - convert a string to an unsigned long long
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * @cp: The start of the string
     * @endp: A pointer to the end of the parsed string will be placed here
     * @base: The number base to use
    
     *
     * This function is obsolete. Please use kstrtoull instead.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	unsigned long long result;
    	unsigned int rv;
    
    	cp = _parse_integer_fixup_radix(cp, &base);
    	rv = _parse_integer(cp, base, &result);
    	/* FIXME */
    	cp += (rv & ~KSTRTOX_OVERFLOW);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (endp)
    		*endp = (char *)cp;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return result;
    }
    
    EXPORT_SYMBOL(simple_strtoull);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /**
    
     * simple_strtoul - convert a string to an unsigned long
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * @cp: The start of the string
     * @endp: A pointer to the end of the parsed string will be placed here
     * @base: The number base to use
    
     *
     * This function is obsolete. Please use kstrtoul instead.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	return simple_strtoull(cp, endp, base);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(simple_strtoul);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /**
    
     * simple_strtol - convert a string to a signed long
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     * @cp: The start of the string
     * @endp: A pointer to the end of the parsed string will be placed here
     * @base: The number base to use
    
     *
     * This function is obsolete. Please use kstrtol instead.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    long simple_strtol(const char *cp, char **endp, unsigned int base)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	if (*cp == '-')
    		return -simple_strtoul(cp + 1, endp, base);
    
    	return simple_strtoul(cp, endp, base);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    /**
     * simple_strtoll - convert a string to a signed long long
     * @cp: The start of the string
     * @endp: A pointer to the end of the parsed string will be placed here
     * @base: The number base to use
    
     *
     * This function is obsolete. Please use kstrtoll instead.
    
    Linus Torvalds's avatar
    Linus Torvalds committed
     */
    
    long long simple_strtoll(const char *cp, char **endp, unsigned int base)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    		return -simple_strtoull(cp + 1, endp, base);
    
    	return simple_strtoull(cp, endp, base);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    }
    
    EXPORT_SYMBOL(simple_strtoll);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    static noinline_for_stack
    int skip_atoi(const char **s)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    	while (isdigit(**s))
    		i = i*10 + *((*s)++) - '0';
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return i;
    }
    
    
    /* Decimal conversion is by far the most typical, and is used
     * for /proc and /sys data. This directly impacts e.g. top performance
     * with many processes running. We optimize it for speed
    
     * using ideas described at <http://www.cs.uiowa.edu/~jones/bcd/divide.html>
     * (with permission from the author, Douglas W. Jones).
     */
    
    #if BITS_PER_LONG != 32 || BITS_PER_LONG_LONG != 64
    /* Formats correctly any integer in [0, 999999999] */
    
    static noinline_for_stack
    
    char *put_dec_full9(char *buf, unsigned q)
    
    	/*
    	 * Possible ways to approx. divide by 10
    	 * (x * 0x1999999a) >> 32 x < 1073741829 (multiply must be 64-bit)
    	 * (x * 0xcccd) >> 19     x <      81920 (x < 262149 when 64-bit mul)
    	 * (x * 0x6667) >> 18     x <      43699
    	 * (x * 0x3334) >> 17     x <      16389
    	 * (x * 0x199a) >> 16     x <      16389
    	 * (x * 0x0ccd) >> 15     x <      16389
    	 * (x * 0x0667) >> 14     x <       2739
    	 * (x * 0x0334) >> 13     x <       1029
    	 * (x * 0x019a) >> 12     x <       1029
    	 * (x * 0x00cd) >> 11     x <       1029 shorter code than * 0x67 (on i386)
    	 * (x * 0x0067) >> 10     x <        179
    	 * (x * 0x0034) >>  9     x <         69 same
    	 * (x * 0x001a) >>  8     x <         69 same
    	 * (x * 0x000d) >>  7     x <         69 same, shortest code (on i386)
    	 * (x * 0x0007) >>  6     x <         19
    	 * See <http://www.cs.uiowa.edu/~jones/bcd/divide.html>
    	 */
    	r      = (q * (uint64_t)0x1999999a) >> 32;
    	*buf++ = (q - 10 * r) + '0'; /* 1 */
    	q      = (r * (uint64_t)0x1999999a) >> 32;
    	*buf++ = (r - 10 * q) + '0'; /* 2 */
    	r      = (q * (uint64_t)0x1999999a) >> 32;
    	*buf++ = (q - 10 * r) + '0'; /* 3 */
    	q      = (r * (uint64_t)0x1999999a) >> 32;
    	*buf++ = (r - 10 * q) + '0'; /* 4 */
    	r      = (q * (uint64_t)0x1999999a) >> 32;
    	*buf++ = (q - 10 * r) + '0'; /* 5 */
    	/* Now value is under 10000, can avoid 64-bit multiply */
    	q      = (r * 0x199a) >> 16;
    	*buf++ = (r - 10 * q)  + '0'; /* 6 */
    	r      = (q * 0xcd) >> 11;
    	*buf++ = (q - 10 * r)  + '0'; /* 7 */
    	q      = (r * 0xcd) >> 11;
    	*buf++ = (r - 10 * q) + '0'; /* 8 */
    	*buf++ = q + '0'; /* 9 */
    
    #endif
    
    /* Similar to above but do not pad with zeros.
     * Code can be easily arranged to print 9 digits too, but our callers
     * always call put_dec_full9() instead when the number has 9 decimal digits.
     */
    
    static noinline_for_stack
    
    char *put_dec_trunc8(char *buf, unsigned r)
    
    	unsigned q;
    
    	/* Copy of previous function's body with added early returns */
    
    	while (r >= 10000) {
    		q = r + '0';
    		r  = (r * (uint64_t)0x1999999a) >> 32;
    		*buf++ = q - 10*r;
    	}
    
    
    	q      = (r * 0x199a) >> 16;	/* r <= 9999 */
    	*buf++ = (r - 10 * q)  + '0';
    
    	if (q == 0)
    		return buf;
    
    	r      = (q * 0xcd) >> 11;	/* q <= 999 */
    	*buf++ = (q - 10 * r)  + '0';
    
    	if (r == 0)
    		return buf;
    
    	q      = (r * 0xcd) >> 11;	/* r <= 99 */
    	*buf++ = (r - 10 * q) + '0';
    
    	if (q == 0)
    		return buf;
    
    	*buf++ = q + '0';		 /* q <= 9 */
    
    /* There are two algorithms to print larger numbers.
     * One is generic: divide by 1000000000 and repeatedly print
     * groups of (up to) 9 digits. It's conceptually simple,
     * but requires a (unsigned long long) / 1000000000 division.
     *
     * Second algorithm splits 64-bit unsigned long long into 16-bit chunks,
     * manipulates them cleverly and generates groups of 4 decimal digits.
     * It so happens that it does NOT require long long division.
     *
     * If long is > 32 bits, division of 64-bit values is relatively easy,
     * and we will use the first algorithm.
     * If long long is > 64 bits (strange architecture with VERY large long long),
     * second algorithm can't be used, and we again use the first one.
     *
     * Else (if long is 32 bits and long long is 64 bits) we use second one.
     */
    
    #if BITS_PER_LONG != 32 || BITS_PER_LONG_LONG != 64
    
    /* First algorithm: generic */
    
    static
    char *put_dec(char *buf, unsigned long long n)
    {
    	if (n >= 100*1000*1000) {
    		while (n >= 1000*1000*1000)
    			buf = put_dec_full9(buf, do_div(n, 1000*1000*1000));
    		if (n >= 100*1000*1000)
    			return put_dec_full9(buf, n);
    	}
    	return put_dec_trunc8(buf, n);
    
    
    #else
    
    /* Second algorithm: valid only for 64-bit long longs */
    
    
    /* See comment in put_dec_full9 for choice of constants */
    
    static noinline_for_stack
    
    void put_dec_full4(char *buf, unsigned q)
    
    	buf[0] = (q - 10 * r) + '0';
    
    	buf[1] = (r - 10 * q)  + '0';
    
    	r      = (q * 0xcd) >> 11;
    
    	buf[2] = (q - 10 * r)  + '0';
    	buf[3] = r + '0';
    }
    
    /*
     * 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.
     * (d1 in the put_dec code, assuming n is all-ones).
     */
    static
    unsigned put_dec_helper4(char *buf, unsigned x)
    {
            uint32_t q = (x * (uint64_t)0x346DC5D7) >> 43;
    
            put_dec_full4(buf, 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.
     */
    static
    char *put_dec(char *buf, unsigned long long n)
    {
    	uint32_t d3, d2, d1, q, h;
    
    	if (n < 100*1000*1000)
    		return put_dec_trunc8(buf, n);
    
    	d1  = ((uint32_t)n >> 16); /* implicit "& 0xffff" */
    	h   = (n >> 32);
    	d2  = (h      ) & 0xffff;
    	d3  = (h >> 16); /* implicit "& 0xffff" */
    
    	q   = 656 * d3 + 7296 * d2 + 5536 * d1 + ((uint32_t)n & 0xffff);
    
    	q = put_dec_helper4(buf, q);
    
    	q += 7671 * d3 + 9496 * d2 + 6 * d1;
    	q = put_dec_helper4(buf+4, q);
    
    	q += 4749 * d3 + 42 * d2;
    	q = put_dec_helper4(buf+8, q);
    
    	q += 281 * d3;
    	buf += 12;
    	if (q)
    		buf = put_dec_trunc8(buf, q);
    	else while (buf[-1] == '0')
    
    /*
     * Convert passed number to decimal string.
     * Returns the length of string.  On buffer overflow, returns 0.
     *
     * If speed is not important, use snprintf(). It's easy to read the code.
     */
    int num_to_str(char *buf, int size, unsigned long long num)
    {
    
    	char tmp[sizeof(num) * 3];
    
    	/* put_dec() may work incorrectly for num = 0 (generate "", not "0") */
    	if (num <= 9) {
    		tmp[0] = '0' + num;
    		len = 1;
    	} else {
    		len = put_dec(tmp, num) - tmp;
    	}
    
    
    	if (len > size)
    		return 0;
    	for (idx = 0; idx < len; ++idx)
    		buf[idx] = tmp[len - idx - 1];
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    #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		/* use lowercase in hex (must be 32 == 0x20) */
    #define SPECIAL	64		/* prefix hex with "0x", octal with "0" */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    enum format_type {
    	FORMAT_TYPE_NONE, /* Just a string part */
    
    	FORMAT_TYPE_PRECISION,
    	FORMAT_TYPE_CHAR,
    	FORMAT_TYPE_STR,
    	FORMAT_TYPE_PTR,
    	FORMAT_TYPE_PERCENT_CHAR,
    	FORMAT_TYPE_INVALID,
    	FORMAT_TYPE_LONG_LONG,
    	FORMAT_TYPE_ULONG,
    	FORMAT_TYPE_LONG,
    
    	FORMAT_TYPE_UBYTE,
    	FORMAT_TYPE_BYTE,
    
    	FORMAT_TYPE_USHORT,
    	FORMAT_TYPE_SHORT,
    	FORMAT_TYPE_UINT,
    	FORMAT_TYPE_INT,
    	FORMAT_TYPE_NRCHARS,
    	FORMAT_TYPE_SIZE_T,
    	FORMAT_TYPE_PTRDIFF
    };
    
    struct printf_spec {
    
    	u8	type;		/* format_type enum */
    
    	u8	flags;		/* flags to number() */
    
    	u8	base;		/* number base, 8, 10 or 16 only */
    	u8	qualifier;	/* number qualifier, one of 'hHlLtzZ' */
    	s16	field_width;	/* width of output field */
    	s16	precision;	/* # of digits/chars */
    
    static noinline_for_stack
    char *number(char *buf, char *end, unsigned long long num,
    	     struct printf_spec spec)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    {
    
    	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
    	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
    
    	char tmp[66];
    	char sign;
    	char locase;
    
    	int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	int i;
    
    	bool is_zero = num == 0LL;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    
    
    	/* locase = 0 or 0x20. ORing digits or letters with 'locase'
    	 * produces same digits or (maybe lowercased) letters */
    
    	locase = (spec.flags & SMALL);
    	if (spec.flags & LEFT)
    		spec.flags &= ~ZEROPAD;
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	sign = 0;
    
    		if ((signed long long)num < 0) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			sign = '-';
    
    			num = -(signed long long)num;
    
    			spec.field_width--;
    		} else if (spec.flags & PLUS) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			sign = '+';
    
    			spec.field_width--;
    		} else if (spec.flags & SPACE) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			sign = ' ';
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    		}
    	}
    
    			spec.field_width -= 2;
    		else if (!is_zero)
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	}
    
    
    	/* generate full string in tmp[], in reverse order */
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	i = 0;
    
    	if (num < spec.base)
    		tmp[i++] = digits[num] | locase;
    
    	/* Generic code, for any base:
    	else do {
    
    		tmp[i++] = (digits[do_div(num,base)] | locase);
    
    	else if (spec.base != 10) { /* 8 or 16 */
    		int mask = spec.base - 1;
    
    			tmp[i++] = (digits[((unsigned char)num) & mask] | locase);
    
    			num >>= shift;
    		} while (num);
    
    	} else { /* base 10 */
    		i = put_dec(tmp, num) - tmp;
    	}
    
    
    	/* printing 100 using %2d gives "100", not "00" */
    
    	if (i > spec.precision)
    		spec.precision = i;
    
    	/* leading space padding */
    
    	spec.field_width -= spec.precision;
    	if (!(spec.flags & (ZEROPAD+LEFT))) {
    
    		while (--spec.field_width >= 0) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				*buf = ' ';
    			++buf;
    		}
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	if (sign) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			*buf = sign;
    		++buf;
    	}
    
    	/* "0x" / "0" prefix */
    	if (need_pfx) {
    
    		if (spec.base == 16 || !is_zero) {
    			if (buf < end)
    				*buf = '0';
    			++buf;
    		}
    
    				*buf = ('X' | locase);
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			++buf;
    		}
    	}
    
    	/* zero or space padding */
    
    	if (!(spec.flags & LEFT)) {
    		char c = (spec.flags & ZEROPAD) ? '0' : ' ';
    		while (--spec.field_width >= 0) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    				*buf = c;
    			++buf;
    		}
    	}
    
    	/* hmm even more zero padding? */
    
    	while (i <= --spec.precision) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			*buf = '0';
    		++buf;
    	}
    
    	/* actual digits of result */
    	while (--i >= 0) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			*buf = tmp[i];
    		++buf;
    	}
    
    	/* trailing space padding */
    
    	while (--spec.field_width >= 0) {
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    			*buf = ' ';
    		++buf;
    	}
    
    Linus Torvalds's avatar
    Linus Torvalds committed
    	return buf;
    }
    
    
    static noinline_for_stack
    char *string(char *buf, char *end, const char *s, struct printf_spec spec)
    
    {
    	int len, i;
    
    	if ((unsigned long)s < PAGE_SIZE)
    
    	len = strnlen(s, spec.precision);
    
    	if (!(spec.flags & LEFT)) {
    		while (len < spec.field_width--) {
    
    			if (buf < end)
    				*buf = ' ';
    			++buf;
    		}
    	}
    	for (i = 0; i < len; ++i) {
    		if (buf < end)
    			*buf = *s;
    		++buf; ++s;
    	}
    
    	while (len < spec.field_width--) {
    
    		if (buf < end)
    			*buf = ' ';
    		++buf;
    	}
    
    static noinline_for_stack
    char *symbol_string(char *buf, char *end, void *ptr,
    
    		    struct printf_spec spec, const char *fmt)
    
    #ifdef CONFIG_KALLSYMS
    	char sym[KSYM_SYMBOL_LEN];
    
    #endif
    
    	if (fmt[1] == 'R')
    		ptr = __builtin_extract_return_addr(ptr);
    	value = (unsigned long)ptr;
    
    #ifdef CONFIG_KALLSYMS
    	if (*fmt == 'B')
    
    		sprint_backtrace(sym, value);
    
    	else if (*fmt != 'f' && *fmt != 's')
    
    		sprint_symbol(sym, value);
    	else
    
    		sprint_symbol_no_offset(sym, value);
    
    	return string(buf, end, sym, spec);
    
    	spec.field_width = 2 * sizeof(void *);
    
    	spec.flags |= SPECIAL | SMALL | ZEROPAD;
    	spec.base = 16;
    
    	return number(buf, end, value, spec);
    
    static noinline_for_stack
    char *resource_string(char *buf, char *end, struct resource *res,
    		      struct printf_spec spec, const char *fmt)
    
    {
    #ifndef IO_RSRC_PRINTK_SIZE
    
    #define IO_RSRC_PRINTK_SIZE	6
    
    #endif
    
    #ifndef MEM_RSRC_PRINTK_SIZE
    
    #define MEM_RSRC_PRINTK_SIZE	10
    
    	static const struct printf_spec io_spec = {
    
    		.field_width = IO_RSRC_PRINTK_SIZE,
    
    		.precision = -1,
    		.flags = SPECIAL | SMALL | ZEROPAD,
    	};
    
    	static const struct printf_spec mem_spec = {
    		.base = 16,
    		.field_width = MEM_RSRC_PRINTK_SIZE,
    		.precision = -1,
    		.flags = SPECIAL | SMALL | ZEROPAD,
    	};
    
    	static const struct printf_spec bus_spec = {
    		.base = 16,
    		.field_width = 2,
    		.precision = -1,
    		.flags = SMALL | ZEROPAD,
    	};
    
    	static const struct printf_spec dec_spec = {
    
    		.base = 10,
    		.precision = -1,
    		.flags = 0,
    	};
    
    	static const struct printf_spec str_spec = {
    
    		.field_width = -1,
    		.precision = 10,
    		.flags = LEFT,
    	};
    
    	static const struct printf_spec flag_spec = {
    
    		.base = 16,
    		.precision = -1,
    		.flags = SPECIAL | SMALL,
    	};
    
    
    	/* 32-bit res (sizeof==4): 10 chars in dec, 10 in hex ("0x" + 8)
    	 * 64-bit res (sizeof==8): 20 chars in dec, 18 in hex ("0x" + 16) */
    #define RSRC_BUF_SIZE		((2 * sizeof(resource_size_t)) + 4)
    #define FLAG_BUF_SIZE		(2 * sizeof(res->flags))
    
    #define DECODED_BUF_SIZE	sizeof("[mem - 64bit pref window disabled]")
    
    #define RAW_BUF_SIZE		sizeof("[mem - flags 0x]")
    	char sym[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
    		     2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
    
    
    	char *p = sym, *pend = sym + sizeof(sym);
    
    	int decode = (fmt[0] == 'R') ? 1 : 0;
    
    	const struct printf_spec *specp;
    
    	if (res->flags & IORESOURCE_IO) {
    
    		p = string(p, pend, "io  ", str_spec);
    
    		specp = &io_spec;
    	} else if (res->flags & IORESOURCE_MEM) {
    
    		p = string(p, pend, "mem ", str_spec);
    
    		specp = &mem_spec;
    	} else if (res->flags & IORESOURCE_IRQ) {
    
    		p = string(p, pend, "irq ", str_spec);
    
    		specp = &dec_spec;
    	} else if (res->flags & IORESOURCE_DMA) {
    
    		p = string(p, pend, "dma ", str_spec);
    
    	} else if (res->flags & IORESOURCE_BUS) {
    		p = string(p, pend, "bus ", str_spec);
    		specp = &bus_spec;
    
    		p = string(p, pend, "??? ", str_spec);
    
    	p = number(p, pend, res->start, *specp);
    
    	if (res->start != res->end) {
    		*p++ = '-';
    
    		p = number(p, pend, res->end, *specp);
    
    		if (res->flags & IORESOURCE_MEM_64)
    			p = string(p, pend, " 64bit", str_spec);
    		if (res->flags & IORESOURCE_PREFETCH)
    			p = string(p, pend, " pref", str_spec);
    
    		if (res->flags & IORESOURCE_WINDOW)
    			p = string(p, pend, " window", str_spec);
    
    		if (res->flags & IORESOURCE_DISABLED)
    			p = string(p, pend, " disabled", str_spec);
    
    	} else {
    		p = string(p, pend, " flags ", str_spec);
    		p = number(p, pend, res->flags, flag_spec);
    
    	return string(buf, end, sym, spec);
    
    static noinline_for_stack
    char *hex_string(char *buf, char *end, u8 *addr, struct printf_spec spec,
    		 const char *fmt)
    {
    	int i, len = 1;		/* if we pass '%ph[CDN]', field witdh remains
    				   negative value, fallback to the default */
    	char separator;
    
    	if (spec.field_width == 0)
    		/* nothing to print */
    		return buf;
    
    	if (ZERO_OR_NULL_PTR(addr))
    		/* NULL pointer */
    		return string(buf, end, NULL, spec);
    
    	switch (fmt[1]) {
    	case 'C':
    		separator = ':';
    		break;
    	case 'D':
    		separator = '-';
    		break;
    	case 'N':
    		separator = 0;
    		break;
    	default:
    		separator = ' ';
    		break;
    	}
    
    	if (spec.field_width > 0)
    		len = min_t(int, spec.field_width, 64);
    
    	for (i = 0; i < len && buf < end - 1; i++) {
    		buf = hex_byte_pack(buf, addr[i]);
    
    		if (buf < end && separator && i != len - 1)
    			*buf++ = separator;
    	}
    
    	return buf;
    }
    
    
    static noinline_for_stack
    char *mac_address_string(char *buf, char *end, u8 *addr,
    			 struct printf_spec spec, const char *fmt)
    
    	char mac_addr[sizeof("xx:xx:xx:xx:xx:xx")];
    
    	char *p = mac_addr;
    	int i;
    
    	bool reversed = false;
    
    	switch (fmt[1]) {
    	case 'F':
    
    		break;
    
    	case 'R':
    		reversed = true;
    		/* fall through */
    
    	default:
    
    		if (reversed)
    			p = hex_byte_pack(p, addr[5 - i]);
    		else
    			p = hex_byte_pack(p, addr[i]);
    
    
    	return string(buf, end, mac_addr, spec);
    
    static noinline_for_stack
    char *ip4_string(char *p, const u8 *addr, const char *fmt)
    
    	bool leading_zeros = (fmt[0] == 'i');
    	int index;
    	int step;
    
    	switch (fmt[2]) {
    	case 'h':
    #ifdef __BIG_ENDIAN
    		index = 0;
    		step = 1;
    #else
    		index = 3;
    		step = -1;
    #endif
    		break;
    	case 'l':
    		index = 3;
    		step = -1;
    		break;
    	case 'n':
    	case 'b':
    	default:
    		index = 0;
    		step = 1;
    		break;
    	}
    
    	for (i = 0; i < 4; i++) {
    		char temp[3];	/* hold each IP quad in reverse order */
    
    		int digits = put_dec_trunc8(temp, addr[index]) - temp;
    
    		if (leading_zeros) {
    			if (digits < 3)
    				*p++ = '0';
    			if (digits < 2)
    				*p++ = '0';
    		}
    		/* reverse the digits in the quad */
    		while (digits--)
    			*p++ = temp[digits];
    		if (i < 3)
    			*p++ = '.';
    
    static noinline_for_stack
    char *ip6_compressed_string(char *p, const char *addr)
    
    	unsigned char zerolength[8];
    	int longest = 1;
    	int colonpos = -1;
    	u16 word;
    
    	bool useIPv4;
    	struct in6_addr in6;
    
    	memcpy(&in6, addr, sizeof(struct in6_addr));
    
    	useIPv4 = ipv6_addr_v4mapped(&in6) || ipv6_addr_is_isatap(&in6);
    
    
    	memset(zerolength, 0, sizeof(zerolength));
    
    	if (useIPv4)
    		range = 6;
    	else
    		range = 8;
    
    	/* find position of longest 0 run */
    	for (i = 0; i < range; i++) {
    		for (j = i; j < range; j++) {
    
    			if (in6.s6_addr16[j] != 0)
    
    				break;
    			zerolength[i]++;
    		}
    	}
    	for (i = 0; i < range; i++) {
    		if (zerolength[i] > longest) {
    			longest = zerolength[i];
    			colonpos = i;
    		}
    	}
    
    	if (longest == 1)		/* don't compress a single 0 */
    		colonpos = -1;
    
    	/* emit address */
    	for (i = 0; i < range; i++) {
    		if (i == colonpos) {
    			if (needcolon || i == 0)
    				*p++ = ':';
    			*p++ = ':';
    			needcolon = false;
    			i += longest - 1;
    			continue;
    		}
    		if (needcolon) {
    			*p++ = ':';
    			needcolon = false;
    		}
    		/* hex u16 without leading 0s */
    
    		word = ntohs(in6.s6_addr16[i]);
    
    		hi = word >> 8;
    		lo = word & 0xff;
    		if (hi) {
    			if (hi > 0x0f)
    
    				p = hex_byte_pack(p, hi);
    
    			p = hex_byte_pack(p, lo);
    
    			p = hex_byte_pack(p, lo);
    
    		else
    			*p++ = hex_asc_lo(lo);
    		needcolon = true;
    	}
    
    	if (useIPv4) {
    		if (needcolon)
    			*p++ = ':';
    
    		p = ip4_string(p, &in6.s6_addr[12], "I4");
    
    static noinline_for_stack
    char *ip6_string(char *p, const char *addr, const char *fmt)
    
    	for (i = 0; i < 8; i++) {
    
    		p = hex_byte_pack(p, *addr++);
    		p = hex_byte_pack(p, *addr++);
    
    static noinline_for_stack
    char *ip6_addr_string(char *buf, char *end, const u8 *addr,
    		      struct printf_spec spec, const char *fmt)
    
    {
    	char ip6_addr[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")];
    
    	if (fmt[0] == 'I' && fmt[2] == 'c')
    
    		ip6_compressed_string(ip6_addr, addr);
    
    		ip6_string(ip6_addr, addr, fmt);
    
    	return string(buf, end, ip6_addr, spec);
    
    static noinline_for_stack
    char *ip4_addr_string(char *buf, char *end, const u8 *addr,
    		      struct printf_spec spec, const char *fmt)
    
    	char ip4_addr[sizeof("255.255.255.255")];
    
    	return string(buf, end, ip4_addr, spec);
    
    static noinline_for_stack
    char *uuid_string(char *buf, char *end, const u8 *addr,
    		  struct printf_spec spec, const char *fmt)
    
    {
    	char uuid[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")];
    	char *p = uuid;
    	int i;
    	static const u8 be[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    	static const u8 le[16] = {3,2,1,0,5,4,7,6,8,9,10,11,12,13,14,15};
    	const u8 *index = be;
    	bool uc = false;
    
    	switch (*(++fmt)) {
    	case 'L':
    		uc = true;		/* fall-through */
    	case 'l':
    		index = le;
    		break;
    	case 'B':
    		uc = true;
    		break;
    	}
    
    	for (i = 0; i < 16; i++) {
    
    		p = hex_byte_pack(p, addr[index[i]]);
    
    		switch (i) {
    		case 3:
    		case 5:
    		case 7:
    		case 9:
    			*p++ = '-';
    			break;
    		}
    	}
    
    	*p = 0;
    
    	if (uc) {
    		p = uuid;
    		do {
    			*p = toupper(*p);
    		} while (*(++p));
    	}
    
    	return string(buf, end, uuid, spec);
    }
    
    
    static
    char *netdev_feature_string(char *buf, char *end, const u8 *addr,
    		      struct printf_spec spec)
    {
    	spec.flags |= SPECIAL | SMALL | ZEROPAD;
    	if (spec.field_width == -1)
    		spec.field_width = 2 + 2 * sizeof(netdev_features_t);
    	spec.base = 16;
    
    	return number(buf, end, *(const netdev_features_t *)addr, spec);
    }
    
    
    int kptr_restrict __read_mostly;
    
    /*
     * Show a '%p' thing.  A kernel extension is that the '%p' is followed
     * by an extra set of alphanumeric characters that are extended format
     * specifiers.
     *
    
     * Right now we handle:
     *
    
     * - 'F' For symbolic function descriptor pointers with offset
     * - 'f' For simple symbolic function names without offset
    
     * - 'S' For symbolic direct pointers with offset
     * - 's' For symbolic direct pointers without offset
    
     * - '[FfSs]R' as above with __builtin_extract_return_addr() translation
    
     * - 'B' For backtraced symbolic direct pointers with offset
    
     * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref]
     * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201]