#include #include #include #include #include /* Internal buffer, used when no buffer is specified for output */ #define _PRINTF_BUFSIZE 64 static char __printf_buffer[_PRINTF_BUFSIZE]; /* Notation for a letter that cannot be used as a specifier */ #define _PRINTF_USED ((__printf_formatter_t *)1) /* Default formatter functions */ __printf_formatter_t __printf_format_c; __printf_formatter_t __printf_format_di; __printf_formatter_t __printf_format_m; __printf_formatter_t __printf_format_n; __printf_formatter_t __printf_format_ouxX; __printf_formatter_t __printf_format_p; __printf_formatter_t __printf_format_s; static __printf_formatter_t *__printf_formatters[52] = { /* Uppercase letters */ NULL, /* A: Hexadecimal floating-point */ NULL, _PRINTF_USED, /* C: Deprecated synonym for lc */ NULL, NULL, /* E: Exponent floating-point */ NULL, /* F: Floating-point */ NULL, /* G: General floating-point */ NULL, _PRINTF_USED, /* I: Locale-aware digits */ NULL, NULL, _PRINTF_USED, /* L: long double size */ NULL, NULL, NULL, NULL, NULL, NULL, _PRINTF_USED, /* S: Deprecated synonym for ls */ NULL, NULL, NULL, NULL, __printf_format_ouxX, /* X: Hexadecimal integer */ NULL, _PRINTF_USED, /* Z: Old synonym for z */ /* Lowercase letters */ NULL, /* a: Hexadecimal floating-point */ NULL, __printf_format_c, /* c: Character */ __printf_format_di, /* d: Decimal integer */ NULL, /* e: Exponent floating-point */ NULL, /* f: Floating-point */ NULL, /* g: General floating-point */ _PRINTF_USED, /* h: short or char size */ __printf_format_di, /* i: Integer */ _PRINTF_USED, /* j: intmax_t size */ NULL, /* k: _ */ _PRINTF_USED, /* l: long or long long size */ __printf_format_m, /* m: Error message from errno */ __printf_format_n, /* n: Number of characters written */ __printf_format_ouxX, /* o: Octal integer */ __printf_format_p, /* p: Pointer */ _PRINTF_USED, /* q: Nonstandard synonym for ll */ NULL, /* r: _ */ __printf_format_s, /* s: String */ _PRINTF_USED, /* t: ptrdiff_t size */ __printf_format_ouxX, /* u: Unsigned decimal integer */ NULL, /* v: _ */ NULL, /* w: _ */ __printf_format_ouxX, /* x: Hexadecimal integer */ NULL, /* y: _ */ _PRINTF_USED, /* z: size_t size */ }; void __printf_register(int spec, __printf_formatter_t *format) { if(isupper(spec)) spec = spec - 'A'; else if(islower(spec)) spec = spec - 'a' + 26; else return; if(__printf_formatters[spec] == _PRINTF_USED || format == _PRINTF_USED) return; __printf_formatters[spec] = format; } void __printf_flush(struct __printf_output *out) { /* Update the number of flushed characters */ out->count += out->ptr - out->buffer; /* String: write a NUL at the end even if overriding a character */ if(out->str && out->str != __printf_buffer) { char *nul = out->ptr; if(out->limit - 1 < nul) nul = out->limit - 1; *nul = 0x00; } /* File pointer: output with fwrite */ else if(out->fp) { // fwrite(out->buffer, out->ptr - out->buffer, 1, out->fp); } /* File pointer: output with write */ else if(out->fd) { // write(fd, out->buffer, out->ptr - out->buffer); } /* Switch to the internal buffer (when writing to a string that string is now full, and when writing to files we've always been there) */ out->buffer = __printf_buffer; out->limit = __printf_buffer + _PRINTF_BUFSIZE; out->ptr = out->buffer; } /* Parse format strings. */ static struct __printf_format parse_fmt(char const * restrict *options_ptr) { /* No options enabled by default, set the size to int */ struct __printf_format opt = { .size = sizeof(int), .precision = -1 }; /* This function acts as a deterministic finite automaton */ enum { basic, /* Reading option characters */ length, /* Reading length digits */ precision, /* Reading precision digits */ } state = basic; char const *options = *options_ptr; /* Previous size letter (used to keep track of hh and ll) */ int size_letter = 0; for(int c; (c = *options); options++) { int index = -1; if(isupper(c)) index = c - 'A'; if(islower(c)) index = c - 'a' + 26; if(index >= 0 && __printf_formatters[index] != _PRINTF_USED) break; if(c == '.') { state = precision; opt.precision = 0; continue; } else if(state == length && c >= '0' && c <= '9') { opt.length = opt.length * 10 + (c - '0'); continue; } else if(state == precision && c >= '0' && c <= '9') { opt.precision = opt.precision * 10 + (c - '0'); continue; } /* Remain in basic state mode */ state = basic; if(c == '#') opt.alternative = 1; if(c == ' ') opt.blank_sign = 1; if(c == '+') opt.force_sign = 1; /* Alignment options, including priority */ if((c == '-' || c == '0') && opt.alignment != '0') { opt.alignment = c; } /* Data size */ if(strchr("hlzjtL", c)) { if(c == 'h' && size_letter == 'h') opt.size = sizeof(char); else if(c == 'h') opt.size = sizeof(short); else if(c == 'l' && size_letter == 'l') opt.size = sizeof(long long); else if(c == 'l') opt.size = sizeof(long); else if(c == 'z') opt.size = sizeof(size_t); else if(c == 'j') opt.size = sizeof(intmax_t); else if(c == 't') opt.size = sizeof(ptrdiff_t); else if(c == 'L') opt.size = sizeof(long double); size_letter = c; } if(c >= '1' && c <= '9') state = length, options--; } *options_ptr = options; return opt; } /* The formatted printer. */ int __printf( struct __printf_output * restrict out, char const * restrict format, va_list *args) { /* Set up the buffer for the output */ if(out->fp || out->fd) { out->buffer = __printf_buffer; out->limit = __printf_buffer + _PRINTF_BUFSIZE; } else { out->buffer = out->str; out->limit = out->str + out->size; } out->ptr = out->buffer; out->count = 0; int c; struct __printf_format opt; while((c = *format++)) { if(c != '%') { __printf_out(out, c); continue; } if(!(c = *format)) break; /* '%' character */ if(c == '%') { __printf_out(out, '%'); format++; continue; } opt = parse_fmt(&format); opt.spec = *format++; int id; if(isupper(opt.spec)) id = opt.spec - 'A'; else if(islower(opt.spec)) id = opt.spec - 'a' + 26; else continue; __printf_formatter_t *f = __printf_formatters[id]; if(f == NULL || f == _PRINTF_USED) continue; f(out, &opt, args); } __printf_flush(out); return (int)out->count; }