libc: Implement strftime()
This commit is contained in:
parent
4c096bd36c
commit
d5a6c7f27f
318
libs/libc/src/strftime.cpp
Normal file
318
libs/libc/src/strftime.cpp
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
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 <typename IntegerType> 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 <typename IntegerType> 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<int>(time->tm_mday, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
append_char(' ');
|
||||||
|
printf_signed_to_string<int>(time->tm_hour % 24, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
append_char(':');
|
||||||
|
printf_signed_to_string<int>(time->tm_min % 60, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
append_char(':');
|
||||||
|
printf_signed_to_string<int>(time->tm_sec % 60, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
append_char(' ');
|
||||||
|
printf_signed_to_string<int>(time->tm_year + 1900, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(time->tm_mday % 32, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'H': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(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<int>(hour, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'j': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(time->tm_yday % 367, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'm': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(time->tm_mon % 13, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'M': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(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<int>(time->tm_sec % 61, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'w': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(time->tm_wday % 7, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(time->tm_mon % 13, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
append_char('/');
|
||||||
|
printf_signed_to_string<int>(time->tm_mday % 32, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
append_char('/');
|
||||||
|
printf_signed_to_string<int>((time->tm_year + 1900) % 100, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'X': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(time->tm_hour % 24, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
append_char(':');
|
||||||
|
printf_signed_to_string<int>(time->tm_min % 60, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
append_char(':');
|
||||||
|
printf_signed_to_string<int>(time->tm_sec % 60, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'y': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>((time->tm_year + 1900) % 100, buf, 10);
|
||||||
|
append_string(buf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'Y': {
|
||||||
|
char buf[30];
|
||||||
|
printf_signed_to_string<int>(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;
|
||||||
|
}
|
@ -135,8 +135,7 @@ extern "C"
|
|||||||
|
|
||||||
char* asctime_r(const struct tm* time, char buf[26])
|
char* asctime_r(const struct tm* time, char buf[26])
|
||||||
{
|
{
|
||||||
snprintf(buf, 26, "%s %s %d %d:%d:%d %d\n", wday_names[time->tm_wday], month_names[time->tm_mon - 1],
|
strftime(buf, 26, "%a %b %d %H:%M:%S %Y\n", time);
|
||||||
time->tm_mday, time->tm_hour, time->tm_min, time->tm_sec, time->tm_year + 1900);
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,9 +155,4 @@ extern "C"
|
|||||||
{
|
{
|
||||||
return asctime(localtime(time));
|
return asctime(localtime(time));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t strftime(char*, size_t, const char*, const struct tm*)
|
|
||||||
{
|
|
||||||
NOT_IMPLEMENTED("strftime");
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user