#include //TODO: verify printf("%d", 0). From the code it looked like it would print // an empty string. // NOTE: this file doesn't exist in a vacuum, it's a template for generating // the formatted print, you should define FMT_CHAR_TYPE before including it inline static int FMT_FUNC_NAME (void *ctx, OutputFunc out, const FMT_CHAR_TYPE *fmt, va_list args) { size_t full_length = 0; while (*fmt) { // skip any normal non percent text const FMT_CHAR_TYPE *start = fmt; while (*fmt && *fmt != '%') fmt++; // print all those non percent text out(ctx, fmt - start, start); full_length += (fmt - start); // null terminated if (*fmt == '\0') break; fmt += 1; if (*fmt == '%') { out(ctx, 1, "%"); fmt++; continue; } unsigned int precision = 0; if (*fmt == '.') { // custom precision fmt++; if (isdigit(*fmt)) { // just a small atoi // TODO: handle overflow, just in case(?) while (isdigit(*fmt)) { precision *= 10u; precision += (*fmt - '0'); fmt++; } } else if (*fmt == '*') { int p = va_arg(args, int); precision = p >= 0 ? ((unsigned int)p) : 0; fmt++; } } // integer length specifiers enum { CHAR, SHORT, INT, LONG, LLONG } int_length = INT; if (*fmt == 'l') { fmt++; if (*fmt == 'l') { int_length = LLONG; fmt++; } else { int_length = LONG; } } else if (*fmt == 'h') { fmt++; if (*fmt == 'h') { int_length = CHAR; fmt++; } else { int_length = SHORT; } } else if (*fmt == 'z') { fmt++; int_length = _Generic((size_t)0, unsigned char: CHAR, unsigned short: SHORT, unsigned int: INT, unsigned long: LONG, unsigned long long: LLONG); } FMT_CHAR_TYPE ch = *fmt++; const char* characters = "0123456789abcdef"; if (ch == 'X') characters = "0123456789ABCDEF"; switch (ch) { case 'c': { const char chr = va_arg(args, int); out(ctx, 1, &chr); full_length ++; break; } case 'f': case 'L': { double d = va_arg(args, double); if(signbit(d)) { // TODO: negative zero out(ctx, 1, "-"); d = -d; } if(isinf(d)) { out(ctx, sizeof"inf"-1, "inf"); break; } else if(isnan(d)) { out(ctx, sizeof"nan"-1, "nan"); break; } uint64_t w = (uint64_t)d; d -= w; FMT_CHAR_TYPE buffer[20]; size_t len = sizeof(buffer); do { buffer[--len] = characters[w % 10]; w /= 10; } while(w != 0); out(ctx, sizeof(buffer) - (len * sizeof(FMT_CHAR_TYPE)), buffer + len); char dot = '.'; out(ctx, 1, &dot); for(int i = 0; i != 6; ++i) { d *= 10; int dv = (int)d; d -= dv; char digit = characters[dv]; out(ctx, 1, &digit); } } break; case 's': { const FMT_CHAR_TYPE *str = va_arg(args, FMT_CHAR_TYPE*); size_t len = FMT_STRLEN_S(str, precision ? precision : SIZE_MAX); out(ctx, len, str); full_length += len; break; } case 'b': case 'o': case 'i': case 'u': case 'd': case 'x': case 'X': { int base = 10; switch (ch) { case 'X': case 'x': base = 16; break; case 'o': base = 8; break; case 'b': base = 2; break; default: base = 10; break; } uintmax_t i; if (ch == 'd' || ch == 'i') { intmax_t num = 0; switch (int_length) { case CHAR: num = (char) va_arg(args, int); break; case SHORT: num = (short) va_arg(args, int); break; case INT: num = va_arg(args, int); break; case LONG: num = va_arg(args, long); break; case LLONG: num = va_arg(args, long long); break; default: break; } if (num < 0) { out(ctx, 1, "-"); i = -num; full_length += 1; } else { i = num; } } else { switch (int_length) { case CHAR: i = (char) va_arg(args, int); break; case SHORT: i = (short) va_arg(args, int); break; case INT: i = va_arg(args, int); break; case LONG: i = va_arg(args, long); break; case LLONG: i = va_arg(args, long long); break; default: i = 0; break; } } if (i == 0) { out(ctx, 1, "0"); full_length += 1; } else { FMT_CHAR_TYPE buffer[20]; size_t len = sizeof(buffer); // we build the number in reverse while (i != 0) { buffer[--len] = characters[i % base]; i /= base; } out(ctx, sizeof(buffer) - (len * sizeof(FMT_CHAR_TYPE)), buffer + len); full_length += (sizeof(buffer) - len); } break; } default: break; } } return full_length; }