#include <string.h>
#include <stdio.h>

#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 SMALL	64		/* use 'abcdef' instead of 'ABCDEF' */

#define is_digit(c)	((c) >= '0' && (c) <= '9')

int skip_atoi(const char **s);

/*

*/

/*
#define do_div(n,base) ({ \
int __res; \
__asm__("divq %%rcx":"=a" (n),"=d" (__res):"0" (n),"1" (0),"c" (base)); \
__res; })
*/

static unsigned long do_div(unsigned long *num, char base) 
{
    unsigned long ret = *num % base;
    *num = *num / base;
    return ret;
}

/*

*/

static char * number(char * str, long num, int base, int size, int precision ,int type);

/*

*/

int skip_atoi(const char **s)
{
	int i=0;

	while (is_digit(**s))
		i = i*10 + *((*s)++) - '0';
	return i;
}

/**
 * number - 对数字进行处理
 * 
 * 
 */
static char *number(char * str, long num, int base, int size, int precision,	int type)
{
	char c,sign,tmp[50];
	const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	int i;

	if (type&SMALL) digits = "0123456789abcdefghijklmnopqrstuvwxyz";
	if (type&LEFT) type &= ~ZEROPAD;
	if (base < 2 || base > 36)
		return 0;
	c = (type & ZEROPAD) ? '0' : ' ' ;
	sign = 0;
	if (type&SIGN && num < 0) {
		sign='-';
		num = -num;
	} else
		sign=(type & PLUS) ? '+' : ((type & SPACE) ? ' ' : 0);
	if (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((unsigned long *)&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;
}


/**
 * vsprintf - 格式化字符串
 * @buf: 要储存的缓冲区
 * @fmt: 格式
 * @args: 参数
 * 
 * 此版本是从MINEOS中移植
 * 
 * 格式：%[标志][最小宽度][.精度][类型长度]类型
 * 
 * 支持的数据类型（type）：
 *      c,s,o,p,x,X,d,i,u,n,%
 * 标志（flags）：
 *      1.-（左对齐）
 *      2.+（输出符号）
 *      3.space（输出值为正时加上空格，为负时加-号）
 *      4.#（type是o,x,X时，添加前缀0,0x,0X。）
 *      5.0(将输出的前面补上0，知道沾满指定列宽位置（不可以搭配使用'-'）)
 * 最小宽度（width）：
 *      1.数值（输出指定宽度）
 *      2.*（在参数中给出宽度）
 * 精度（precision）：
 *      1.以.开头。数值（对于整形[d,i,o,u,x,X],表示输出的最小数字个数，
 *          不足补前导零，超过不截断）
 *      2.对于字符串，表示最大可输出字符数，不足正常输出，超过则阶段。
 *          不显示指定，则默认为0
 *      3.*，用参数指定位数。
 * 类型长度（length）：
 *      h，short int类型
 *      l，long int类型
 *      L，long double类型
 *      Z，unused
 *      
 */
int vsprintf(char * buf,const char *fmt, va_list args)
{
	char * str,*s;
	int flags;
	int field_width;
	int precision;
	int len,i;

	int qualifier;		/* 'h', 'l', 'L' or 'Z' for integer fields */

	for(str = buf; *fmt; fmt++)
	{
        
		if(*fmt != '%')
		{
			*str++ = *fmt;
			continue;
		}
		flags = 0;
		repeat:
			fmt++;
			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++;
				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++;
					precision = va_arg(args, int);
				}
				if(precision < 0)
					precision = 0;
			}
			
			qualifier = -1;
			if(*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z')
			{	
				qualifier = *fmt;
				fmt++;
			}
							
			switch(*fmt)
			{
				case 'c':

					if(!(flags & LEFT))
						while(--field_width > 0)
							*str++ = ' ';
					*str++ = (unsigned char)va_arg(args, int);
					while(--field_width > 0)
						*str++ = ' ';
					break;

				case 's':
				
					s = va_arg(args,char *);
					if(!s)
						s = '\0';
					len = strlen(s);
					if(precision < 0)
						precision = len;
					else if(len > precision)
						len = precision;
					
					if(!(flags & LEFT))
						while(len < field_width--)
							*str++ = ' ';
					for(i = 0;i < len ;i++)
						*str++ = *s++;
					while(len < field_width--)
						*str++ = ' ';
					break;

				case 'o':
					
					if(qualifier == 'l')
						str = number(str,va_arg(args,unsigned long),8,field_width,precision,flags);
					else
						str = number(str,va_arg(args,unsigned int),8,field_width,precision,flags);
					break;

				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);
					break;

				case 'x':

					flags |= SMALL;

				case 'X':

					if(qualifier == 'l')
						str = number(str,va_arg(args,unsigned long),16,field_width,precision,flags);
					else
						str = number(str,va_arg(args,unsigned int),16,field_width,precision,flags);
					break;

				case 'd':
				case 'i':

					flags |= SIGN;
				case 'u':

					if(qualifier == 'l')
						str = number(str,va_arg(args,long),10,field_width,precision,flags);
					else
						str = number(str,va_arg(args,int),10,field_width,precision,flags);
					break;

				case 'n':
					
					if(qualifier == 'l')
					{
						long *ip = va_arg(args,long *);
						*ip = (str - buf);
					}
					else
					{
						int *ip = va_arg(args,int *);
						*ip = (str - buf);
					}
					break;

				case '%':
					
					*str++ = '%';
					break;

				default:

					*str++ = '%';	
					if(*fmt)
						*str++ = *fmt;
					else
						fmt--;
					break;
			}

	}
	*str = '\0';
	return str - buf;
}

