#include #include static const char *day_names[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", }; static const char *month_names[12] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", }; /* Perform a call to snprintf() to print a value */ #define do_snprintf(...) { \ size_t _written = snprintf(s+len, n-len, __VA_ARGS__); \ len += _written; \ if(len >= n) goto full; \ }; break /* Perform a sub-call to strftime to expand a complex format */ #define do_strftime(fmt) { \ size_t _written = strftime2(s+len, n-len, fmt, time); \ if(_written == 0) goto full; \ len += _written; \ }; break size_t strftime2(char * restrict s, size_t n, const char * restrict format, const struct tm * restrict time) { size_t len = 0; while(*format) { if(*format != '%') { s[len++] = *format++; if(len >= n) goto full; continue; } format++; int c = *format++; /* In the "C" locale, ignore modifiers E and O */ if(c == 'E') c = *format++; else if(c == 'O') c = *format++; switch(c) { case 'a': do_snprintf("%.3s", day_names[time->tm_wday]); case 'A': do_snprintf("%s", day_names[time->tm_wday]); case 'b': do_snprintf("%.3s", month_names[time->tm_mon]); case 'B': do_snprintf("%s", month_names[time->tm_mon]); case 'c': do_strftime("%a %b %e %T %Y"); case 'C': do_snprintf("%02d", (time->tm_year + 1900) / 100); case 'd': do_snprintf("%02d", time->tm_mday); case 'D': do_strftime("%m/%d/%y"); case 'e': do_snprintf("%2d", time->tm_mday); case 'F': do_strftime("%Y-%m-%d"); /* TODO: strftime: Support week-based year */ case 'g': do_snprintf("??"); case 'G': do_snprintf("????"); case 'h': do_strftime("%b"); case 'H': do_snprintf("%02d", time->tm_hour); case 'I': do_snprintf("%02d", (time->tm_hour+11)%12 + 1); case 'j': do_snprintf("%03d", time->tm_yday + 1); case 'm': do_snprintf("%02d", time->tm_mon + 1); case 'M': do_snprintf("%02d", time->tm_min); case 'n': do_snprintf("\n"); case 'p': do_snprintf(time->tm_hour < 12 ? "AM" : "PM"); case 'r': do_strftime("%I:%M:%S %p"); case 'R': do_strftime("%H:%M"); case 'S': do_snprintf("%02d", time->tm_sec); case 't': do_snprintf("\t"); case 'T': do_strftime("%H:%M:%S"); case 'u': do_snprintf("%d", (time->tm_wday+6) % 7 + 1); /* TODO: strftime: Support week number */ case 'U': do_snprintf("??"); case 'V': do_snprintf("??"); case 'w': do_snprintf("%d", time->tm_wday); case 'W': do_snprintf("??"); case 'x': do_strftime("%m/%d/%y"); case 'X': do_strftime("%T"); case 'y': do_snprintf("%02d", time->tm_year % 100); case 'Y': do_snprintf("%d", time->tm_year + 1900); /* TODO: strftime: Support timezones */ case 'z': break; case 'Z': break; case '%': do_snprintf("%%"); } } s[len] = 0; return len; full: s[n-1] = 0; return 0; }