diff options
| author | Wolfgang Denk <wd@denx.de> | 2011-12-23 20:53:58 +0100 | 
|---|---|---|
| committer | Wolfgang Denk <wd@denx.de> | 2011-12-23 20:53:58 +0100 | 
| commit | bfcc40bb09b05c90cc3b1496abb270eb8aa72134 (patch) | |
| tree | c551fbe329411533b80f0298f6f0ff5afb48cee7 /lib/vsprintf.c | |
| parent | cba9a894fdb1cb49b60fcd1d1d6919cbd7995dd5 (diff) | |
| parent | 1895420b2ef7358014b355aa4f4f2c348267a6d9 (diff) | |
| download | olio-uboot-2014.01-bfcc40bb09b05c90cc3b1496abb270eb8aa72134.tar.xz olio-uboot-2014.01-bfcc40bb09b05c90cc3b1496abb270eb8aa72134.zip | |
Merge branch 'next' of ../next
* 'next' of ../next:
  mkenvimage: Add version info switch (-V)
  mkenvimage: Fix getopt() error handling
  mkenvimage: Fix some typos
  phy: add Micrel KS8721BL phy definition
  net: introduce per device index
  mvgbe: remove setting of ethaddr within the driver
  x86: Add support for specifying an initrd with the zboot command
  x86: Refactor the zboot innards so they can be reused with a vboot image
  x86: Add infrastructure to extract an e820 table from the coreboot tables
  x86: Add support for booting Linux using the 32 bit boot protocol
  x86: Clean up the x86 zimage code in preparation to extend it
  x86: Import code from coreboot's libpayload to parse the coreboot table
  x86: Initial commit for running as a coreboot payload
  CHECKPATCH: ./board/esd/hh405/logo_320_240_8bpp.c
  CHECKPATCH: ./board/esd/hh405/logo_1024_768_8bpp.c
  CHECKPATCH: ./board/esd/hh405/logo_320_240_4bpp.c
  CHECKPATCH: ./board/esd/hh405/logo_640_480_24bpp.c
  CHECKPATCH: ./board/esd/apc405/logo_640_480_24bpp.c
  CHECKPATCH: ./board/esd/voh405/logo_320_240_4bpp.c
  CHECKPATCH: ./board/esd/voh405/logo_640_480_24bpp.c
  CHECKPATCH: ./board/esd/hh405/fpgadata.c
  CHECKPATCH: ./board/esd/pci405/fpgadata.c
  CHECKPATCH: ./board/esd/tasreg/fpgadata.c
  CHECKPATCH: ./board/esd/apc405/fpgadata.c
  CHECKPATCH: ./board/esd/voh405/fpgadata.c
  CHECKPATCH: ./board/esd/ash405/fpgadata.c
  CHECKPATCH: ./board/esd/dasa_sim/fpgadata.c
  CHECKPATCH: ./board/esd/ar405/fpgadata_xl30.c
  CHECKPATCH: ./board/esd/ar405/fpgadata.c
  CHECKPATCH: ./board/esd/plu405/fpgadata.c
  CHECKPATCH: ./board/esd/wuh405/fpgadata.c
  CHECKPATCH: ./board/esd/cpci405/fpgadata_cpci405.c
  CHECKPATCH: ./board/esd/cpci405/fpgadata_cpci405ab.c
  CHECKPATCH: ./board/esd/cpci405/fpgadata_cpci4052.c
  CHECKPATCH: ./board/esd/canbt/fpgadata.c
  CHECKPATCH: ./board/esd/du405/fpgadata.c
  CHECKPATCH: ./board/esd/cpciiser4/fpgadata.c
  CHECKPATCH: ./board/dave/PPChameleonEVB/fpgadata.c
  avr32:mmu.c: fix printf() length modifier
  fat.c: fix printf() length modifier
  cmd_sf.c: fix printf() length modifier
  Make printf and vprintf safe from buffer overruns
  vsprintf: Move function documentation into header file
  Add safe vsnprintf and snprintf library functions
  Move vsprintf functions into their own header
Conflicts:
	tools/mkenvimage.c
