/*
* linux/kernel/vsprintf.c
*
* (C) 1991 Linus Torvalds
*/

/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
* Wirzenius wrote this portably, Torvalds fucked it up :-)
*/

#include <stdarg.h>		// ׼ͷļԺʽбҪ˵-
// (va_list)(va_start, va_arg va_end)
// vsprintfvprintfvfprintf 
#include <string.h>		// ַͷļҪһЩйַǶ뺯

/* we use this so that we can do without the ctype library */
/* ʹĶ壬ǾͿԲʹctype  */
#define is_digit(c) ((c) >= '' && (c) <= '9')	// жַǷַ

// úִַתִָָ룬ǽֵָ뽫ǰơ
static int
skip_atoi (const char **s)
{
  int i = 0;

  while (is_digit (**s))
    i = i * 10 + *((*s)++) - '';
  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 */	/* 0x */
#define SMALL 64 /* use 'abcdef' instead of 'ABCDEF' */	/* ʹСдĸ */

// 룺n Ϊbase Ϊn Ϊֵ̣Ϊ
// μ4.5.3 йǶϢ
#define do_div(n,base) ({ \
int __res; \
__asm__( "divl %4": "=a" (n), "=d" (__res): "" (n), "1" (0), "r" (base)); \
__res; })

// תΪָƵַ
// 룺num-base-ƣsize-ַȣprecision-ֳ()type-ѡ
// str ַָ롣
static char *
number (char *str, int num, int base, int size, int precision, int type)
{
  char c, sign, tmp[36];
  const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  int i;

// type ָСдĸСдĸ
// ָҪ߽磩е־
// ƻС2 36˳Ҳֻܴ2-32 ֮
  if (type & SMALL)
    digits = "0123456789abcdefghijklmnopqrstuvwxyz";
  if (type & LEFT)
    type &= ~ZEROPAD;
  if (base < 2 || base > 36)
    return 0;
// ָҪ㣬ַc='0'Ҳ''c ڿոַ
// ָǴֵnum С0÷űsign=ţʹnum ȡֵ
// ָǼӺţsign=Ӻţʹո־sign=ո񣬷0
  c = (type & ZEROPAD) ? '' : ' ';
  if (type & SIGN && num < 0)
    {
      sign = '-';
      num = -num;
    }
  else
    sign = (type & PLUS) ? '+' : ((type & SPACE) ? ' ' : 0);
// ţֵ1ָתʮƿټ2 λ(0x)
// ڰ˽ƿȼ1ڰ˽תǰһ㣩
  if (sign)
    size--;
  if (type & SPECIAL)
    if (base == 16)
      size -= 2;
    else if (base == 8)
      size--;
// ֵnum Ϊ0ʱַ='0'ݸĻֵnum תַʽ
  i = 0;
  if (num == 0)
    tmp[i++] = '';
  else
    while (num != 0)
      tmp[i++] = digits[do_div (num, base)];
// ֵַھֵ򾫶ֵչΪֵָ
// ֵsize ȥڴֵַĸ
  if (i > precision)
    precision = i;
  size -= precision;
// ʼγҪתʱַstr С
// û(ZEROPAD)루־str 
// ʣֵָĿոλš
  if (!(type & (ZEROPAD + LEFT)))
    while (size-- > 0)
      *str++ = ' ';
  if (sign)
    *str++ = sign;
// ָתڰ˽תͷһλһ'0'ʮ'0x'
  if (type & SPECIAL)
    if (base == 8)
      *str++ = '';
    else if (base == 16)
      {
	*str++ = '';
	*str++ = digits[33];	// 'X''x'
      }
// û룩־ʣдc ַ'0'ո񣩣51 С
  if (!(type & LEFT))
    while (size-- > 0)
      *str++ = c;
// ʱi ֵnum ָָСھֵstr з루ֵ-i'0'
  while (i < precision--)
    *str++ = '';
// תֵõַstr Сi 
  while (i-- > 0)
    *str++ = tmp[i];
// ֵԴ㣬ʾͱ־־־ʣзո
  while (size-- > 0)
    *str++ = ' ';
  return str;			// תõַ
}

