mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-24 19:08:50 +09:00
![Mike Frysinger](/assets/img/avatar_default.png)
This brings in support for the %p modifier which allows us to easily print out things like ip addresses, mac addresses, and pointers. It also converts the rarely used 'q' length modifier to the common 'L' modifier when dealing with quad types. While this new code is a bit larger (~1k .text), most of it should be made up by converting the existing ip/mac address code to use format modifiers. Signed-off-by: Mike Frysinger <vapier@gentoo.org>
695 lines
16 KiB
C
695 lines
16 KiB
C
/*
|
|
* linux/lib/vsprintf.c
|
|
*
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
*/
|
|
|
|
/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
|
|
/*
|
|
* Wirzenius wrote this portably, Torvalds fucked it up :-)
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <linux/types.h>
|
|
#include <linux/string.h>
|
|
#include <linux/ctype.h>
|
|
|
|
#include <common.h>
|
|
#if !defined (CONFIG_PANIC_HANG)
|
|
#include <command.h>
|
|
/*cmd_boot.c*/
|
|
extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
|
|
#endif
|
|
|
|
#ifdef CONFIG_SYS_64BIT_VSPRINTF
|
|
# define NUM_TYPE long long
|
|
#else
|
|
# define NUM_TYPE long
|
|
#endif
|
|
#define noinline __attribute__((noinline))
|
|
|
|
#define do_div(n, base) ({ \
|
|
unsigned int __res; \
|
|
__res = ((unsigned NUM_TYPE) n) % base; \
|
|
n = ((unsigned NUM_TYPE) n) / base; \
|
|
__res; \
|
|
})
|
|
|
|
const char hex_asc[] = "0123456789abcdef";
|
|
#define hex_asc_lo(x) hex_asc[((x) & 0x0f)]
|
|
#define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4]
|
|
|
|
static inline char *pack_hex_byte(char *buf, u8 byte)
|
|
{
|
|
*buf++ = hex_asc_hi(byte);
|
|
*buf++ = hex_asc_lo(byte);
|
|
return buf;
|
|
}
|
|
|
|
unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
|
|
{
|
|
unsigned long result = 0,value;
|
|
|
|
if (*cp == '0') {
|
|
cp++;
|
|
if ((*cp == 'x') && isxdigit(cp[1])) {
|
|
base = 16;
|
|
cp++;
|
|
}
|
|
if (!base) {
|
|
base = 8;
|
|
}
|
|
}
|
|
if (!base) {
|
|
base = 10;
|
|
}
|
|
while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
|
|
? toupper(*cp) : *cp)-'A'+10) < base) {
|
|
result = result*base + value;
|
|
cp++;
|
|
}
|
|
if (endp)
|
|
*endp = (char *)cp;
|
|
return result;
|
|
}
|
|
|
|
long simple_strtol(const char *cp,char **endp,unsigned int base)
|
|
{
|
|
if(*cp=='-')
|
|
return -simple_strtoul(cp+1,endp,base);
|
|
return simple_strtoul(cp,endp,base);
|
|
}
|
|
|
|
int ustrtoul(const char *cp, char **endp, unsigned int base)
|
|
{
|
|
unsigned long result = simple_strtoul(cp, endp, base);
|
|
switch (**endp) {
|
|
case 'G' :
|
|
result *= 1024;
|
|
/* fall through */
|
|
case 'M':
|
|
result *= 1024;
|
|
/* fall through */
|
|
case 'K':
|
|
case 'k':
|
|
result *= 1024;
|
|
if ((*endp)[1] == 'i') {
|
|
if ((*endp)[2] == 'B')
|
|
(*endp) += 3;
|
|
else
|
|
(*endp) += 2;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#ifdef CONFIG_SYS_64BIT_STRTOUL
|
|
unsigned long long simple_strtoull (const char *cp, char **endp, unsigned int base)
|
|
{
|
|
unsigned long long result = 0, value;
|
|
|
|
if (*cp == '0') {
|
|
cp++;
|
|
if ((*cp == 'x') && isxdigit (cp[1])) {
|
|
base = 16;
|
|
cp++;
|
|
}
|
|
if (!base) {
|
|
base = 8;
|
|
}
|
|
}
|
|
if (!base) {
|
|
base = 10;
|
|
}
|
|
while (isxdigit (*cp) && (value = isdigit (*cp)
|
|
? *cp - '0'
|
|
: (islower (*cp) ? toupper (*cp) : *cp) - 'A' + 10) < base) {
|
|
result = result * base + value;
|
|
cp++;
|
|
}
|
|
if (endp)
|
|
*endp = (char *) cp;
|
|
return result;
|
|
}
|
|
#endif /* CONFIG_SYS_64BIT_STRTOUL */
|
|
|
|
/* we use this so that we can do without the ctype library */
|
|
#define is_digit(c) ((c) >= '0' && (c) <= '9')
|
|
|
|
static int skip_atoi(const char **s)
|
|
{
|
|
int i=0;
|
|
|
|
while (is_digit(**s))
|
|
i = i*10 + *((*s)++) - '0';
|
|
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 code from
|
|
* http://www.cs.uiowa.edu/~jones/bcd/decimal.html
|
|
* (with permission from the author, Douglas W. Jones). */
|
|
|
|
/* Formats correctly any integer in [0,99999].
|
|
* Outputs from one to five digits depending on input.
|
|
* On i386 gcc 4.1.2 -O2: ~250 bytes of code. */
|
|
static char* put_dec_trunc(char *buf, unsigned q)
|
|
{
|
|
unsigned d3, d2, d1, d0;
|
|
d1 = (q>>4) & 0xf;
|
|
d2 = (q>>8) & 0xf;
|
|
d3 = (q>>12);
|
|
|
|
d0 = 6*(d3 + d2 + d1) + (q & 0xf);
|
|
q = (d0 * 0xcd) >> 11;
|
|
d0 = d0 - 10*q;
|
|
*buf++ = d0 + '0'; /* least significant digit */
|
|
d1 = q + 9*d3 + 5*d2 + d1;
|
|
if (d1 != 0) {
|
|
q = (d1 * 0xcd) >> 11;
|
|
d1 = d1 - 10*q;
|
|
*buf++ = d1 + '0'; /* next digit */
|
|
|
|
d2 = q + 2*d2;
|
|
if ((d2 != 0) || (d3 != 0)) {
|
|
q = (d2 * 0xd) >> 7;
|
|
d2 = d2 - 10*q;
|
|
*buf++ = d2 + '0'; /* next digit */
|
|
|
|
d3 = q + 4*d3;
|
|
if (d3 != 0) {
|
|
q = (d3 * 0xcd) >> 11;
|
|
d3 = d3 - 10*q;
|
|
*buf++ = d3 + '0'; /* next digit */
|
|
if (q != 0)
|
|
*buf++ = q + '0'; /* most sign. digit */
|
|
}
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
/* Same with if's removed. Always emits five digits */
|
|
static char* put_dec_full(char *buf, unsigned q)
|
|
{
|
|
/* BTW, if q is in [0,9999], 8-bit ints will be enough, */
|
|
/* but anyway, gcc produces better code with full-sized ints */
|
|
unsigned d3, d2, d1, d0;
|
|
d1 = (q>>4) & 0xf;
|
|
d2 = (q>>8) & 0xf;
|
|
d3 = (q>>12);
|
|
|
|
/* Possible ways to approx. divide by 10 */
|
|
/* gcc -O2 replaces multiply with shifts and adds */
|
|
// (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386)
|
|
// (x * 0x67) >> 10: 1100111
|
|
// (x * 0x34) >> 9: 110100 - same
|
|
// (x * 0x1a) >> 8: 11010 - same
|
|
// (x * 0x0d) >> 7: 1101 - same, shortest code (on i386)
|
|
|
|
d0 = 6*(d3 + d2 + d1) + (q & 0xf);
|
|
q = (d0 * 0xcd) >> 11;
|
|
d0 = d0 - 10*q;
|
|
*buf++ = d0 + '0';
|
|
d1 = q + 9*d3 + 5*d2 + d1;
|
|
q = (d1 * 0xcd) >> 11;
|
|
d1 = d1 - 10*q;
|
|
*buf++ = d1 + '0';
|
|
|
|
d2 = q + 2*d2;
|
|
q = (d2 * 0xd) >> 7;
|
|
d2 = d2 - 10*q;
|
|
*buf++ = d2 + '0';
|
|
|
|
d3 = q + 4*d3;
|
|
q = (d3 * 0xcd) >> 11; /* - shorter code */
|
|
/* q = (d3 * 0x67) >> 10; - would also work */
|
|
d3 = d3 - 10*q;
|
|
*buf++ = d3 + '0';
|
|
*buf++ = q + '0';
|
|
return buf;
|
|
}
|
|
/* No inlining helps gcc to use registers better */
|
|
static noinline char* put_dec(char *buf, unsigned NUM_TYPE num)
|
|
{
|
|
while (1) {
|
|
unsigned rem;
|
|
if (num < 100000)
|
|
return put_dec_trunc(buf, num);
|
|
rem = do_div(num, 100000);
|
|
buf = put_dec_full(buf, rem);
|
|
}
|
|
}
|
|
|
|
#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 */
|
|
|
|
static char *number(char *buf, 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"; */
|
|
|
|
char tmp[66];
|
|
char sign;
|
|
char locase;
|
|
int need_pfx = ((type & SPECIAL) && base != 10);
|
|
int i;
|
|
|
|
/* locase = 0 or 0x20. ORing digits or letters with 'locase'
|
|
* produces same digits or (maybe lowercased) letters */
|
|
locase = (type & SMALL);
|
|
if (type & LEFT)
|
|
type &= ~ZEROPAD;
|
|
sign = 0;
|
|
if (type & SIGN) {
|
|
if ((signed NUM_TYPE) num < 0) {
|
|
sign = '-';
|
|
num = - (signed NUM_TYPE) num;
|
|
size--;
|
|
} else if (type & PLUS) {
|
|
sign = '+';
|
|
size--;
|
|
} else if (type & SPACE) {
|
|
sign = ' ';
|
|
size--;
|
|
}
|
|
}
|
|
if (need_pfx) {
|
|
size--;
|
|
if (base == 16)
|
|
size--;
|
|
}
|
|
|
|
/* generate full string in tmp[], in reverse order */
|
|
i = 0;
|
|
if (num == 0)
|
|
tmp[i++] = '0';
|
|
/* Generic code, for any base:
|
|
else do {
|
|
tmp[i++] = (digits[do_div(num,base)] | locase);
|
|
} while (num != 0);
|
|
*/
|
|
else if (base != 10) { /* 8 or 16 */
|
|
int mask = base - 1;
|
|
int shift = 3;
|
|
if (base == 16) shift = 4;
|
|
do {
|
|
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 > precision)
|
|
precision = i;
|
|
/* leading space padding */
|
|
size -= precision;
|
|
if (!(type & (ZEROPAD+LEFT)))
|
|
while(--size >= 0)
|
|
*buf++ = ' ';
|
|
/* sign */
|
|
if (sign)
|
|
*buf++ = sign;
|
|
/* "0x" / "0" prefix */
|
|
if (need_pfx) {
|
|
*buf++ = '0';
|
|
if (base == 16)
|
|
*buf++ = ('X' | locase);
|
|
}
|
|
/* zero or space padding */
|
|
if (!(type & LEFT)) {
|
|
char c = (type & ZEROPAD) ? '0' : ' ';
|
|
while (--size >= 0)
|
|
*buf++ = c;
|
|
}
|
|
/* hmm even more zero padding? */
|
|
while (i <= --precision)
|
|
*buf++ = '0';
|
|
/* actual digits of result */
|
|
while (--i >= 0)
|
|
*buf++ = tmp[i];
|
|
/* trailing space padding */
|
|
while (--size >= 0)
|
|
*buf++ = ' ';
|
|
return buf;
|
|
}
|
|
|
|
static char *string(char *buf, char *s, int field_width, int precision, int flags)
|
|
{
|
|
int len, i;
|
|
|
|
if (s == 0)
|
|
s = "<NULL>";
|
|
|
|
len = strnlen(s, precision);
|
|
|
|
if (!(flags & LEFT))
|
|
while (len < field_width--)
|
|
*buf++ = ' ';
|
|
for (i = 0; i < len; ++i)
|
|
*buf++ = *s++;
|
|
while (len < field_width--)
|
|
*buf++ = ' ';
|
|
return buf;
|
|
}
|
|
|
|
#ifdef CONFIG_CMD_NET
|
|
static char *mac_address_string(char *buf, u8 *addr, int field_width,
|
|
int precision, int flags)
|
|
{
|
|
char mac_addr[6 * 3]; /* (6 * 2 hex digits), 5 colons and trailing zero */
|
|
char *p = mac_addr;
|
|
int i;
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
p = pack_hex_byte(p, addr[i]);
|
|
if (!(flags & SPECIAL) && i != 5)
|
|
*p++ = ':';
|
|
}
|
|
*p = '\0';
|
|
|
|
return string(buf, mac_addr, field_width, precision, flags & ~SPECIAL);
|
|
}
|
|
|
|
static char *ip6_addr_string(char *buf, u8 *addr, int field_width,
|
|
int precision, int flags)
|
|
{
|
|
char ip6_addr[8 * 5]; /* (8 * 4 hex digits), 7 colons and trailing zero */
|
|
char *p = ip6_addr;
|
|
int i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
p = pack_hex_byte(p, addr[2 * i]);
|
|
p = pack_hex_byte(p, addr[2 * i + 1]);
|
|
if (!(flags & SPECIAL) && i != 7)
|
|
*p++ = ':';
|
|
}
|
|
*p = '\0';
|
|
|
|
return string(buf, ip6_addr, field_width, precision, flags & ~SPECIAL);
|
|
}
|
|
|
|
static char *ip4_addr_string(char *buf, u8 *addr, int field_width,
|
|
int precision, int flags)
|
|
{
|
|
char ip4_addr[4 * 4]; /* (4 * 3 decimal digits), 3 dots and trailing zero */
|
|
char temp[3]; /* hold each IP quad in reverse order */
|
|
char *p = ip4_addr;
|
|
int i, digits;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
digits = put_dec_trunc(temp, addr[i]) - temp;
|
|
/* reverse the digits in the quad */
|
|
while (digits--)
|
|
*p++ = temp[digits];
|
|
if (i != 3)
|
|
*p++ = '.';
|
|
}
|
|
*p = '\0';
|
|
|
|
return string(buf, ip4_addr, field_width, precision, flags & ~SPECIAL);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* 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:
|
|
*
|
|
* - 'M' For a 6-byte MAC address, it prints the address in the
|
|
* usual colon-separated hex notation
|
|
* - 'I' [46] for IPv4/IPv6 addresses printed in the usual way (dot-separated
|
|
* decimal for v4 and colon separated network-order 16 bit hex for v6)
|
|
* - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is
|
|
* currently the same
|
|
*
|
|
* Note: The difference between 'S' and 'F' is that on ia64 and ppc64
|
|
* 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)
|
|
{
|
|
if (!ptr)
|
|
return string(buf, "(null)", field_width, precision, flags);
|
|
|
|
#ifdef CONFIG_CMD_NET
|
|
switch (*fmt) {
|
|
case 'm':
|
|
flags |= SPECIAL;
|
|
/* Fallthrough */
|
|
case 'M':
|
|
return mac_address_string(buf, 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);
|
|
if (fmt[1] == '4')
|
|
return ip4_addr_string(buf, ptr, field_width, precision, flags);
|
|
flags &= ~SPECIAL;
|
|
break;
|
|
}
|
|
#endif
|
|
flags |= SMALL;
|
|
if (field_width == -1) {
|
|
field_width = 2*sizeof(void *);
|
|
flags |= ZEROPAD;
|
|
}
|
|
return number(buf, (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)
|
|
{
|
|
unsigned NUM_TYPE num;
|
|
int base;
|
|
char *str;
|
|
|
|
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', 'l', or 'L' for integer fields */
|
|
/* 'z' support added 23/7/1999 S.H. */
|
|
/* 'z' changed to 'Z' --davidm 1/25/99 */
|
|
/* 't' added for ptrdiff_t */
|
|
|
|
str = buf;
|
|
|
|
for (; *fmt ; ++fmt) {
|
|
if (*fmt != '%') {
|
|
*str++ = *fmt;
|
|
continue;
|
|
}
|
|
|
|
/* process flags */
|
|
flags = 0;
|
|
repeat:
|
|
++fmt; /* this also skips first '%' */
|
|
switch (*fmt) {
|
|
case '-': flags |= LEFT; goto repeat;
|
|
case '+': flags |= PLUS; goto repeat;
|
|
case ' ': flags |= SPACE; goto repeat;
|
|
case '#': flags |= SPECIAL; goto repeat;
|
|
case '0': flags |= ZEROPAD; goto repeat;
|
|
}
|
|
|
|
/* get field width */
|
|
field_width = -1;
|
|
if (is_digit(*fmt))
|
|
field_width = skip_atoi(&fmt);
|
|
else if (*fmt == '*') {
|
|
++fmt;
|
|
/* it's the next argument */
|
|
field_width = va_arg(args, int);
|
|
if (field_width < 0) {
|
|
field_width = -field_width;
|
|
flags |= LEFT;
|
|
}
|
|
}
|
|
|
|
/* get the precision */
|
|
precision = -1;
|
|
if (*fmt == '.') {
|
|
++fmt;
|
|
if (is_digit(*fmt))
|
|
precision = skip_atoi(&fmt);
|
|
else if (*fmt == '*') {
|
|
++fmt;
|
|
/* it's the next argument */
|
|
precision = va_arg(args, int);
|
|
}
|
|
if (precision < 0)
|
|
precision = 0;
|
|
}
|
|
|
|
/* get the conversion qualifier */
|
|
qualifier = -1;
|
|
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
|
|
*fmt == 'Z' || *fmt == 'z' || *fmt == 't') {
|
|
qualifier = *fmt;
|
|
++fmt;
|
|
if (qualifier == 'l' && *fmt == 'l') {
|
|
qualifier = 'L';
|
|
++fmt;
|
|
}
|
|
}
|
|
|
|
/* default base */
|
|
base = 10;
|
|
|
|
switch (*fmt) {
|
|
case 'c':
|
|
if (!(flags & LEFT))
|
|
while (--field_width > 0)
|
|
*str++ = ' ';
|
|
*str++ = (unsigned char) va_arg(args, int);
|
|
while (--field_width > 0)
|
|
*str++ = ' ';
|
|
continue;
|
|
|
|
case 's':
|
|
str = string(str, va_arg(args, char *), field_width, precision, flags);
|
|
continue;
|
|
|
|
case 'p':
|
|
str = pointer(fmt+1, str,
|
|
va_arg(args, void *),
|
|
field_width, precision, flags);
|
|
/* Skip all alphanumeric pointer suffixes */
|
|
while (isalnum(fmt[1]))
|
|
fmt++;
|
|
continue;
|
|
|
|
case 'n':
|
|
if (qualifier == 'l') {
|
|
long * ip = va_arg(args, long *);
|
|
*ip = (str - buf);
|
|
} else {
|
|
int * ip = va_arg(args, int *);
|
|
*ip = (str - buf);
|
|
}
|
|
continue;
|
|
|
|
case '%':
|
|
*str++ = '%';
|
|
continue;
|
|
|
|
/* integer number formats - set up the flags and "break" */
|
|
case 'o':
|
|
base = 8;
|
|
break;
|
|
|
|
case 'x':
|
|
flags |= SMALL;
|
|
case 'X':
|
|
base = 16;
|
|
break;
|
|
|
|
case 'd':
|
|
case 'i':
|
|
flags |= SIGN;
|
|
case 'u':
|
|
break;
|
|
|
|
default:
|
|
*str++ = '%';
|
|
if (*fmt)
|
|
*str++ = *fmt;
|
|
else
|
|
--fmt;
|
|
continue;
|
|
}
|
|
#ifdef CONFIG_SYS_64BIT_VSPRINTF
|
|
if (qualifier == 'L') /* "quad" for 64 bit variables */
|
|
num = va_arg(args, unsigned long long);
|
|
else
|
|
#endif
|
|
if (qualifier == 'l') {
|
|
num = va_arg(args, unsigned long);
|
|
if (flags & SIGN)
|
|
num = (signed long) num;
|
|
} else if (qualifier == 'Z' || qualifier == 'z') {
|
|
num = va_arg(args, size_t);
|
|
} else if (qualifier == 't') {
|
|
num = va_arg(args, ptrdiff_t);
|
|
} else if (qualifier == 'h') {
|
|
num = (unsigned short) va_arg(args, int);
|
|
if (flags & SIGN)
|
|
num = (signed short) num;
|
|
} else {
|
|
num = va_arg(args, unsigned int);
|
|
if (flags & SIGN)
|
|
num = (signed int) num;
|
|
}
|
|
str = number(str, num, base, field_width, precision, flags);
|
|
}
|
|
*str = '\0';
|
|
return str-buf;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* The function returns the number of characters written
|
|
* into @buf.
|
|
*
|
|
* See the vsprintf() documentation for format string extensions over C99.
|
|
*/
|
|
int sprintf(char * buf, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
int i;
|
|
|
|
va_start(args, fmt);
|
|
i=vsprintf(buf,fmt,args);
|
|
va_end(args);
|
|
return i;
|
|
}
|
|
|
|
void panic(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vprintf(fmt, args);
|
|
putc('\n');
|
|
va_end(args);
|
|
#if defined (CONFIG_PANIC_HANG)
|
|
hang();
|
|
#else
|
|
udelay (100000); /* allow messages to go out */
|
|
do_reset (NULL, 0, 0, NULL);
|
|
#endif
|
|
}
|