Signed-off-by: Wolfgang Denk <wd@denx.de>
Diffstat (limited to 'lib/vsprintf.c')
| -rw-r--r-- | lib/vsprintf.c | 237 | 
1 files changed, 149 insertions, 88 deletions
| diff --git a/lib/vsprintf.c b/lib/vsprintf.c index e497a8686..e38a4b70f 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -26,6 +26,9 @@  # define NUM_TYPE long long  #define noinline __attribute__((noinline)) +/* some reluctance to put this into a new limits.h, so it is here */ +#define INT_MAX		((int)(~0U>>1)) +  const char hex_asc[] = "0123456789abcdef";  #define hex_asc_lo(x)   hex_asc[((x) & 0x0f)]  #define hex_asc_hi(x)   hex_asc[((x) & 0xf0) >> 4] @@ -64,32 +67,6 @@ unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)  	return result;  } -/** - * strict_strtoul - convert a string to an unsigned long strictly - * @cp: The string to be converted - * @base: The number base to use - * @res: The converted result value - * - * strict_strtoul converts a string to an unsigned long only if the - * string is really an unsigned long string, any string containing - * any invalid char at the tail will be rejected and -EINVAL is returned, - * only a newline char at the tail is acceptible because people generally - * change a module parameter in the following way: - * - *      echo 1024 > /sys/module/e1000/parameters/copybreak - * - * echo will append a newline to the tail. - * - * It returns 0 if conversion is successful and *res is set to the converted - * value, otherwise it returns -EINVAL and *res is set to 0. - * - * simple_strtoul just ignores the successive invalid characters and - * return the converted value of prefix part of the string. - * - * Copied this function from Linux 2.6.38 commit ID: - * 521cb40b0c44418a4fd36dc633f575813d59a43d - * - */  int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)  {  	char *tail; @@ -291,7 +268,22 @@ static noinline char* put_dec(char *buf, unsigned NUM_TYPE num)  #define SMALL	32		/* Must be 32 == 0x20 */  #define SPECIAL	64		/* 0x */ -static char *number(char *buf, unsigned NUM_TYPE num, int base, int size, int precision, int type) +#ifdef CONFIG_SYS_VSNPRINTF +/* + * Macro to add a new character to our output string, but only if it will + * fit. The macro moves to the next character position in the output string. + */ +#define ADDCH(str, ch) do { \ +	if ((str) < end) \ +		*(str) = (ch); \ +	++str; \ +	} while (0) +#else +#define ADDCH(str, ch)	(*(str)++ = (ch)) +#endif + +static char *number(char *buf, char *end, unsigned NUM_TYPE num, +		int base, int size, int precision, int type)  {  	/* we are called with base 8, 10 or 16, only, thus don't need "G..."  */  	static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ @@ -353,37 +345,40 @@ static char *number(char *buf, unsigned NUM_TYPE num, int base, int size, int pr  		precision = i;  	/* leading space padding */  	size -= precision; -	if (!(type & (ZEROPAD+LEFT))) -		while(--size >= 0) -			*buf++ = ' '; +	if (!(type & (ZEROPAD + LEFT))) { +		while (--size >= 0) +			ADDCH(buf, ' '); +	}  	/* sign */  	if (sign) -		*buf++ = sign; +		ADDCH(buf, sign);  	/* "0x" / "0" prefix */  	if (need_pfx) { -		*buf++ = '0'; +		ADDCH(buf, '0');  		if (base == 16) -			*buf++ = ('X' | locase); +			ADDCH(buf, 'X' | locase);  	}  	/* zero or space padding */  	if (!(type & LEFT)) {  		char c = (type & ZEROPAD) ? '0' : ' '; +  		while (--size >= 0) -			*buf++ = c; +			ADDCH(buf, c);  	}  	/* hmm even more zero padding? */  	while (i <= --precision) -		*buf++ = '0'; +		ADDCH(buf, '0');  	/* actual digits of result */  	while (--i >= 0) -		*buf++ = tmp[i]; +		ADDCH(buf, tmp[i]);  	/* trailing space padding */  	while (--size >= 0) -		*buf++ = ' '; +		ADDCH(buf, ' ');  	return buf;  } -static char *string(char *buf, char *s, int field_width, int precision, int flags) +static char *string(char *buf, char *end, char *s, int field_width, +		int precision, int flags)  {  	int len, i; @@ -394,16 +389,16 @@ static char *string(char *buf, char *s, int field_width, int precision, int flag  	if (!(flags & LEFT))  		while (len < field_width--) -			*buf++ = ' '; +			ADDCH(buf, ' ');  	for (i = 0; i < len; ++i) -		*buf++ = *s++; +		ADDCH(buf, *s++);  	while (len < field_width--) -		*buf++ = ' '; +		ADDCH(buf, ' ');  	return buf;  }  #ifdef CONFIG_CMD_NET -static char *mac_address_string(char *buf, u8 *addr, int field_width, +static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width,  				int precision, int flags)  {  	char mac_addr[6 * 3]; /* (6 * 2 hex digits), 5 colons and trailing zero */ @@ -417,10 +412,11 @@ static char *mac_address_string(char *buf, u8 *addr, int field_width,  	}  	*p = '\0'; -	return string(buf, mac_addr, field_width, precision, flags & ~SPECIAL); +	return string(buf, end, mac_addr, field_width, precision, +		      flags & ~SPECIAL);  } -static char *ip6_addr_string(char *buf, u8 *addr, int field_width, +static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width,  			 int precision, int flags)  {  	char ip6_addr[8 * 5]; /* (8 * 4 hex digits), 7 colons and trailing zero */ @@ -435,10 +431,11 @@ static char *ip6_addr_string(char *buf, u8 *addr, int field_width,  	}  	*p = '\0'; -	return string(buf, ip6_addr, field_width, precision, flags & ~SPECIAL); +	return string(buf, end, ip6_addr, field_width, precision, +		      flags & ~SPECIAL);  } -static char *ip4_addr_string(char *buf, u8 *addr, int field_width, +static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width,  			 int precision, int flags)  {  	char ip4_addr[4 * 4]; /* (4 * 3 decimal digits), 3 dots and trailing zero */ @@ -456,7 +453,8 @@ static char *ip4_addr_string(char *buf, u8 *addr, int field_width,  	}  	*p = '\0'; -	return string(buf, ip4_addr, field_width, precision, flags & ~SPECIAL); +	return string(buf, end, ip4_addr, field_width, precision, +		      flags & ~SPECIAL);  }  #endif @@ -478,10 +476,12 @@ static char *ip4_addr_string(char *buf, u8 *addr, int field_width,   * function pointers are really function descriptors, which contain a   * pointer to the real address.   */ -static char *pointer(const char *fmt, char *buf, void *ptr, int field_width, int precision, int flags) +static char *pointer(const char *fmt, char *buf, char *end, void *ptr, +		int field_width, int precision, int flags)  {  	if (!ptr) -		return string(buf, "(null)", field_width, precision, flags); +		return string(buf, end, "(null)", field_width, precision, +			      flags);  #ifdef CONFIG_CMD_NET  	switch (*fmt) { @@ -489,15 +489,18 @@ static char *pointer(const char *fmt, char *buf, void *ptr, int field_width, int  		flags |= SPECIAL;  		/* Fallthrough */  	case 'M': -		return mac_address_string(buf, ptr, field_width, precision, flags); +		return mac_address_string(buf, end, ptr, field_width, +					  precision, flags);  	case 'i':  		flags |= SPECIAL;  		/* Fallthrough */  	case 'I':  		if (fmt[1] == '6') -			return ip6_addr_string(buf, ptr, field_width, precision, flags); +			return ip6_addr_string(buf, end, ptr, field_width, +					       precision, flags);  		if (fmt[1] == '4') -			return ip4_addr_string(buf, ptr, field_width, precision, flags); +			return ip4_addr_string(buf, end, ptr, field_width, +					       precision, flags);  		flags &= ~SPECIAL;  		break;  	} @@ -507,27 +510,12 @@ static char *pointer(const char *fmt, char *buf, void *ptr, int field_width, int  		field_width = 2*sizeof(void *);  		flags |= ZEROPAD;  	} -	return number(buf, (unsigned long) ptr, 16, field_width, precision, flags); +	return number(buf, end, (unsigned long)ptr, 16, field_width, +		      precision, flags);  } -/** - * vsprintf - Format a string and place it in a buffer - * @buf: The buffer to place the result into - * @fmt: The format string to use - * @args: Arguments for the format string - * - * This function follows C99 vsprintf, but has some extensions: - * %pS output the name of a text symbol - * %pF output the name of a function pointer - * %pR output the address range in a struct resource - * - * The function returns the number of characters written - * into @buf. - * - * Call this function if you are already dealing with a va_list. - * You probably want sprintf() instead. - */ -int vsprintf(char *buf, const char *fmt, va_list args) +static int vsnprintf_internal(char *buf, size_t size, const char *fmt, +			      va_list args)  {  	unsigned NUM_TYPE num;  	int base; @@ -542,12 +530,20 @@ int vsprintf(char *buf, const char *fmt, va_list args)  				/* 'z' support added 23/7/1999 S.H.    */  				/* 'z' changed to 'Z' --davidm 1/25/99 */  				/* 't' added for ptrdiff_t */ +	char *end = buf + size; +#ifdef CONFIG_SYS_VSNPRINTF +	/* Make sure end is always >= buf - do we want this in U-Boot? */ +	if (end < buf) { +		end = ((void *)-1); +		size = end - buf; +	} +#endif  	str = buf;  	for (; *fmt ; ++fmt) {  		if (*fmt != '%') { -			*str++ = *fmt; +			ADDCH(str, *fmt);  			continue;  		} @@ -609,20 +605,22 @@ int vsprintf(char *buf, const char *fmt, va_list args)  		switch (*fmt) {  		case 'c': -			if (!(flags & LEFT)) +			if (!(flags & LEFT)) {  				while (--field_width > 0) -					*str++ = ' '; -			*str++ = (unsigned char) va_arg(args, int); +					ADDCH(str, ' '); +			} +			ADDCH(str, (unsigned char) va_arg(args, int));  			while (--field_width > 0) -				*str++ = ' '; +				ADDCH(str, ' ');  			continue;  		case 's': -			str = string(str, va_arg(args, char *), field_width, precision, flags); +			str = string(str, end, va_arg(args, char *), +				     field_width, precision, flags);  			continue;  		case 'p': -			str = pointer(fmt+1, str, +			str = pointer(fmt+1, str, end,  					va_arg(args, void *),  					field_width, precision, flags);  			/* Skip all alphanumeric pointer suffixes */ @@ -641,7 +639,7 @@ int vsprintf(char *buf, const char *fmt, va_list args)  			continue;  		case '%': -			*str++ = '%'; +			ADDCH(str, '%');  			continue;  		/* integer number formats - set up the flags and "break" */ @@ -662,9 +660,9 @@ int vsprintf(char *buf, const char *fmt, va_list args)  			break;  		default: -			*str++ = '%'; +			ADDCH(str, '%');  			if (*fmt) -				*str++ = *fmt; +				ADDCH(str, *fmt);  			else  				--fmt;  			continue; @@ -688,23 +686,86 @@ int vsprintf(char *buf, const char *fmt, va_list args)  			if (flags & SIGN)  				num = (signed int) num;  		} -		str = number(str, num, base, field_width, precision, flags); +		str = number(str, end, num, base, field_width, precision, +			     flags); +	} + +#ifdef CONFIG_SYS_VSNPRINTF +	if (size > 0) { +		ADDCH(str, '\0'); +		if (str > end) +			end[-1] = '\0';  	} +#else  	*str = '\0'; +#endif +	/* the trailing null byte doesn't count towards the total */  	return str-buf;  } +#ifdef CONFIG_SYS_VSNPRINTF +int vsnprintf(char *buf, size_t size, const char *fmt, +			      va_list args) +{ +	return vsnprintf_internal(buf, size, fmt, args); +} + +int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) +{ +	int i; + +	i = vsnprintf(buf, size, fmt, args); + +	if (likely(i < size)) +		return i; +	if (size != 0) +		return size - 1; +	return 0; +} + +int snprintf(char *buf, size_t size, const char *fmt, ...) +{ +	va_list args; +	int i; + +	va_start(args, fmt); +	i = vsnprintf(buf, size, fmt, args); +	va_end(args); + +	return i; +} + +int scnprintf(char *buf, size_t size, const char *fmt, ...) +{ +	va_list args; +	int i; + +	va_start(args, fmt); +	i = vscnprintf(buf, size, fmt, args); +	va_end(args); + +	return i; +} +#endif /* CONFIG_SYS_VSNPRINT */ +  /** - * sprintf - Format a string and place it in a buffer - * @buf: The buffer to place the result into - * @fmt: The format string to use - * @...: Arguments for the format string + * Format a string and place it in a buffer (va_list version) + * + * @param buf	The buffer to place the result into + * @param fmt	The format string to use + * @param args	Arguments for the format string   *   * The function returns the number of characters written - * into @buf. + * into @buf. Use vsnprintf() or vscnprintf() in order to avoid + * buffer overflows.   * - * See the vsprintf() documentation for format string extensions over C99. + * If you're not already dealing with a va_list consider using sprintf().   */ +int vsprintf(char *buf, const char *fmt, va_list args) +{ +	return vsnprintf_internal(buf, INT_MAX, fmt, args); +} +  int sprintf(char * buf, const char *fmt, ...)  {  	va_list args; |