#include #include #include #include #include static void printf_strrev(char* arr, int size) { int left = 0; int right = size - 1; for (int i = left; i < right; i++) { char temp = arr[i]; arr[i] = arr[right]; arr[right] = temp; right--; } } template static char* printf_unsigned_to_string(IntegerType number, char* arr, int base) { int i = 0; if (number == 0) { arr[i] = '0'; arr[i + 1] = '\0'; return arr; } while (number != 0) { IntegerType r = number % (IntegerType)base; arr[i] = (char)((r > 9) ? (r - 10) + 'a' : r + '0'); i++; number /= base; } printf_strrev(arr, i); arr[i] = '\0'; return arr; } template static char* printf_signed_to_string(IntegerType number, char* arr, int base) { int i = 0, negative = 0; if (number == 0) { arr[i] = '0'; arr[i + 1] = '\0'; return arr; } if (number < 0 && base == 10) { number *= -1; negative = 1; } while (number != 0) { IntegerType r = number % base; arr[i] = (char)((r > 9) ? (r - 10) + 'a' : r + '0'); i++; number /= base; } if (negative) { arr[i] = '-'; i++; } printf_strrev(arr, i); arr[i] = '\0'; return arr; } const char* short_week_days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; const char* long_week_days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; const char* short_month_names[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; const char* long_month_names[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; extern "C" size_t strftime(char* s, size_t max, const char* format, const struct tm* time) { char buffer[1025]; // 1024 with null terminator size_t format_size = strlen(format); size_t format_index = 0; size_t buffer_insert_index = 0; ssize_t max_remaining = (ssize_t)max; size_t written = 0; if (s && max) *s = 0; auto flush_buffer = [&]() { size_t buffer_length = buffer_insert_index; written += buffer_length; buffer_insert_index = 0; if (max_remaining < 0) { buffer[buffer_length] = 0; if (s) strncat(s, buffer, sizeof(buffer)); return; } if (max_remaining == 0) { return; } if (buffer_length <= (size_t)max_remaining) { max_remaining -= buffer_length; buffer[buffer_length] = 0; if (s) strncat(s, buffer, sizeof(buffer)); } else { buffer[max_remaining] = 0; max_remaining = 0; if (s) strncat(s, buffer, sizeof(buffer)); } }; auto append_string = [&](const char* str) { while (strlen(str) > 1024) { flush_buffer(); memcpy(buffer, str, 1024); str += 1024; buffer_insert_index = 1024; } if (buffer_insert_index + strlen(str) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, str, strlen(str)); buffer_insert_index += strlen(str); if (buffer_insert_index == 1024) flush_buffer(); }; auto append_char = [&](char c) { buffer[buffer_insert_index++] = c; if (buffer_insert_index == 1024) flush_buffer(); }; while (format_index < format_size) { char current_char = format[format_index]; if (current_char == '%') { if (format_index + 1 == format_size) // end of format string { format_index++; continue; } else { format_index++; current_char = format[format_index]; switch (current_char) { case '%': { append_char('%'); break; } case 'a': { append_string(short_week_days[time->tm_wday % 7]); break; } case 'A': { append_string(long_week_days[time->tm_wday % 7]); break; } case 'b': { append_string(short_month_names[(time->tm_mon - 1) % 12]); break; } case 'B': { append_string(long_month_names[(time->tm_mon - 1) % 12]); break; } case 'c': { char buf[32]; append_string(short_week_days[time->tm_wday % 7]); append_char(' '); append_string(short_month_names[(time->tm_mon - 1) % 12]); append_char(' '); printf_signed_to_string(time->tm_mday, buf, 10); append_string(buf); append_char(' '); printf_signed_to_string(time->tm_hour % 24, buf, 10); append_string(buf); append_char(':'); printf_signed_to_string(time->tm_min % 60, buf, 10); append_string(buf); append_char(':'); printf_signed_to_string(time->tm_sec % 60, buf, 10); append_string(buf); append_char(' '); printf_signed_to_string(time->tm_year + 1900, buf, 10); append_string(buf); break; } case 'd': { char buf[30]; printf_signed_to_string(time->tm_mday % 32, buf, 10); append_string(buf); break; } case 'H': { char buf[30]; printf_signed_to_string(time->tm_hour % 24, buf, 10); append_string(buf); break; } case 'I': { char buf[30]; int hour = time->tm_hour % 12; if (hour == 0) hour = 12; printf_signed_to_string(hour, buf, 10); append_string(buf); break; } case 'j': { char buf[30]; printf_signed_to_string(time->tm_yday % 367, buf, 10); append_string(buf); break; } case 'm': { char buf[30]; printf_signed_to_string(time->tm_mon % 13, buf, 10); append_string(buf); break; } case 'M': { char buf[30]; printf_signed_to_string(time->tm_min % 60, buf, 10); append_string(buf); break; } case 'p': { if (time->tm_hour < 12) append_string("AM"); else append_string("PM"); break; } case 'P': { if (time->tm_hour < 12) append_string("am"); else append_string("pm"); break; } case 'S': { char buf[30]; printf_signed_to_string(time->tm_sec % 61, buf, 10); append_string(buf); break; } case 'w': { char buf[30]; printf_signed_to_string(time->tm_wday % 7, buf, 10); append_string(buf); break; } case 'x': { char buf[30]; printf_signed_to_string(time->tm_mon % 13, buf, 10); append_string(buf); append_char('/'); printf_signed_to_string(time->tm_mday % 32, buf, 10); append_string(buf); append_char('/'); printf_signed_to_string((time->tm_year + 1900) % 100, buf, 10); append_string(buf); break; } case 'X': { char buf[30]; printf_signed_to_string(time->tm_hour % 24, buf, 10); append_string(buf); append_char(':'); printf_signed_to_string(time->tm_min % 60, buf, 10); append_string(buf); append_char(':'); printf_signed_to_string(time->tm_sec % 60, buf, 10); append_string(buf); break; } case 'y': { char buf[30]; printf_signed_to_string((time->tm_year + 1900) % 100, buf, 10); append_string(buf); break; } case 'Y': { char buf[30]; printf_signed_to_string(time->tm_year + 1900, buf, 10); append_string(buf); break; } case 'Z': { append_string("UTC"); // FIXME: Add timezone support. break; } default: { fprintf(stderr, "strftime: unknown format specifier %%%c\n", current_char); abort(); } } } } else { append_char(current_char); } format_index++; } if (buffer_insert_index > 0) flush_buffer(); return written; }