#include #include #include enum TYPE { TYPE_INT = 1, TYPE_UINT = 2, TYPE_DEC = 3, TYPE_OCT = 4, TYPE_HEX = 5, TYPE_CHAR = 6, TYPE_STRING = 7, TYPE_PERCENT = 8 }; enum FLAGS { FL_ZERO = 1, /* zero padded */ FL_ALT = 2, /* alternate form (effect is type dependent) */ FL_UPPER = 4 /* uppercase */ }; static void num(char **str, char flags, int base, int fwidth, int x) { int i, y, len = 0; char c; char *s = NULL; /* print the prefix, if the alt flag is set */ if(x && (flags & FL_ALT) && base == 8) s = "0"; if(x && (flags & FL_ALT) && base == 16) s = (flags & FL_UPPER) ? "0X" : "0x"; while(s && *s) *(*str)++ = *s++; /* calculate the number of digits */ y = x; while(y) { y /= base; len++; } /* print the padding characters (if any) */ for(i = 0; i < fwidth - len; i++) *((*str)++) = (flags & FL_ZERO) ? '0' : ' '; *str += len; s = *str; while(len--) { c = (x % base) + '0'; if(c > '9') c += (flags & FL_UPPER) ? 7 : 39; *(--s) = c; x /= base; } } int vsprintf(char *str, char *fmt, va_list ap) { int x, fwidth; char flags; char *s; char *start = str; uint8_t type; while(*fmt) { if(*fmt != '%') { *str++ = *fmt++; continue; } type = 0; flags = 0; fwidth = 0; fmt++; for(;;) { switch(*fmt) { case '#': flags |= FL_ALT; break; case '%': type = TYPE_PERCENT; goto done; case 'c': type = TYPE_CHAR; goto done; case 'd': type = TYPE_DEC; goto done; case 'o': type = TYPE_OCT; goto done; case 's': type = TYPE_STRING; goto done; case 'x': type = TYPE_HEX; goto done; case 'X': type = TYPE_HEX; flags |= FL_UPPER; goto done; default: /* the first zero enables zero-padding */ if(*fmt == '0' && !fwidth) { flags |= FL_ZERO; goto next; } /* subsequent numbers indicate field width */ if(*fmt >= '0' && *fmt <= '9') { fwidth = (fwidth * 10) + (*fmt - '0'); goto next; } goto done; } next: fmt++; } done: fmt++; switch(type) { case TYPE_STRING: s = va_arg(ap, char*); while(*s) *str++ = *s++; break; case TYPE_CHAR: *str++ = va_arg(ap, char); break; case TYPE_DEC: x = va_arg(ap, int); num(&str, flags, 10, fwidth, x); break; case TYPE_OCT: x = va_arg(ap, int); num(&str, flags, 8, fwidth, x); break; case TYPE_HEX: x = va_arg(ap, int); num(&str, flags, 16, fwidth, x); break; case TYPE_PERCENT: *str++ = '%'; break; } } *str = 0; return (str - start); }