/**
 * vsprintf_old - 格式化字符串到缓冲区
 * 
 * xbook旧版本
 */
int vsprintf_old(char *buf, const char *fmt, va_list args)
{
	char*	p;

	va_list	p_next_arg = args;
	int	m;

	char	inner_buf[STR_DEFAULT_LEN];
	char	cs;
	int	align_nr;

	for (p=buf;*fmt;fmt++) {
		if (*fmt != '%') {
			*p++ = *fmt;
			continue;
		}
		else {		/* a format string begins */
			align_nr = 0;
		}

		fmt++;

		if (*fmt == '%') {
			*p++ = *fmt;
			continue;
		}
		else if (*fmt == '0') {
			cs = '0';
			fmt++;
		}
		else {
			cs = ' ';
		}
		while (((unsigned char)(*fmt) >= '0') && ((unsigned char)(*fmt) <= '9')) {
			align_nr *= 10;
			align_nr += *fmt - '0';
			fmt++;
		}

		char * q = inner_buf;
		
		memset(q, 0, sizeof(inner_buf));

		switch (*fmt) {
		case 'c':
			*q++ = *((char*)p_next_arg);
			p_next_arg += 4;
			break;
		case 'x':
			m = *((int*)p_next_arg);

			itoa16_align(q, m);	//对齐的16进制数
			//itoa(&q, m, 16);
			p_next_arg += 4;
			break;
		case 'd':
			m = *((int*)p_next_arg);
			if (m < 0) {
				m = m * (-1);
				*q++ = '-';
			}
			itoa(&q, m, 10);
			p_next_arg += 4;
			break;
		case 's':
			strcpy(q, (*((char**)p_next_arg)));
			q += strlen(*((char**)p_next_arg));
			p_next_arg += 4;
			break;
		default:
			break;
		}

		int k;
		for (k = 0; k < ((align_nr > strlen(inner_buf)) ? (align_nr - strlen(inner_buf)) : 0); k++) {
			*p++ = cs;
		}
		q = inner_buf;
		while (*q) {
			*p++ = *q++;
		}
	}

	*p = 0;

	return (p - buf);
}


/**
 * vsnprintf - 格式化字符串
 * @buf: 要储存的缓冲区
 * @buflen: 缓冲区长度
 * @fmt: 格式
 * @args: 参数
 * 
 * 有长度限定的字符串格式化，每次只需要从vsprintf复制过来修改参数即可
 */
int vsnprintf(char * buf, int buflen, const char *fmt, va_list args)
{
	char * str,*s;
	int flags;
	int field_width;
	int precision;
	int len,i;

	int qualifier;		/* 'h', 'l', 'L' or 'Z' for integer fields */

    /* 需要添加长度判断，buflen */
	for(str = buf; *fmt && buflen--; fmt++)
	{
        
		if(*fmt != '%')
		{
			*str++ = *fmt;
			continue;
		}
		flags = 0;
		repeat:
        fmt++;
        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++;
            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++;
                precision = va_arg(args, int);
            }
            if(precision < 0)
                precision = 0;
        }
        
        qualifier = -1;
        if(*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z')
        {	
            qualifier = *fmt;
            fmt++;
        }
                        
        switch(*fmt)
        {
        case 'c':

            if(!(flags & LEFT))
                while(--field_width > 0)
                    *str++ = ' ';
            *str++ = (unsigned char)va_arg(args, int);
            while(--field_width > 0)
                *str++ = ' ';
            break;

        case 's':
        
            s = va_arg(args,char *);
            if(!s)
                s = '\0';
            len = strlen(s);
            if(precision < 0)
                precision = len;
            else if(len > precision)
                len = precision;
            
            if(!(flags & LEFT))
                while(len < field_width--)
                    *str++ = ' ';
            for(i = 0;i < len ;i++)
                *str++ = *s++;
            while(len < field_width--)
                *str++ = ' ';
            break;

        case 'o':
            
            if(qualifier == 'l')
                str = number(str,va_arg(args,unsigned long),8,field_width,precision,flags);
            else
                str = number(str,va_arg(args,unsigned int),8,field_width,precision,flags);
            break;

        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);
            break;

        case 'x':

            flags |= SMALL;

        case 'X':

            if(qualifier == 'l')
                str = number(str,va_arg(args,unsigned long),16,field_width,precision,flags);
            else
                str = number(str,va_arg(args,unsigned int),16,field_width,precision,flags);
            break;

        case 'd':
        case 'i':

            flags |= SIGN;
        case 'u':

            if(qualifier == 'l')
                str = number(str,va_arg(args,long),10,field_width,precision,flags);
            else
                str = number(str,va_arg(args,int),10,field_width,precision,flags);
            break;

        case 'n':
            
            if(qualifier == 'l')
            {
                long *ip = va_arg(args,long *);
                *ip = (str - buf);
            }
            else
            {
                int *ip = va_arg(args,int *);
                *ip = (str - buf);
            }
            break;

        case '%':
            
            *str++ = '%';
            break;

        default:

            *str++ = '%';	
            if(*fmt)
                *str++ = *fmt;
            else
                fmt--;
            break;
        }
	}
	*str = '\0';
	return str - buf;
}


int sprintf(char *buf, const char *fmt, ...)
{
	va_list arg = (va_list)((char*)(&fmt) + 4);        /* 4 是参数 fmt 所占堆栈中的大小 */
	return vsprintf(buf, fmt, arg);
}

int snprintf(char *buf, int buflen, const char *fmt, ...)
{
	va_list arg = (va_list)((char*)(&fmt) + 4);        /* 4 是参数 fmt 所占堆栈中的大小 */
	return vsnprintf(buf, buflen, fmt, arg);
}