|  | /* | 
|  | *  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; | 
|  | } |