| /* |
| * linux/lib/vsprintf.c |
| * |
| * Copyright (C) 1991, 1992 Linus Torvalds |
| */ |
| |
| /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ |
| /* |
| * Wirzenius wrote this portably, Torvalds fucked it up :-) |
| */ |
| |
| /* |
| * 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 |
| */ |
| |
| /* (trimmed to just scanf for Akaros). grep AKAROS_PORT for dirty hacks */ |
| |
| #include <ros/common.h> |
| #include <error.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdarg.h> |
| #include <ns.h> |
| #include <ctype.h> |
| |
| /** |
| * skip_spaces - Removes leading whitespace from @str. |
| * @str: The string to be stripped. |
| * |
| * Returns a pointer to the first non-whitespace character in @str. |
| */ |
| static char *skip_spaces(const char *str) |
| { |
| while (isspace(*str)) |
| ++str; |
| return (char *)str; |
| } |
| |
| static int skip_atoi(const char **s) |
| { |
| int i = 0; |
| |
| while (isdigit(**s)) |
| i = i*10 + *((*s)++) - '0'; |
| |
| return i; |
| } |
| |
| const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) |
| { |
| if (*base == 0) { |
| if (s[0] == '0') { |
| if (_tolower(s[1]) == 'x' && isxdigit(s[2])) |
| *base = 16; |
| else |
| *base = 8; |
| } else |
| *base = 10; |
| } |
| if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x') |
| s += 2; |
| return s; |
| } |
| |
| /** |
| * vsscanf - Unformat a buffer into a list of arguments |
| * @buf: input buffer |
| * @fmt: format of buffer |
| * @args: arguments |
| */ |
| int vsscanf(const char *buf, const char *fmt, va_list args) |
| { |
| const char *str = buf; |
| char *next; |
| char digit; |
| int num = 0; |
| uint8_t qualifier; |
| unsigned int base; |
| union { |
| long long s; |
| unsigned long long u; |
| } val; |
| int16_t field_width; |
| bool is_sign; |
| |
| while (*fmt) { |
| /* skip any white space in format */ |
| /* white space in format matchs any amount of |
| * white space, including none, in the input. |
| */ |
| if (isspace(*fmt)) { |
| fmt = skip_spaces(++fmt); |
| str = skip_spaces(str); |
| } |
| |
| /* anything that is not a conversion must match exactly */ |
| if (*fmt != '%' && *fmt) { |
| if (*fmt++ != *str++) |
| break; |
| continue; |
| } |
| |
| if (!*fmt) |
| break; |
| ++fmt; |
| |
| /* skip this conversion. |
| * advance both strings to next white space |
| */ |
| if (*fmt == '*') { |
| if (!*str) |
| break; |
| while (!isspace(*fmt) && *fmt != '%' && *fmt) |
| fmt++; |
| while (!isspace(*str) && *str) |
| str++; |
| continue; |
| } |
| |
| /* get field width */ |
| field_width = -1; |
| if (isdigit(*fmt)) { |
| field_width = skip_atoi(&fmt); |
| if (field_width <= 0) |
| break; |
| } |
| |
| /* get conversion qualifier */ |
| qualifier = -1; |
| if (*fmt == 'h' || _tolower(*fmt) == 'l' || |
| _tolower(*fmt) == 'z') { |
| qualifier = *fmt++; |
| if (unlikely(qualifier == *fmt)) { |
| if (qualifier == 'h') { |
| qualifier = 'H'; |
| fmt++; |
| } else if (qualifier == 'l') { |
| qualifier = 'L'; |
| fmt++; |
| } |
| } |
| } |
| |
| if (!*fmt) |
| break; |
| |
| if (*fmt == 'n') { |
| /* return number of characters read so far */ |
| *va_arg(args, int *) = str - buf; |
| ++fmt; |
| continue; |
| } |
| |
| if (!*str) |
| break; |
| |
| base = 10; |
| is_sign = false; |
| |
| switch (*fmt++) { |
| case 'c': |
| { |
| char *s = (char *)va_arg(args, char*); |
| if (field_width == -1) |
| field_width = 1; |
| do { |
| *s++ = *str++; |
| } while (--field_width > 0 && *str); |
| num++; |
| } |
| continue; |
| case 's': |
| { |
| char *s = (char *)va_arg(args, char *); |
| if (field_width == -1) |
| field_width = INT16_MAX; |
| /* first, skip leading white space in buffer */ |
| str = skip_spaces(str); |
| |
| /* now copy until next white space */ |
| while (*str && !isspace(*str) && field_width--) |
| *s++ = *str++; |
| *s = '\0'; |
| num++; |
| } |
| continue; |
| case 'o': |
| base = 8; |
| break; |
| case 'x': |
| case 'X': |
| base = 16; |
| break; |
| case 'i': |
| base = 0; |
| case 'd': |
| is_sign = true; |
| case 'u': |
| break; |
| case '%': |
| /* looking for '%' in str */ |
| if (*str++ != '%') |
| return num; |
| continue; |
| default: |
| /* invalid format; stop here */ |
| return num; |
| } |
| |
| /* have some sort of integer conversion. |
| * first, skip white space in buffer. |
| */ |
| str = skip_spaces(str); |
| |
| digit = *str; |
| if (is_sign && digit == '-') |
| digit = *(str + 1); |
| |
| if (!digit |
| || (base == 16 && !isxdigit(digit)) |
| || (base == 10 && !isdigit(digit)) |
| || (base == 8 && (!isdigit(digit) || digit > '7')) |
| || (base == 0 && !isdigit(digit))) |
| break; |
| |
| if (is_sign) |
| #if 0 // AKAROS_PORT |
| val.s = qualifier != 'L' ? |
| strtol(str, &next, base) : |
| strtoll(str, &next, base); |
| #else |
| val.s = strtol(str, &next, base); |
| #endif |
| else |
| #if 0 // AKAROS_PORT |
| val.u = qualifier != 'L' ? |
| strtoul(str, &next, base) : |
| strtoull(str, &next, base); |
| #else |
| val.s = strtoul(str, &next, base); |
| #endif |
| |
| if (field_width > 0 && next - str > field_width) { |
| if (base == 0) |
| _parse_integer_fixup_radix(str, &base); |
| while (next - str > field_width) { |
| if (is_sign) // AKAROS_PORT |
| val.s = (int64_t)(val.s / base); |
| else |
| val.u = (uint64_t)(val.u / base); |
| --next; |
| } |
| } |
| |
| switch (qualifier) { |
| case 'H': /* that's 'hh' in format */ |
| if (is_sign) |
| *va_arg(args, signed char *) = val.s; |
| else |
| *va_arg(args, unsigned char *) = val.u; |
| break; |
| case 'h': |
| if (is_sign) |
| *va_arg(args, short *) = val.s; |
| else |
| *va_arg(args, unsigned short *) = val.u; |
| break; |
| case 'l': |
| if (is_sign) |
| *va_arg(args, long *) = val.s; |
| else |
| *va_arg(args, unsigned long *) = val.u; |
| break; |
| case 'L': |
| if (is_sign) |
| *va_arg(args, long long *) = val.s; |
| else |
| *va_arg(args, unsigned long long *) = val.u; |
| break; |
| case 'Z': |
| case 'z': |
| *va_arg(args, size_t *) = val.u; |
| break; |
| default: |
| if (is_sign) |
| *va_arg(args, int *) = val.s; |
| else |
| *va_arg(args, unsigned int *) = val.u; |
| break; |
| } |
| num++; |
| |
| if (!next) |
| break; |
| str = next; |
| } |
| |
| return num; |
| } |
| |
| /** |
| * sscanf - Unformat a buffer into a list of arguments |
| * @buf: input buffer |
| * @fmt: formatting of buffer |
| * @...: resulting arguments |
| */ |
| int sscanf(const char *buf, const char *fmt, ...) |
| { |
| va_list args; |
| int i; |
| |
| va_start(args, fmt); |
| i = vsscanf(buf, fmt, args); |
| va_end(args); |
| |
| return i; |
| } |