#include #include #include #include #include const char HEX_SET[0x10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; #define FILE_WRITE(_f, _s, _l, _r) \ { \ size_t _rc = _f->write(_f, (const unsigned char *)_s, _l); \ if ((size_t) - 1 == _rc) \ assert(0); \ *(int *)(_r) += _rc; \ } int fprint_num(FILE *f, long long n, int base, char *char_set, int prefix, int zero_padding, int right_padding) { int c = 0; char str[32]; int i = 0; int is_signed = 0; if (n < 0) { is_signed = 1; n *= -1; } if (0 == n) { str[i] = char_set[0]; i++; } else { for (; n != 0 && i < 32; i++, n /= base) { str[i] = char_set[(n % base)]; } } if (is_signed) { str[i] = '-'; i++; } char t = (zero_padding) ? '0' : ' '; int orig_i = i; if (!right_padding) { for (; prefix - orig_i > 0; prefix--) { FILE_WRITE(f, &t, 1, &c); } } for (i--; i >= 0; i--) { FILE_WRITE(f, &(str[i]), 1, &c); } if (right_padding) { for (; prefix - orig_i > 0; prefix--) { FILE_WRITE(f, &t, 1, &c); } } return c; } int fprint_int(FILE *f, long long n, int prefix, int zero_padding, int right_padding) { return fprint_num(f, n, 10, "0123456789", prefix, zero_padding, right_padding); } int fprint_hex(FILE *f, long long n, int prefix, int zero_padding, int right_padding) { return fprint_num(f, n, 16, "0123456789abcdef", prefix, zero_padding, right_padding); } int fprint_octal(FILE *f, long long n, int prefix, int zero_padding, int right_padding) { return fprint_num(f, n, 8, "012345678", prefix, zero_padding, right_padding); } int print_string(FILE *f, const char *s, int *rc, int prefix, int right_padding, int precision) { int l = strlen(s); char t = ' '; int c = 0; if (!right_padding) { if (prefix) { assert(-1 == precision); // FIXME: Is this correct? } for (; prefix - l > 0; prefix--) { FILE_WRITE(f, &t, 1, &c); } } int bl = precision; for (; *s; s++, (*rc)++) { if (precision != -1) { if (0 == bl) { break; } bl--; } int r = 0; FILE_WRITE(f, (const unsigned char *)s, 1, &r); } if (right_padding) { assert(-1 == precision); // FIXME: Is this correct? for (; prefix - l > 0; prefix--) { FILE_WRITE(f, &t, 1, &c); } } (*rc) += c; return 0; } int parse_precision(const char **fmt) { const char *s = *fmt; int rc = 0; for (int i = 0;; i++, s++) { if ('\0' == *s) { break; } const char c = *s; if ('*' == c) { assert(i == 0); return -1; } else if (!(c >= '0' && c <= '9')) { s--; break; } rc *= 10; rc += c - '0'; } *fmt = s; return rc; } int vfprintf(FILE *f, const char *fmt, va_list ap) { int rc = 0; const char *s = fmt; int prefix = 0; int long_level = 0; int zero_padding = 0; int right_padding = 0; int cont = 0; int precision = -1; for (; *s; s++) { if (!cont && '%' != *s) { FILE_WRITE(f, (const unsigned char *)s, 1, &rc); continue; } if (!cont) { cont = 1; continue; } if ('\0' == *s) { break; } switch (*s) { case '.': s++; assert('\0' != *s); precision = parse_precision(&s); assert('\0' != *s); if (-1 == precision) { precision = va_arg(ap, int); } cont = 1; break; case '0': prefix *= 10; if (0 == prefix) { zero_padding = 1; } cont = 1; break; case '-': assert(0 == prefix); right_padding = 1; cont = 1; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': prefix *= 10; prefix += (*s) - '0'; cont = 1; break; case 'l': long_level++; assert(long_level <= 2); break; case 'i': case 'd': if (-1 != precision) { zero_padding = 1; prefix = precision; right_padding = 0; } if (2 == long_level) { rc += fprint_int(f, va_arg(ap, long long), prefix, zero_padding, right_padding); } else if (1 == long_level) { rc += fprint_int(f, va_arg(ap, long long), prefix, zero_padding, right_padding); } else { rc += fprint_int(f, va_arg(ap, int), prefix, zero_padding, right_padding); } long_level = 0; cont = 0; break; case 'u': assert(-1 == precision); rc += fprint_int(f, va_arg(ap, unsigned int), prefix, zero_padding, right_padding); cont = 0; break; case 's': { assert(!zero_padding); // this is not supported to strings char *a = va_arg(ap, char *); if (!a) { if (-1 == print_string(f, "(NULL)", &rc, prefix, right_padding, precision)) { return -1; } cont = 0; break; } if (-1 == print_string(f, a, &rc, prefix, right_padding, precision)) { return -1; } cont = 0; break; } case 'p': // TODO: Print this out in a nicer way case 'x': assert(-1 == precision); rc += fprint_hex(f, va_arg(ap, const uint32_t), prefix, zero_padding, right_padding); cont = 0; break; case 'o': assert(-1 == precision); rc += fprint_octal(f, va_arg(ap, const uint32_t), prefix, zero_padding, right_padding); cont = 0; break; case '%': { FILE_WRITE(f, (const unsigned char *)"%", 1, &rc); cont = 0; break; } case 'c': { char c = va_arg(ap, const int); FILE_WRITE(f, (const unsigned char *)&c, 1, &rc); cont = 0; break; } default: printf("got %c but that is not supported by printf\n", *s); assert(0); break; } if (!cont) { prefix = 0; zero_padding = right_padding = 0; precision = -1; } } fflush(f); return rc; }