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