forked from Minki/linux
5b8fafcac6
Prior to 9b706aee7d
("x86: trivial printk optimizations") this was
36 because it had 26 characters and 10 digits but now it's just
16 hex digits so the sanity check needs updated.
This function is always called with a valid "base" so it doesn't
make a difference to how the kernel works, it's just a cleanup.
Reported-by: Alexey Petrenko <alexey.petrenko@oracle.com>
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
310 lines
6.0 KiB
C
310 lines
6.0 KiB
C
/* -*- linux-c -*- ------------------------------------------------------- *
|
|
*
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
* Copyright 2007 rPath, Inc. - All Rights Reserved
|
|
*
|
|
* This file is part of the Linux kernel, and is made available under
|
|
* the terms of the GNU General Public License version 2.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* Oh, it's a waste of space, but oh-so-yummy for debugging. This
|
|
* version of printf() does not include 64-bit support. "Live with
|
|
* it."
|
|
*
|
|
*/
|
|
|
|
#include "boot.h"
|
|
|
|
static int skip_atoi(const char **s)
|
|
{
|
|
int i = 0;
|
|
|
|
while (isdigit(**s))
|
|
i = i * 10 + *((*s)++) - '0';
|
|
return i;
|
|
}
|
|
|
|
#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 __do_div(n, base) ({ \
|
|
int __res; \
|
|
__res = ((unsigned long) n) % (unsigned) base; \
|
|
n = ((unsigned long) n) / (unsigned) base; \
|
|
__res; })
|
|
|
|
static char *number(char *str, long 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 c, sign, locase;
|
|
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;
|
|
if (base < 2 || base > 16)
|
|
return NULL;
|
|
c = (type & ZEROPAD) ? '0' : ' ';
|
|
sign = 0;
|
|
if (type & SIGN) {
|
|
if (num < 0) {
|
|
sign = '-';
|
|
num = -num;
|
|
size--;
|
|
} else if (type & PLUS) {
|
|
sign = '+';
|
|
size--;
|
|
} else if (type & SPACE) {
|
|
sign = ' ';
|
|
size--;
|
|
}
|
|
}
|
|
if (type & SPECIAL) {
|
|
if (base == 16)
|
|
size -= 2;
|
|
else if (base == 8)
|
|
size--;
|
|
}
|
|
i = 0;
|
|
if (num == 0)
|
|
tmp[i++] = '0';
|
|
else
|
|
while (num != 0)
|
|
tmp[i++] = (digits[__do_div(num, base)] | locase);
|
|
if (i > precision)
|
|
precision = i;
|
|
size -= precision;
|
|
if (!(type & (ZEROPAD + LEFT)))
|
|
while (size-- > 0)
|
|
*str++ = ' ';
|
|
if (sign)
|
|
*str++ = sign;
|
|
if (type & SPECIAL) {
|
|
if (base == 8)
|
|
*str++ = '0';
|
|
else if (base == 16) {
|
|
*str++ = '0';
|
|
*str++ = ('X' | locase);
|
|
}
|
|
}
|
|
if (!(type & LEFT))
|
|
while (size-- > 0)
|
|
*str++ = c;
|
|
while (i < precision--)
|
|
*str++ = '0';
|
|
while (i-- > 0)
|
|
*str++ = tmp[i];
|
|
while (size-- > 0)
|
|
*str++ = ' ';
|
|
return str;
|
|
}
|
|
|
|
int vsprintf(char *buf, const char *fmt, va_list args)
|
|
{
|
|
int len;
|
|
unsigned long num;
|
|
int i, base;
|
|
char *str;
|
|
const char *s;
|
|
|
|
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 */
|
|
|
|
for (str = buf; *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 (isdigit(*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 (isdigit(*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') {
|
|
qualifier = *fmt;
|
|
++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':
|
|
s = va_arg(args, char *);
|
|
len = strnlen(s, precision);
|
|
|
|
if (!(flags & LEFT))
|
|
while (len < field_width--)
|
|
*str++ = ' ';
|
|
for (i = 0; i < len; ++i)
|
|
*str++ = *s++;
|
|
while (len < field_width--)
|
|
*str++ = ' ';
|
|
continue;
|
|
|
|
case 'p':
|
|
if (field_width == -1) {
|
|
field_width = 2 * sizeof(void *);
|
|
flags |= ZEROPAD;
|
|
}
|
|
str = number(str,
|
|
(unsigned long)va_arg(args, void *), 16,
|
|
field_width, precision, flags);
|
|
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;
|
|
}
|
|
if (qualifier == 'l')
|
|
num = va_arg(args, unsigned long);
|
|
else if (qualifier == 'h') {
|
|
num = (unsigned short)va_arg(args, int);
|
|
if (flags & SIGN)
|
|
num = (short)num;
|
|
} else if (flags & SIGN)
|
|
num = va_arg(args, int);
|
|
else
|
|
num = va_arg(args, unsigned int);
|
|
str = number(str, num, base, field_width, precision, flags);
|
|
}
|
|
*str = '\0';
|
|
return str - buf;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int printf(const char *fmt, ...)
|
|
{
|
|
char printf_buf[1024];
|
|
va_list args;
|
|
int printed;
|
|
|
|
va_start(args, fmt);
|
|
printed = vsprintf(printf_buf, fmt, args);
|
|
va_end(args);
|
|
|
|
puts(printf_buf);
|
|
|
|
return printed;
|
|
}
|