// 溯͸ʽַС
// ΪںʹøʽLinus ںʵ˸C ׼
// вfmt Ǹʽַargs Ǹ仯ֵbuf ַ
// μбйظʽתַĽܡ
int
vsprintf (char *buf, const char *fmt, va_list args)
{
  int len;
  int i;
  char *str;			// ڴתеַ
  char *s;
  int *ip;

  int flags;			/* flags to number() */
/* number()ʹõı־ */
  int field_width;		/* width of output field */
/* ֶο*/
  int precision;		/* min. # of digits for integers; max
				   number of chars for from string */
/* min. ָmax. ַַ */
  int qualifier;		/* 'h', 'l', or 'L' for integer fields */
/* 'h', 'l','L'ֶ */
// ȽַָָbufȻɨʽַԸʽתָʾӦĴ
  for (str = buf; *fmt; ++fmt)
    {
// ʽתָʾַ'%'ʼfmt ʽַɨ'%'ѰҸʽתַĿʼ
// Ǹʽָʾһַδstr
      if (*fmt != '%')
	{
	  *str++ = *fmt;
	  continue;
	}

// ȡøʽָʾַеı־򣬲־flags С
/* 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 '':
	  flags |= ZEROPAD;
	  goto repeat;		// Ҫ('0')
	}

// ȡǰֶοֵfield_width СֱֵȡΪֵ
// ַ'*'ʾһָȡ˵va_arg ȡֵʱֵ
// С0øʾб־'-'־룩˻ڱ־ñ־
// ֶοֵȡΪֵ
/* get field width */
      field_width = -1;
      if (is_digit (*fmt))
	field_width = skip_atoi (&fmt);
      else if (*fmt == '*')
	{
/* it's the next argument */
	  field_width = va_arg (args, int);
	  if (field_width < 0)
	    {
	      field_width = -field_width;
	      flags |= LEFT;
	    }
	}

// δ룬ȡʽתľ򣬲precision Сʼı־'.'
// 䴦ơֱֵȡΪֵ
// ַ'*'ʾһָȡ˵va_arg ȡֵʱֵС0
// ֶξֵȡΪֵ
/* get the precision */
      precision = -1;
      if (*fmt == '.')
	{
	  ++fmt;
	  if (is_digit (*fmt))
	    precision = skip_atoi (&fmt);
	  else if (*fmt == '*')
	    {
/* it's the next argument */
	      precision = va_arg (args, int);
	    }
	  if (precision < 0)
	    precision = 0;
	}

// δηqualifer h,l,L ĺμб˵
/* get the conversion qualifier */
      qualifier = -1;
      if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L')
	{
	  qualifier = *fmt;
	  ++fmt;
	}

// תָʾ
      switch (*fmt)
	{
// תָʾ'c'ʾӦӦַʱ־룬ֶǰ
// ֵ-1 ոַȻٷַ򻹴0ʾΪ룬
// ַӿֵ-1 ոַ
	case 'c':
	  if (!(flags & LEFT))
	    while (--field_width > 0)
	      *str++ = ' ';
	  *str++ = (unsigned char) va_arg (args, int);
	  while (--field_width > 0)
	    *str++ = ' ';
	  break;

// תָʾ's'ʾӦַȡַĳȣ䳬˾ֵ
// չ=ַȡʱ־룬ֶǰ(ֵ-ַ)
// ոַȻٷַ򻹴0ʾΪ룬ڲַ
// (ֵ-ַ)ոַ
	case 's':
	  s = va_arg (args, char *);
	  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;

// ʽת'o'ʾ轫ӦĲתɰ˽ַnumber()
	case 'o':
	  str = number (str, va_arg (args, unsigned long), 8,
			field_width, precision, flags);
	  break;

// ʽת'p'ʾӦһָ͡ʱòûÿĬϿ
// Ϊ8Ҫ㡣Ȼnumber()д
	case 'p':
	  if (field_width == -1)
	    {
	      field_width = 8;
	      flags |= ZEROPAD;
	    }
	  str = number (str,
			(unsigned long) va_arg (args, void *), 16,
			field_width, precision, flags);
	  break;

// ʽתָʾ'x''X'ʾӦҪӡʮ'x'ʾСдĸʾ
	case 'x':
	  flags |= SMALL;
	case 'X':
	  str = number (str, va_arg (args, unsigned long), 16,
			field_width, precision, flags);
	  break;

// ʽתַ'd','i''u'ʾӦ'd', 'i'Ҫ
// ű־'u'޷
	case 'd':
	case 'i':
	  flags |= SIGN;
	case 'u':
	  str = number (str, va_arg (args, unsigned long), 10,
			field_width, precision, flags);
	  break;

// ʽתָʾ'n'ʾҪѵĿǰΪֹתַ浽Ӧָָλ
	  С
// va_arg()ȡøòָ룬ȻѾתõַָָλá
	case 'n':
	  ip = va_arg (args, int *);
	  *ip = (str - buf);
	  break;

// ʽת'%'ʾʽַдֱӽһ'%'дС
// ʽתλôַҲֱӽַдУص107 м
// ʽַʾѾʽַĽβ˳ѭ
	default:
	  if (*fmt != '%')
	    *str++ = '%';
	  if (*fmt)
	    *str++ = *fmt;
	  else
	    --fmt;
	  break;
	}
    }
  *str = '\0';			// תõַβnull
  return str - buf;		// תõֵַ
}
