text: /*
   *  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
   */
  
  #include <stdarg.h>
  #include <lwk/linux_compat.h>
  #include <lwk/types.h>
  #include <lwk/string.h>
  #include <lwk/ctype.h>
  #include <lwk/kernel.h>
  
  #include <arch/page.h>		/* for PAGE_SIZE */
  #include <arch/div64.h>
  
  /**
   * 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 result = 0,value;
  
  	if (!base) {
  		base = 10;
  		if (*cp == '0') {
  			base = 8;
  			cp++;
  			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
  				cp++;
  				base = 16;
  			}
  		}
  	} else if (base == 16) {
  		if (cp[0] == '0' && toupper(cp[1]) == 'X')
  			cp += 2;
  	}
  	while (isxdigit(*cp) &&
  	       (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
  		result = result*base + value;
  		cp++;
  	}
  	if (endp)
  		*endp = (char *)cp;
  	return result;
  }
  
  EXPORT_SYMBOL(simple_strtoul);
  
  /**
   * simple_strtol - convert a string to a signed 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
   */
  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);
  }
  
  EXPORT_SYMBOL(simple_strtol);
  
  /**
   * simple_strtoull - convert a string to an unsigned long 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 long simple_strtoull(const char *cp,char **endp,unsigned int base)
  {
  	unsigned long long result = 0,value;
  
  	if (!base) {
  		base = 10;
  		if (*cp == '0') {
  			base = 8;
  			cp++;
  			if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
  				cp++;
  				base = 16;
  			}
  		}
  	} else if (base == 16) {
  		if (cp[0] == '0' && toupper(cp[1]) == 'X')
  			cp += 2;
  	}
  	while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
  	    ? toupper(*cp) : *cp)-'A'+10) < base) {
  		result = result*base + value;
  		cp++;
  	}
  	if (endp)
  		*endp = (char *)cp;
  	return result;
  }
  
  EXPORT_SYMBOL(simple_strtoull);
  
  /**
   * simple_strtoll - convert a string to a signed long 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
   */
  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);
  }
  
  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 SPECIAL	32		/* 0x */
  #define LARGE	64		/* use 'ABCDEF' instead of 'abcdef' */
  
  static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
  {
  	char c,sign,tmp[66];
  	const char *digits;
  	static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
  	static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  	int i;
  
  	digits = (type & LARGE) ? large_digits : small_digits;
  	if (type & LEFT)
  		type &= ~ZEROPAD;
  	if (base < 2 || base > 36)
  		return NULL;
  	c = (type & ZEROPAD) ? '0' : ' ';
  	sign = 0;
  	if (type & SIGN) {
  		if ((signed long long) num < 0) {
  			sign = '-';
  			num = - (signed long long) 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)];
  	if (i > precision)
  		precision = i;
  	size -= precision;
  	if (!(type&(ZEROPAD+LEFT))) {
  		while(size-->0) {
  			if (buf <= end)
  				*buf = ' ';
  			++buf;
  		}
  	}
  	if (sign) {
  		if (buf <= end)
  			*buf = sign;
  		++buf;
  	}
  	if (type & SPECIAL) {
  		if (base==8) {
  			if (buf <= end)
  				*buf = '0';
  			++buf;
  		} else if (base==16) {
  			if (buf <= end)
  				*buf = '0';
  			++buf;
  			if (buf <= end)
  				*buf = digits[33];
  			++buf;
  		}
  	}
  	if (!(type & LEFT)) {
  		while (size-- > 0) {
  			if (buf <= end)
  				*buf = c;
  			++buf;
  		}
  	}
  	while (i < precision--) {
  		if (buf <= end)
  			*buf = '0';
  		++buf;
  	}
  	while (i-- > 0) {
  		if (buf <= end)
  			*buf = tmp[i];
  		++buf;
  	}
  	while (size-- > 0) {
  		if (buf <= end)
  			*buf = ' ';
  		++buf;
  	}
  	return buf;
  }
  
  /**
   * vsnprintf - Format a string and place it in a buffer
   * @buf: The buffer to place the result into
   * @size: The size of the buffer, including the trailing null space
   * @fmt: The format string to use
   * @args: Arguments for the format string
   *
   * 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
   * number of characters written into @buf as return value
   * (not including the trailing '\0'), use vscnprintf. If the
   * return is greater than or equal to @size, the resulting
   * string is truncated.
   *
   * Call this function if you are already dealing with a va_list.
   * You probably want snprintf instead.
   */
  int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
  {
  	int len;
  	unsigned long long num;
  	int i, base;
  	char *str, *end, c;
  	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 */
  				/* 'z' support added 23/7/1999 S.H.    */
  				/* 'z' changed to 'Z' --davidm 1/25/99 */
  				/* 't' added for ptrdiff_t */
  
  	/* Reject out-of-range values early */
  	if (unlikely((int) size < 0)) {
  		/* There can be only one.. */
  		static int warn = 1;
  		WARN_ON(warn);
  		warn = 0;
  		return 0;
  	}
  
  	str = buf;
  	end = buf + size - 1;
  
  	if (end < buf - 1) {
  		end = ((void *) -1);
  		size = end - buf + 1;
  	}
  
  	for (; *fmt ; ++fmt) {
  		if (*fmt != '%') {
  			if (str <= end)
  				*str = *fmt;
  			++str;
  			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' ||
  		    *fmt =='Z' || *fmt == 'z' || *fmt == 't') {
  			qualifier = *fmt;
  			++fmt;
  			if (qualifier == 'l' && *fmt == 'l') {
  				qualifier = 'L';
  				++fmt;
  			}
  		}
  
  		/* default base */
  		base = 10;
  
  		switch (*fmt) {
  			case 'c':
  				if (!(flags & LEFT)) {
  					while (--field_width > 0) {
  						if (str <= end)
  							*str = ' ';
  						++str;
  					}
  				}
  				c = (unsigned char) va_arg(args, int);
  				if (str <= end)
  					*str = c;
  				++str;
  				while (--field_width > 0) {
  					if (str <= end)
  						*str = ' ';
  					++str;
  				}
  				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;
  				}
  				continue;
  
  			case 'p':
  				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);
  				continue;
  
  
  			case 'n':
  				/* FIXME:
  				* What does C99 say about the overflow case here? */
  				if (qualifier == 'l') {
  					long * ip = va_arg(args, long *);
  					*ip = (str - buf);
  				} else if (qualifier == 'Z' || qualifier == 'z') {
  					size_t * ip = va_arg(args, size_t *);
  					*ip = (str - buf);
  				} else {
  					int * ip = va_arg(args, int *);
  					*ip = (str - buf);
  				}
  				continue;
  
  			case '%':
  				if (str <= end)
  					*str = '%';
  				++str;
  				continue;
  
  				/* integer number formats - set up the flags and "break" */
  			case 'o':
  				base = 8;
  				break;
  
  			case 'X':
  				flags |= LARGE;
  			case 'x':
  				base = 16;
  				break;
  
  			case 'd':
  			case 'i':
  				flags |= SIGN;
  			case 'u':
  				break;
  
  			default:
  				if (str <= end)
  					*str = '%';
  				++str;
  				if (*fmt) {
  					if (str <= end)
  						*str = *fmt;
  					++str;
  				} else {
  					--fmt;
  				}
  				continue;
  		}
  		if (qualifier == 'L')
  			num = va_arg(args, long long);
  		else if (qualifier == 'l') {
  			num = va_arg(args, unsigned long);
  			if (flags & SIGN)
  				num = (signed long) num;
  		} else if (qualifier == 'Z' || qualifier == 'z') {
  			num = va_arg(args, size_t);
  		} else if (qualifier == 't') {
  			num = va_arg(args, ptrdiff_t);
  		} else if (qualifier == 'h') {
  			num = (unsigned short) va_arg(args, int);
  			if (flags & SIGN)
  				num = (signed short) num;
  		} else {
  			num = va_arg(args, unsigned int);
  			if (flags & SIGN)
  				num = (signed int) num;
  		}
  		str = number(str, end, num, base,
  				field_width, precision, flags);
  	}
  	if (str <= end)
  		*str = '\0';
  	else if (size > 0)
  		/* don't write out a null byte if the buf size is zero */
  		*end = '\0';
  	/* the trailing null byte doesn't count towards the total
  	* ++str;
  	*/
  	return str-buf;
  }
  
  EXPORT_SYMBOL(vsnprintf);
  
  /**
   * vscnprintf - Format a string and place it in a buffer
   * @buf: The buffer to place the result into
   * @size: The size of the buffer, including the trailing null space
   * @fmt: The format string to use
   * @args: Arguments for the format string
   *
   * The return value is the number of characters which have been written into
   * the @buf not including the trailing '\0'. If @size is <= 0 the function
   * returns 0.
   *
   * Call this function if you are already dealing with a va_list.
   * You probably want scnprintf instead.
   */
  int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
  {
  	int i;
  
  	i=vsnprintf(buf,size,fmt,args);
  	return (i >= size) ? (size - 1) : i;
  }
  
  EXPORT_SYMBOL(vscnprintf);
  
  /**
   * snprintf - Format a string and place it in a buffer
   * @buf: The buffer to place the result into
   * @size: The size of the buffer, including the trailing null space
   * @fmt: The format string to use
   * @...: Arguments for the format string
   *
   * The return value is the number of characters which would be
   * 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.
   */
  int snprintf(char * buf, size_t size, const char *fmt, ...)
  {
  	va_list args;
  	int i;
  
  	va_start(args, fmt);
  	i=vsnprintf(buf,size,fmt,args);
  	va_end(args);
  	return i;
  }
  
  EXPORT_SYMBOL(snprintf);
  
  /**
   * scnprintf - Format a string and place it in a buffer
   * @buf: The buffer to place the result into
   * @size: The size of the buffer, including the trailing null space
   * @fmt: The format string to use
   * @...: Arguments for the format string
   *
   * The return value is the number of characters written into @buf not including
   * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is
   * greater than or equal to @size, the resulting string is truncated.
   */
  
  int scnprintf(char * buf, size_t size, const char *fmt, ...)
  {
  	va_list args;
  	int i;
  
  	va_start(args, fmt);
  	i = vsnprintf(buf, size, fmt, args);
  	va_end(args);
  	return (i >= size) ? (size - 1) : i;
  }
  EXPORT_SYMBOL(scnprintf);
  
  /**
   * vsprintf - 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
   *
   * The function returns the number of characters written
   * into @buf. Use vsnprintf or vscnprintf in order to avoid
   * buffer overflows.
   *
   * Call this function if you are already dealing with a va_list.
   * You probably want sprintf instead.
   */
  int vsprintf(char *buf, const char *fmt, va_list args)
  {
  	return vsnprintf(buf, INT_MAX, fmt, args);
  }
  
  EXPORT_SYMBOL(vsprintf);
  
  /**
   * sprintf - Format a string and place it in a buffer
   * @buf: The buffer to place the result into
   * @fmt: The format string to use
   * @...: Arguments for the format string
   *
   * The function returns the number of characters written
   * into @buf. Use snprintf or scnprintf in order to avoid
   * buffer overflows.
   */
  int sprintf(char * buf, const char *fmt, ...)
  {
  	va_list args;
  	int i;
  
  	va_start(args, fmt);
  	i=vsnprintf(buf, INT_MAX, fmt, args);
  	va_end(args);
  	return i;
  }
  
  EXPORT_SYMBOL(sprintf);
  
  /**
   * 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;
  	int qualifier;
  	int base;
  	int field_width;
  	int is_sign = 0;
  
  	while(*fmt && *str) {
  		/* skip any white space in format */
  		/* white space in format matchs any amount of
  		 * white space, including none, in the input.
  		 */
  		if (isspace(*fmt)) {
  			while (isspace(*fmt))
  				++fmt;
  			while (isspace(*str))
  				++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 == '*') {
  			while (!isspace(*fmt) && *fmt)
  				fmt++;
  			while (!isspace(*str) && *str)
  				str++;
  			continue;
  		}
  
  		/* get field width */
  		field_width = -1;
  		if (isdigit(*fmt))
  			field_width = skip_atoi(&fmt);
  
  		/* get conversion qualifier */
  		qualifier = -1;
  		if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
  		    *fmt == 'Z' || *fmt == 'z') {
  			qualifier = *fmt++;
  			if (unlikely(qualifier == *fmt)) {
  				if (qualifier == 'h') {
  					qualifier = 'H';
  					fmt++;
  				} else if (qualifier == 'l') {
  					qualifier = 'L';
  					fmt++;
  				}
  			}
  		}
  		base = 10;
  		is_sign = 0;
  
  		if (!*fmt || !*str)
  			break;
  
  		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 = INT_MAX;
  			/* first, skip leading white space in buffer */
  			while (isspace(*str))
  				str++;
  
  			/* now copy until next white space */
  			while (*str && !isspace(*str) && field_width--) {
  				*s++ = *str++;
  			}
  			*s = '\0';
  			num++;
  		}
  		continue;
  		case 'n':
  			/* return number of characters read so far */
  		{
  			int *i = (int *)va_arg(args,int*);
  			*i = str - buf;
  		}
  		continue;
  		case 'o':
  			base = 8;
  			break;
  		case 'x':
  		case 'X':
  			base = 16;
  			break;
  		case 'i':
                          base = 0;
  		case 'd':
  			is_sign = 1;
  		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.
  		 */
  		while (isspace(*str))
  			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;
  
  		switch(qualifier) {
  		case 'H':	/* that's 'hh' in format */
  			if (is_sign) {
  				signed char *s = (signed char *) va_arg(args,signed char *);
  				*s = (signed char) simple_strtol(str,&next,base);
  			} else {
  				unsigned char *s = (unsigned char *) va_arg(args, unsigned char *);
  				*s = (unsigned char) simple_strtoul(str, &next, base);
  			}
  			break;
  		case 'h':
  			if (is_sign) {
  				short *s = (short *) va_arg(args,short *);
  				*s = (short) simple_strtol(str,&next,base);
  			} else {
  				unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
  				*s = (unsigned short) simple_strtoul(str, &next, base);
  			}
  			break;
  		case 'l':
  			if (is_sign) {
  				long *l = (long *) va_arg(args,long *);
  				*l = simple_strtol(str,&next,base);
  			} else {
  				unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
  				*l = simple_strtoul(str,&next,base);
  			}
  			break;
  		case 'L':
  			if (is_sign) {
  				long long *l = (long long*) va_arg(args,long long *);
  				*l = simple_strtoll(str,&next,base);
  			} else {
  				unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
  				*l = simple_strtoull(str,&next,base);
  			}
  			break;
  		case 'Z':
  		case 'z':
  		{
  			size_t *s = (size_t*) va_arg(args,size_t*);
  			*s = (size_t) simple_strtoul(str,&next,base);
  		}
  		break;
  		default:
  			if (is_sign) {
  				int *i = (int *) va_arg(args, int*);
  				*i = (int) simple_strtol(str,&next,base);
  			} else {
  				unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
  				*i = (unsigned int) simple_strtoul(str,&next,base);
  			}
  			break;
  		}
  		num++;
  
  		if (!next)
  			break;
  		str = next;
  	}
  	return num;
  }
  
  EXPORT_SYMBOL(vsscanf);
  
  /**
   * 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;
  }
  
  EXPORT_SYMBOL(sscanf);
  