#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
+#include <linux/kallsyms.h>
+#include <linux/uaccess.h>
#include <asm/page.h> /* for PAGE_SIZE */
#include <asm/div64.h>
+#include <asm/sections.h> /* for dereference_function_descriptor() */
/* Works only for digits and letters, but small and fast */
#define TOLOWER(x) ((x) | 0x20)
+static unsigned int simple_guess_base(const char *cp)
+{
+ if (cp[0] == '0') {
+ if (TOLOWER(cp[1]) == 'x' && isxdigit(cp[2]))
+ return 16;
+ else
+ return 8;
+ } else {
+ return 10;
+ }
+}
+
/**
* simple_strtoul - convert a string to an unsigned 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
*/
-unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
+unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
{
- unsigned long result = 0,value;
+ unsigned long result = 0;
- if (!base) {
- base = 10;
- if (*cp == '0') {
- base = 8;
- cp++;
- if ((TOLOWER(*cp) == 'x') && isxdigit(cp[1])) {
- cp++;
- base = 16;
- }
- }
- } else if (base == 16) {
- if (cp[0] == '0' && TOLOWER(cp[1]) == 'x')
- cp += 2;
- }
- while (isxdigit(*cp) &&
- (value = isdigit(*cp) ? *cp-'0' : TOLOWER(*cp)-'a'+10) < base) {
- result = result*base + value;
+ if (!base)
+ base = simple_guess_base(cp);
+
+ if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x')
+ cp += 2;
+
+ while (isxdigit(*cp)) {
+ unsigned int value;
+
+ value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10;
+ if (value >= base)
+ break;
+ result = result * base + value;
cp++;
}
+
if (endp)
*endp = (char *)cp;
return result;
}
-
EXPORT_SYMBOL(simple_strtoul);
/**
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
-long simple_strtol(const char *cp,char **endp,unsigned int base)
+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);
+ if(*cp == '-')
+ return -simple_strtoul(cp + 1, endp, base);
+ return simple_strtoul(cp, endp, base);
}
-
EXPORT_SYMBOL(simple_strtol);
/**
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
-unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
+unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
{
- unsigned long long result = 0,value;
+ unsigned long long result = 0;
- if (!base) {
- base = 10;
- if (*cp == '0') {
- base = 8;
- cp++;
- if ((TOLOWER(*cp) == 'x') && isxdigit(cp[1])) {
- cp++;
- base = 16;
- }
- }
- } else if (base == 16) {
- if (cp[0] == '0' && TOLOWER(cp[1]) == 'x')
- cp += 2;
- }
- while (isxdigit(*cp)
- && (value = isdigit(*cp) ? *cp-'0' : TOLOWER(*cp)-'a'+10) < base) {
- result = result*base + value;
+ if (!base)
+ base = simple_guess_base(cp);
+
+ if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x')
+ cp += 2;
+
+ while (isxdigit(*cp)) {
+ unsigned int value;
+
+ value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10;
+ if (value >= base)
+ break;
+ result = result * base + value;
cp++;
}
+
if (endp)
*endp = (char *)cp;
return result;
}
-
EXPORT_SYMBOL(simple_strtoull);
/**
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
-long long simple_strtoll(const char *cp,char **endp,unsigned int base)
+long long simple_strtoll(const char *cp, char **endp, unsigned int base)
{
if(*cp=='-')
- return -simple_strtoull(cp+1,endp,base);
- return simple_strtoull(cp,endp,base);
+ return -simple_strtoull(cp + 1, endp, base);
+ return simple_strtoull(cp, endp, base);
}
-
/**
* strict_strtoul - convert a string to an unsigned long strictly
* @cp: The string to be converted
* simple_strtoul just ignores the successive invalid characters and
* return the converted value of prefix part of the string.
*/
-int strict_strtoul(const char *cp, unsigned int base, unsigned long *res);
+int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
+{
+ char *tail;
+ unsigned long val;
+ size_t len;
+
+ *res = 0;
+ len = strlen(cp);
+ if (len == 0)
+ return -EINVAL;
+
+ val = simple_strtoul(cp, &tail, base);
+ if ((*tail == '\0') ||
+ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
+ *res = val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(strict_strtoul);
/**
* strict_strtol - convert a string to a long strictly
* 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.
*/
-int strict_strtol(const char *cp, unsigned int base, long *res);
+int strict_strtol(const char *cp, unsigned int base, long *res)
+{
+ int ret;
+ if (*cp == '-') {
+ ret = strict_strtoul(cp + 1, base, (unsigned long *)res);
+ if (!ret)
+ *res = -(*res);
+ } else {
+ ret = strict_strtoul(cp, base, (unsigned long *)res);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(strict_strtol);
/**
* strict_strtoull - convert a string to an unsigned long long strictly
* simple_strtoull just ignores the successive invalid characters and
* return the converted value of prefix part of the string.
*/
-int strict_strtoull(const char *cp, unsigned int base, unsigned long long *res);
+int strict_strtoull(const char *cp, unsigned int base, unsigned long long *res)
+{
+ char *tail;
+ unsigned long long val;
+ size_t len;
+
+ *res = 0;
+ len = strlen(cp);
+ if (len == 0)
+ return -EINVAL;
+
+ val = simple_strtoull(cp, &tail, base);
+ if ((*tail == '\0') ||
+ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {
+ *res = val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(strict_strtoull);
/**
* strict_strtoll - convert a string to a long long strictly
* 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.
*/
-int strict_strtoll(const char *cp, unsigned int base, long long *res);
-
-#define define_strict_strtoux(type, valtype) \
-int strict_strtou##type(const char *cp, unsigned int base, valtype *res)\
-{ \
- char *tail; \
- valtype val; \
- size_t len; \
- \
- *res = 0; \
- len = strlen(cp); \
- if (len == 0) \
- return -EINVAL; \
- \
- val = simple_strtoul(cp, &tail, base); \
- if ((*tail == '\0') || \
- ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) {\
- *res = val; \
- return 0; \
- } \
- \
- return -EINVAL; \
-} \
-
-#define define_strict_strtox(type, valtype) \
-int strict_strto##type(const char *cp, unsigned int base, valtype *res) \
-{ \
- int ret; \
- if (*cp == '-') { \
- ret = strict_strtou##type(cp+1, base, res); \
- if (!ret) \
- *res = -(*res); \
- } else \
- ret = strict_strtou##type(cp, base, res); \
- \
- return ret; \
-} \
-
-define_strict_strtoux(l, unsigned long)
-define_strict_strtox(l, long)
-define_strict_strtoux(ll, unsigned long long)
-define_strict_strtox(ll, long long)
+int strict_strtoll(const char *cp, unsigned int base, long long *res)
+{
+ int ret;
+ if (*cp == '-') {
+ ret = strict_strtoull(cp + 1, base, (unsigned long long *)res);
+ if (!ret)
+ *res = -(*res);
+ } else {
+ ret = strict_strtoull(cp, base, (unsigned long long *)res);
+ }
-EXPORT_SYMBOL(strict_strtoul);
-EXPORT_SYMBOL(strict_strtol);
+ return ret;
+}
EXPORT_SYMBOL(strict_strtoll);
-EXPORT_SYMBOL(strict_strtoull);
static int skip_atoi(const char **s)
{
return buf;
}
+static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+{
+ int len, i;
+
+ if ((unsigned long)s < PAGE_SIZE)
+ s = "<NULL>";
+
+ len = strnlen(s, precision);
+
+ if (!(flags & LEFT)) {
+ while (len < field_width--) {
+ if (buf < end)
+ *buf = ' ';
+ ++buf;
+ }
+ }
+ for (i = 0; i < len; ++i) {
+ if (buf < end)
+ *buf = *s;
+ ++buf; ++s;
+ }
+ while (len < field_width--) {
+ if (buf < end)
+ *buf = ' ';
+ ++buf;
+ }
+ return buf;
+}
+
+static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int precision, int flags)
+{
+ unsigned long value = (unsigned long) ptr;
+#ifdef CONFIG_KALLSYMS
+ char sym[KSYM_SYMBOL_LEN];
+ sprint_symbol(sym, value);
+ return string(buf, end, sym, field_width, precision, flags);
+#else
+ field_width = 2*sizeof(void *);
+ flags |= SPECIAL | SMALL | ZEROPAD;
+ return number(buf, end, value, 16, field_width, precision, flags);
+#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 just handle 'F' (for symbolic Function descriptor pointers)
+ * and 'S' (for Symbolic direct pointers), but this can easily be
+ * extended in the future (network address types etc).
+ *
+ * The difference between 'S' and 'F' is that on ia64 and ppc64 function
+ * pointers are really function descriptors, which contain a pointer the
+ * real address.
+ */
+static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
+{
+ switch (*fmt) {
+ case 'F':
+ ptr = dereference_function_descriptor(ptr);
+ /* Fallthrough */
+ case 'S':
+ return symbol_string(buf, end, ptr, field_width, precision, flags);
+ }
+ flags |= SMALL;
+ if (field_width == -1) {
+ field_width = 2*sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
+}
+
/**
* vsnprintf - 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 vsnprintf, but has some extensions:
+ * %pS output the name of a text symbol
+ * %pF output the name of a function pointer
+ *
* The return value is the number of characters which would
* be generated for the given input, excluding the trailing
* '\0', as per ISO C99. If you want to have the exact
*/
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
- int len;
unsigned long long num;
- int i, base;
+ int base;
char *str, *end, c;
- const char *s;
int flags; /* flags to number() */
continue;
case 's':
- s = va_arg(args, char *);
- if ((unsigned long)s < PAGE_SIZE)
- s = "<NULL>";
-
- len = strnlen(s, precision);
-
- if (!(flags & LEFT)) {
- while (len < field_width--) {
- if (str < end)
- *str = ' ';
- ++str;
- }
- }
- for (i = 0; i < len; ++i) {
- if (str < end)
- *str = *s;
- ++str; ++s;
- }
- while (len < field_width--) {
- if (str < end)
- *str = ' ';
- ++str;
- }
+ str = string(str, end, va_arg(args, char *), field_width, precision, flags);
continue;
case 'p':
- flags |= SMALL;
- if (field_width == -1) {
- field_width = 2*sizeof(void *);
- flags |= ZEROPAD;
- }
- str = number(str, end,
- (unsigned long) va_arg(args, void *),
- 16, field_width, precision, flags);
+ str = pointer(fmt+1, str, end,
+ va_arg(args, void *),
+ field_width, precision, flags);
+ /* Skip all alphanumeric pointer suffixes */
+ while (isalnum(fmt[1]))
+ fmt++;
continue;
-
case 'n':
/* FIXME:
* What does C99 say about the overflow case here? */
/* the trailing null byte doesn't count towards the total */
return str-buf;
}
-
EXPORT_SYMBOL(vsnprintf);
/**
*
* Call this function if you are already dealing with a va_list.
* You probably want scnprintf() instead.
+ *
+ * See the vsnprintf() documentation for format string extensions over C99.
*/
int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
{
i=vsnprintf(buf,size,fmt,args);
return (i >= size) ? (size - 1) : i;
}
-
EXPORT_SYMBOL(vscnprintf);
/**
* generated for the given input, excluding the trailing null,
* as per ISO C99. If the return is greater than or equal to
* @size, the resulting string is truncated.
+ *
+ * See the vsnprintf() documentation for format string extensions over C99.
*/
int snprintf(char * buf, size_t size, const char *fmt, ...)
{
va_end(args);
return i;
}
-
EXPORT_SYMBOL(snprintf);
/**
*
* Call this function if you are already dealing with a va_list.
* You probably want sprintf() instead.
+ *
+ * See the vsnprintf() documentation for format string extensions over C99.
*/
int vsprintf(char *buf, const char *fmt, va_list args)
{
return vsnprintf(buf, INT_MAX, fmt, args);
}
-
EXPORT_SYMBOL(vsprintf);
/**
* The function returns the number of characters written
* into @buf. Use snprintf() or scnprintf() in order to avoid
* buffer overflows.
+ *
+ * See the vsnprintf() documentation for format string extensions over C99.
*/
int sprintf(char * buf, const char *fmt, ...)
{
va_end(args);
return i;
}
-
EXPORT_SYMBOL(sprintf);
/**
return num;
}
-
EXPORT_SYMBOL(vsscanf);
/**
va_end(args);
return i;
}
-
EXPORT_SYMBOL(sscanf);