#include #include #include #include #include struct strftime_state { char* const buf; const size_t max; size_t pos; }; static bool try_put_char(strftime_state& state, char c) { if (state.pos == state.max) return false; state.buf[state.pos++] = c; return true; } static bool try_put_string(strftime_state& state, const char* s) { if (state.pos + strlen(s) == (state.max - 1)) return false; memcpy(state.buf + state.pos, s, strlen(s)); state.pos += strlen(s); return true; } static bool try_format(strftime_state& state, const char* format, ...) { va_list ap; va_start(ap, format); char buf[BUFSIZ]; vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); return try_put_string(state, buf); } static const char* abday_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const char* day_names[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; static const char* abmon_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char* mon_names[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; extern "C" { size_t strftime(char* buf, size_t max, const char* format, const struct tm* tm) { strftime_state state = { .buf = buf, .max = max, .pos = 0 }; while (*format) { if (*format != '%') { if (!try_put_char(state, *format)) return 0; format++; continue; } else { format++; switch (*format) { case 'a': if (!try_put_string(state, abday_names[tm->tm_wday])) return 0; break; case 'A': if (!try_put_string(state, day_names[tm->tm_wday])) return 0; break; case 'b': if (!try_put_string(state, abmon_names[tm->tm_mon])) return 0; break; case 'B': if (!try_put_string(state, mon_names[tm->tm_mon])) return 0; break; case 'c': if (!try_format(state, "%s %s %.2d %.2d:%.2d:%.2d %d", abday_names[tm->tm_wday], abmon_names[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_year + 1900)) return 0; break; case 'd': if (!try_format(state, "%.2d", tm->tm_mday)) return 0; break; case 'H': if (!try_format(state, "%.2d", tm->tm_hour)) return 0; break; case 'I': { int hour = tm->tm_hour % 12; if (hour == 0) hour = 12; if (!try_format(state, "%.2d", hour)) return 0; break; } case 'j': todo(); case 'm': if (!try_format(state, "%.2d", tm->tm_mon + 1)) return 0; break; case 'M': if (!try_format(state, "%.2d", tm->tm_min)) return 0; break; case 'p': if (tm->tm_hour >= 12) { if (!try_put_string(state, "PM")) return 0; } else { if (!try_put_string(state, "AM")) return 0; } break; case 'S': if (!try_format(state, "%.2d", tm->tm_sec)) return 0; break; case 'x': // Sorry, Americans, but I ain't doing m/d/y. if (!try_format(state, "%.2d/%.2d/%.2d", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900)) return 0; break; case 'X': if (!try_format(state, "%.2d:%.2d:%.2d", tm->tm_hour, tm->tm_min, tm->tm_sec)) return 0; break; case 'y': if (!try_format(state, "%.2d", (tm->tm_year + 1900) % 100)) return 0; break; case 'Y': if (!try_format(state, "%d", tm->tm_year + 1900)) return 0; break; default: fprintf(stderr, "strftime: unexpected conversion specifier %%%c\n", *format); exit(255); } format++; continue; } } state.buf[state.pos] = '\0'; return state.pos; } }