Skip to content
Snippets Groups Projects
vsprintf.c 7.44 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*
     *  linux/lib/vsprintf.c
     *
     *  Copyright (C) 1991, 1992  Linus Torvalds
     */
    
    /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
    /*
     * Wirzenius wrote this portably, Torvalds fucked it up :-)
     */
    
    #include <stdarg.h>
    #include <linux/types.h>
    #include <linux/string.h>
    #include <linux/ctype.h>
    
    #include <common.h>
    #if !defined (CONFIG_PANIC_HANG)
    #include <command.h>
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*cmd_boot.c*/
    extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    #endif
    
    unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
    {
    	unsigned long result = 0,value;
    
    	if (*cp == '0') {
    		cp++;
    		if ((*cp == 'x') && isxdigit(cp[1])) {
    			base = 16;
    			cp++;
    		}
    		if (!base) {
    			base = 8;
    		}
    	}
    	if (!base) {
    		base = 10;
    	}
    	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;
    }
    
    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 CFG_64BIT_STRTOUL
    unsigned long long simple_strtoull (const char *cp, char **endp, unsigned int base)
    {
    	unsigned long long result = 0, value;
    
    	if (*cp == '0') {
    		cp++;
    		if ((*cp == 'x') && isxdigit (cp[1])) {
    			base = 16;
    			cp++;
    		}
    		if (!base) {
    			base = 8;
    		}
    	}
    	if (!base) {
    		base = 10;
    	}
    	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;
    }
    #endif /* CFG_64BIT_STRTOUL */
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    /* we use this so that we can do without the ctype library */
    #define is_digit(c)	((c) >= '0' && (c) <= '9')
    
    static int skip_atoi(const char **s)
    {
    	int i=0;
    
    	while (is_digit(**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' */
    
    #define do_div(n,base) ({ \
    
    	int __res; \
    	__res = ((unsigned long) n) % (unsigned) base; \
    	n = ((unsigned long) n) / (unsigned) base; \
    	__res; \
    })
    
    #if CFG_64BIT_VSPRINTF
    static char * number(char * str, long long num, int base, int size, int precision ,int type)
    #else
    static char * number(char * str, long num, int base, int size, int precision ,int type)
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    	char c,sign,tmp[66];
    	const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
    	int i;
    
    	if (type & LARGE)
    		digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    	if (type & LEFT)
    		type &= ~ZEROPAD;
    	if (base < 2 || base > 36)
    		return 0;
    	c = (type & ZEROPAD) ? '0' : ' ';
    	sign = 0;
    	if (type & SIGN) {
    		if (num < 0) {
    			sign = '-';
    			num = -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)
    			*str++ = ' ';
    	if (sign)
    		*str++ = sign;
    	if (type & SPECIAL) {
    		if (base==8)
    			*str++ = '0';
    		else if (base==16) {
    			*str++ = '0';
    			*str++ = digits[33];
    		}
    	}
    	if (!(type & LEFT))
    		while (size-- > 0)
    			*str++ = c;
    	while (i < precision--)
    		*str++ = '0';
    	while (i-- > 0)
    		*str++ = tmp[i];
    	while (size-- > 0)
    		*str++ = ' ';
    	return str;
    }
    
    /* Forward decl. needed for IP address printing stuff... */
    int sprintf(char * buf, const char *fmt, ...);
    
    int vsprintf(char *buf, const char *fmt, va_list args)
    {
    	int len;
    
    #if CFG_64BIT_VSPRINTF
    	unsigned long long num;
    #else
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	unsigned long num;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	int i, base;
    	char * str;
    	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 'q' for integer fields */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	for (str=buf ; *fmt ; ++fmt) {
    		if (*fmt != '%') {
    			*str++ = *fmt;
    			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 (is_digit(*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 (is_digit(*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 == 'q') {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			qualifier = *fmt;
    			++fmt;
    		}
    
    		/* default base */
    		base = 10;
    
    		switch (*fmt) {
    		case 'c':
    			if (!(flags & LEFT))
    				while (--field_width > 0)
    					*str++ = ' ';
    			*str++ = (unsigned char) va_arg(args, int);
    			while (--field_width > 0)
    				*str++ = ' ';
    			continue;
    
    		case 's':
    			s = va_arg(args, char *);
    			if (!s)
    				s = "<NULL>";
    
    			len = strnlen(s, precision);
    
    			if (!(flags & LEFT))
    				while (len < field_width--)
    					*str++ = ' ';
    			for (i = 0; i < len; ++i)
    				*str++ = *s++;
    			while (len < field_width--)
    				*str++ = ' ';
    			continue;
    
    		case 'p':
    			if (field_width == -1) {
    				field_width = 2*sizeof(void *);
    				flags |= ZEROPAD;
    			}
    			str = number(str,
    				(unsigned long) va_arg(args, void *), 16,
    				field_width, precision, flags);
    			continue;
    
    
    		case 'n':
    			if (qualifier == 'l') {
    				long * ip = va_arg(args, long *);
    				*ip = (str - buf);
    			} else {
    				int * ip = va_arg(args, int *);
    				*ip = (str - buf);
    			}
    			continue;
    
    		case '%':
    			*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:
    			*str++ = '%';
    			if (*fmt)
    				*str++ = *fmt;
    			else
    				--fmt;
    			continue;
    		}
    
    #if CFG_64BIT_VSPRINTF
    		if (qualifier == 'q')  /* "quad" for 64 bit variables */
    			num = va_arg(args, unsigned long long);
    		else
    #endif
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    		if (qualifier == 'l')
    			num = va_arg(args, unsigned long);
    		else if (qualifier == 'h') {
    			num = (unsigned short) va_arg(args, int);
    			if (flags & SIGN)
    				num = (short) num;
    		} else if (flags & SIGN)
    			num = va_arg(args, int);
    		else
    			num = va_arg(args, unsigned int);
    		str = number(str, num, base, field_width, precision, flags);
    	}
    	*str = '\0';
    	return str-buf;
    }
    
    int sprintf(char * buf, const char *fmt, ...)
    {
    	va_list args;
    	int i;
    
    	va_start(args, fmt);
    	i=vsprintf(buf,fmt,args);
    	va_end(args);
    	return i;
    }
    
    void panic(const char *fmt, ...)
    {
    	va_list	args;
    	va_start(args, fmt);
    
    	vprintf(fmt, args);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	putc('\n');
    	va_end(args);
    #if defined (CONFIG_PANIC_HANG)
    	hang();
    #else
    	udelay (100000);	/* allow messages to go out */
    	do_reset (NULL, 0, 0, NULL);
    #endif
    }