/* ---------- sprintf function for mysql that can handle blobs---------- */
/*      This code is in public domain ;  Originally By Chunhua Liu       */


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 *str, long num, int base, int size, int precision,
int type)
{
 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)
 {
  int __res;
  __res = ((unsigned long) num) % (unsigned) base;
  num = ((unsigned long) num) / (unsigned) base;
  tmp[i++] = digits[__res];
 }
 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;
}

static size_t strnlen(const char * s, size_t count)
{
 const char *sc;

 for (sc = s; count-- && *sc != '\0'; ++sc)
  /* nothing */;
    return sc - s;
}

int myvsnprintf(char *buf, size_t maxlen, const char *fmt, va_list args)
{
 int len;
 unsigned long num;
 int i, base;
 char *str;
 const char *s;
 char esc;

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

 for (str=buf; *fmt; ++fmt)
 {
  if (str-buf >= maxlen)
  {
   buf[maxlen-1] = '\0'; // truncated
   return -1;
  }

  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 (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')
  {
   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++ = ' ';
    if (str-buf >= maxlen)
    {
     buf[maxlen-1] = '\0'; // truncated
     return -1;
    }
   }

   for (i = 0; i < len; ++i)
   {
    *str++ = *s++;
    if (str-buf >= maxlen)
    {
     buf[maxlen-1] = '\0'; // truncated
     return -1;
    }
   }

   while (len < field_width--)
   {
    *str++ = ' ';
    if (str-buf >= maxlen)
    {
     buf[maxlen-1] = '\0'; // truncated
     return -1;
    }
   }

   continue;

  case 'b':
   s = va_arg(args, char *);
   if (!s) s = "<NULL>";

   len = precision;

   for (i = 0; i < len; ++i)
   {
    esc = 0;
    switch (*s)
    {
    case 0:    /* Must be escaped for 'mysql' */
     *str++= '\\';
     esc = '0';
     break;
    case '\n':    /* Must be escaped for logs */
     *str++= '\\';
     esc = 'n';
     break;
    case '\r':
     *str++= '\\';
     esc = 'r';
     break;
    case '\\':
     *str++= '\\';
     esc = '\\';
     break;
    case '\'':
     *str++= '\\';
     esc = '\'';
     break;
    case '"':    /* Better safe than sorry */
     *str++= '\\';
     esc = '"';
     break;
    case '\032':   /* This gives problems on Win32 */
     *str++= '\\';
     esc = 'Z';
     break;
    default:
     *str++= *s;
    }
    s++;
    if (str-buf >= maxlen)
    {
     buf[maxlen-1] = '\0'; // truncated
     return -1;
    }
    if ( esc )
    {
     *str++ = esc;
     if (str-buf >= maxlen)
     {
      buf[maxlen-1] = '\0'; // truncated
      return -1;
     }
    }
   }

   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 (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 mysnprintf(char * buf, size_t maxlen, const char *fmt, ...)
{
 va_list args;
 int i;

 va_start(args, fmt);
 i=myvsnprintf(buf, maxlen, fmt, args);
 va_end(args);
 return i;
}


int main()
{
 char buf[10];
 char bin[20]="\0'\"\r\n\01234567890";

 printf("12345678901234567890\n");
 mysnprintf(buf, 10, "%.10b", bin); /* use type 'b' to work with blob field,
the dot is needed */
 printf("%s\n", buf);
 mysnprintf(buf, 10, "%.*b", 3, bin);
 printf(buf);
}

/*---- source end here */