Merge pull request #576 from no92/strptime
This commit is contained in:
commit
d743bb66c5
|
@ -221,6 +221,7 @@ internal_sources = [
|
|||
'options/internal/generic/frigg.cpp',
|
||||
'options/internal/generic/inline-emitter.cpp',
|
||||
'options/internal/generic/sigset.cpp',
|
||||
'options/internal/generic/strings.cpp',
|
||||
'options/internal/generic/ubsan.cpp',
|
||||
'options/internal/gcc/stack_protector.cpp',
|
||||
'options/internal/gcc/guard-abi.cpp',
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <mlibc/allocator.hpp>
|
||||
#include <mlibc/lock.hpp>
|
||||
#include <mlibc/bitutil.hpp>
|
||||
#include <mlibc/strings.hpp>
|
||||
|
||||
#include <frg/mutex.hpp>
|
||||
|
||||
|
@ -615,478 +616,6 @@ char *ctime_r(const time_t *clock, char *buf) {
|
|||
return asctime_r(localtime(clock), buf);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
int month_to_day(int month) {
|
||||
switch(month){
|
||||
case 0: return 0;
|
||||
case 1: return 31;
|
||||
case 2: return 59;
|
||||
case 3: return 90;
|
||||
case 4: return 120;
|
||||
case 5: return 151;
|
||||
case 6: return 181;
|
||||
case 7: return 212;
|
||||
case 8: return 243;
|
||||
case 9: return 273;
|
||||
case 10: return 304;
|
||||
case 11: return 334;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int is_leapyear(int year) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
}
|
||||
|
||||
int month_and_year_to_day_in_year(int month, int year){
|
||||
int day = month_to_day(month);
|
||||
if(is_leapyear(year) && month < 2)
|
||||
return day + 1;
|
||||
|
||||
return day;
|
||||
}
|
||||
|
||||
int target_determination(int month) {
|
||||
switch(month){
|
||||
case 0: return 3;
|
||||
case 1: return 14;
|
||||
case 2: return 14;
|
||||
case 3: return 4;
|
||||
case 4: return 9;
|
||||
case 5: return 6;
|
||||
case 6: return 11;
|
||||
case 7: return 8;
|
||||
case 8: return 5;
|
||||
case 9: return 10;
|
||||
case 10: return 7;
|
||||
case 11: return 12;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int doom_determination(int full_year) {
|
||||
int century = full_year / 100;
|
||||
int anchor = 2 + 5 * (century % 4) % 7;
|
||||
|
||||
int year = full_year % 100;
|
||||
|
||||
if(year % 2)
|
||||
year += 11;
|
||||
|
||||
year /= 2;
|
||||
|
||||
if(year % 2)
|
||||
year += 11;
|
||||
|
||||
return 7 - (year % 7) + anchor;
|
||||
}
|
||||
|
||||
//Determine day of week through the doomsday algorithm.
|
||||
int day_determination(int day, int month, int year) {
|
||||
int doom = doom_determination(year);
|
||||
bool leap = is_leapyear(year);
|
||||
|
||||
int target = target_determination(month);
|
||||
if(leap && month < 2)
|
||||
target++;
|
||||
|
||||
int doom_dif = (day - target) % 7;
|
||||
return (doom + doom_dif) % 7;
|
||||
}
|
||||
|
||||
struct strptime_internal_state {
|
||||
bool has_century;
|
||||
bool has_year;
|
||||
bool has_month;
|
||||
bool has_day_of_month;
|
||||
bool has_day_of_year;
|
||||
bool has_day_of_week;
|
||||
|
||||
bool full_year_given;
|
||||
|
||||
int century;
|
||||
|
||||
size_t format_index;
|
||||
size_t input_index;
|
||||
};
|
||||
|
||||
char *strptime_internal(const char *__restrict input, const char *__restrict format,
|
||||
struct tm *__restrict tm, struct strptime_internal_state *__restrict state) {
|
||||
while(isspace(input[state->input_index]))
|
||||
state->input_index++;
|
||||
|
||||
if(input[state->input_index] == '\0')
|
||||
return NULL;
|
||||
|
||||
while(format[state->format_index] != '\0'){
|
||||
if(format[state->format_index] != '%'){
|
||||
if(isspace(format[state->format_index])){
|
||||
while(isspace(input[state->input_index++]));
|
||||
state->input_index--;
|
||||
}
|
||||
else {
|
||||
if(format[state->format_index] != input[state->input_index++])
|
||||
return NULL;
|
||||
}
|
||||
state->format_index++;
|
||||
continue;
|
||||
}
|
||||
state->format_index++;
|
||||
switch(format[state->format_index]){
|
||||
case '%':
|
||||
if(input[state->input_index++] != '%')
|
||||
return NULL;
|
||||
break;
|
||||
case 'a':
|
||||
case 'A':
|
||||
__ensure(!"strptime() %a and %A directives unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'b':
|
||||
case 'B':
|
||||
case 'h':
|
||||
__ensure(!"strptime() %b, %B and %h directives unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'c':
|
||||
__ensure(!"strptime() %c directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'C': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
state->century = product;
|
||||
state->has_century = true;
|
||||
break;
|
||||
}
|
||||
case 'd': //`%d` and `%e` are equivalent
|
||||
case 'e': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_mday = product;
|
||||
state->has_day_of_month = true;
|
||||
break;
|
||||
}
|
||||
case 'D': { //equivalent to `%m/%d/%y`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%m/%d/%y", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_hour = product;
|
||||
break;
|
||||
}
|
||||
case 'I':
|
||||
__ensure(!"strptime() %I directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'j': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 3 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_yday = product - 1;
|
||||
state->has_day_of_year = true;
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_mon = product - 1;
|
||||
state->has_month = true;
|
||||
break;
|
||||
}
|
||||
case 'M': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_min = product;
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
case 't': {
|
||||
size_t n = 0;
|
||||
while(isspace(input[state->input_index++]))
|
||||
n++;
|
||||
if(n == 0)
|
||||
return NULL;
|
||||
state->input_index--;
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
__ensure(!"strptime() %p directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'r':
|
||||
__ensure(!"strptime() %r directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'R': { //equivalent to `%H:%M`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%H:%M", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_sec = product;
|
||||
break;
|
||||
}
|
||||
case 'T': { //equivalent to `%H:%M:%S`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%H:%M:%S", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'U':
|
||||
__ensure(!"strptime() %U directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'w': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 1 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_wday = product;
|
||||
state->has_day_of_week = true;
|
||||
break;
|
||||
}
|
||||
case 'W':
|
||||
__ensure(!"strptime() %W directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'x':
|
||||
__ensure(!"strptime() %x directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'X':
|
||||
__ensure(!"strptime() %X directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'y': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
if(product < 69)
|
||||
product += 100;
|
||||
state->input_index += n;
|
||||
tm->tm_year = product;
|
||||
state->has_year = true;
|
||||
break;
|
||||
}
|
||||
case 'Y': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 4 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_year = product - 1900;
|
||||
state->has_year = true;
|
||||
state->has_century = true;
|
||||
state->full_year_given = true;
|
||||
state->century = product / 100;
|
||||
break;
|
||||
}
|
||||
case 'F': { //GNU extensions
|
||||
//equivalent to `%Y-%m-%d`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%Y-%m-%d", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
__ensure(!"strptime() %g directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'G':
|
||||
__ensure(!"strptime() %G directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'u': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 1 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_wday = product - 1;
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
__ensure(!"strptime() %V directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'z':
|
||||
__ensure(!"strptime() %z directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'Z':
|
||||
__ensure(!"strptime() %Z directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 's': //end of GNU extensions
|
||||
__ensure(!"strptime() %s directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'E': { //locale-dependent date & time representation
|
||||
__ensure(!"strptime() %E* directives unimplemented.");
|
||||
__builtin_unreachable();
|
||||
/*
|
||||
state->format_index++;
|
||||
switch(format[state->format_index]){
|
||||
case 'c':
|
||||
break;
|
||||
case 'C':
|
||||
break;
|
||||
case 'x':
|
||||
break;
|
||||
case 'X':
|
||||
break;
|
||||
case 'y':
|
||||
break;
|
||||
case 'Y':
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
}
|
||||
case 'O': { //locale-dependent numeric symbols
|
||||
__ensure(!"strptime() %O* directives unimplemented.");
|
||||
__builtin_unreachable();
|
||||
/*
|
||||
state->format_index++;
|
||||
switch(format[state->format_index]){
|
||||
case 'd':
|
||||
case 'e':
|
||||
break;
|
||||
case 'H':
|
||||
break;
|
||||
case 'I':
|
||||
break;
|
||||
case 'm':
|
||||
break;
|
||||
case 'M':
|
||||
break;
|
||||
case 'S':
|
||||
break;
|
||||
case 'U':
|
||||
break;
|
||||
case 'w':
|
||||
break;
|
||||
case 'W':
|
||||
break;
|
||||
case 'y':
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
}
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
state->format_index++;
|
||||
}
|
||||
|
||||
return (char*)input + state->input_index;
|
||||
}
|
||||
|
||||
} //anonymous namespace
|
||||
|
||||
char *strptime(const char *__restrict s, const char *__restrict format, struct tm *__restrict tm){
|
||||
struct strptime_internal_state state = {};
|
||||
|
||||
char *result = strptime_internal(s, format, tm, &state);
|
||||
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
if(state.has_century && !state.full_year_given){
|
||||
int full_year = state.century * 100;
|
||||
|
||||
if(state.has_year){
|
||||
//Compensate for default century-adjustment of `%j` operand
|
||||
if(tm->tm_year >= 100)
|
||||
full_year += tm->tm_year - 100;
|
||||
else
|
||||
full_year += tm->tm_year;
|
||||
}
|
||||
|
||||
tm->tm_year = full_year - 1900;
|
||||
|
||||
state.has_year = true;
|
||||
}
|
||||
|
||||
if(state.has_month && !state.has_day_of_year){
|
||||
int day = 0;
|
||||
if(state.has_year)
|
||||
day = month_and_year_to_day_in_year(tm->tm_mon, tm->tm_year);
|
||||
else
|
||||
day = month_to_day(tm->tm_mon);
|
||||
|
||||
tm->tm_yday = day + tm->tm_mday - 1;
|
||||
state.has_day_of_year = true;
|
||||
}
|
||||
|
||||
if(state.has_year && !state.has_day_of_week){
|
||||
if(!state.has_month && !state.has_day_of_month){
|
||||
tm->tm_wday = day_determination(0, 0, tm->tm_year + 1900);
|
||||
}
|
||||
else if(state.has_month && state.has_day_of_month){
|
||||
tm->tm_wday = day_determination(tm->tm_mday, tm->tm_mon, tm->tm_year + 1900);
|
||||
}
|
||||
state.has_day_of_week = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
time_t timelocal(struct tm *) {
|
||||
__ensure(!"Not implemented");
|
||||
__builtin_unreachable();
|
||||
|
|
|
@ -99,8 +99,10 @@ struct tm *localtime_r(const time_t *, struct tm *);
|
|||
char *asctime_r(const struct tm *tm, char *buf);
|
||||
char *ctime_r(const time_t *, char *);
|
||||
|
||||
#ifdef __MLIBC_POSIX_OPTION
|
||||
char *strptime(const char *__restrict, const char *__restrict,
|
||||
struct tm *__restrict);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
22
options/internal/generic/strings.cpp
Normal file
22
options/internal/generic/strings.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <ctype.h>
|
||||
|
||||
#include <mlibc/strings.hpp>
|
||||
|
||||
namespace mlibc {
|
||||
|
||||
int strncasecmp(const char *a, const char *b, size_t size) {
|
||||
for(size_t i = 0; i < size; i++) {
|
||||
unsigned char a_byte = tolower(a[i]);
|
||||
unsigned char b_byte = tolower(b[i]);
|
||||
if(!a_byte && !b_byte)
|
||||
return 0;
|
||||
// If only one char is null, one of the following cases applies.
|
||||
if(a_byte < b_byte)
|
||||
return -1;
|
||||
if(a_byte > b_byte)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
12
options/internal/include/mlibc/strings.hpp
Normal file
12
options/internal/include/mlibc/strings.hpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef MLIBC_STRINGS
|
||||
#define MLIBC_STRINGS
|
||||
|
||||
#include <bits/size_t.h>
|
||||
|
||||
namespace mlibc {
|
||||
|
||||
int strncasecmp(const char *a, const char *b, size_t size);
|
||||
|
||||
} // namespace mlibc
|
||||
|
||||
#endif // MLIBC_STRINGS
|
|
@ -22,6 +22,48 @@ char *nl_langinfo(nl_item item) {
|
|||
case ABMON_12: return const_cast<char *>("Dec");
|
||||
}
|
||||
__builtin_unreachable();
|
||||
} else if(item >= MON_1 && item <= MON_12) {
|
||||
switch(item) {
|
||||
case MON_1: return const_cast<char *>("January");
|
||||
case MON_2: return const_cast<char *>("Feburary");
|
||||
case MON_3: return const_cast<char *>("March");
|
||||
case MON_4: return const_cast<char *>("April");
|
||||
case MON_5: return const_cast<char *>("May");
|
||||
case MON_6: return const_cast<char *>("June");
|
||||
case MON_7: return const_cast<char *>("July");
|
||||
case MON_8: return const_cast<char *>("August");
|
||||
case MON_9: return const_cast<char *>("September");
|
||||
case MON_10: return const_cast<char *>("October");
|
||||
case MON_11: return const_cast<char *>("November");
|
||||
case MON_12: return const_cast<char *>("December");
|
||||
}
|
||||
__builtin_unreachable();
|
||||
} else if(item == AM_STR) {
|
||||
return const_cast<char *>("AM");
|
||||
} else if(item == PM_STR) {
|
||||
return const_cast<char *>("PM");
|
||||
} else if(item >= DAY_1 && item <= DAY_7) {
|
||||
switch(item) {
|
||||
case DAY_1: return const_cast<char *>("Sunday");
|
||||
case DAY_2: return const_cast<char *>("Monday");
|
||||
case DAY_3: return const_cast<char *>("Tuesday");
|
||||
case DAY_4: return const_cast<char *>("Wednesday");
|
||||
case DAY_5: return const_cast<char *>("Thursday");
|
||||
case DAY_6: return const_cast<char *>("Friday");
|
||||
case DAY_7: return const_cast<char *>("Saturday");
|
||||
}
|
||||
__builtin_unreachable();
|
||||
} else if(item >= ABDAY_1 && item <= ABDAY_7) {
|
||||
switch(item) {
|
||||
case ABDAY_1: return const_cast<char *>("Sun");
|
||||
case ABDAY_2: return const_cast<char *>("Mon");
|
||||
case ABDAY_3: return const_cast<char *>("Tue");
|
||||
case ABDAY_4: return const_cast<char *>("Wed");
|
||||
case ABDAY_5: return const_cast<char *>("Thu");
|
||||
case ABDAY_6: return const_cast<char *>("Fri");
|
||||
case ABDAY_7: return const_cast<char *>("Sat");
|
||||
}
|
||||
__builtin_unreachable();
|
||||
}else{
|
||||
mlibc::infoLogger() << "mlibc: nl_langinfo item "
|
||||
<< item << " is not implemented properly" << frg::endlog;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <ctype.h>
|
||||
#include <bits/ensure.h>
|
||||
#include <mlibc/strings.hpp>
|
||||
|
||||
char *index (const char *s, int c) {
|
||||
return strchr(s, c);
|
||||
|
@ -88,17 +89,6 @@ int strcasecmp(const char *a, const char *b) {
|
|||
}
|
||||
|
||||
int strncasecmp(const char *a, const char *b, size_t size) {
|
||||
for(size_t i = 0; i < size; i++) {
|
||||
unsigned char a_byte = tolower(a[i]);
|
||||
unsigned char b_byte = tolower(b[i]);
|
||||
if(!a_byte && !b_byte)
|
||||
return 0;
|
||||
// If only one char is null, one of the following cases applies.
|
||||
if(a_byte < b_byte)
|
||||
return -1;
|
||||
if(a_byte > b_byte)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return mlibc::strncasecmp(a, b, size);
|
||||
}
|
||||
|
||||
|
|
505
options/posix/generic/time.cpp
Normal file
505
options/posix/generic/time.cpp
Normal file
|
@ -0,0 +1,505 @@
|
|||
#include <ctype.h>
|
||||
#include <langinfo.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <bits/ensure.h>
|
||||
#include <mlibc/strings.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
int month_to_day(int month) {
|
||||
switch(month){
|
||||
case 0: return 0;
|
||||
case 1: return 31;
|
||||
case 2: return 59;
|
||||
case 3: return 90;
|
||||
case 4: return 120;
|
||||
case 5: return 151;
|
||||
case 6: return 181;
|
||||
case 7: return 212;
|
||||
case 8: return 243;
|
||||
case 9: return 273;
|
||||
case 10: return 304;
|
||||
case 11: return 334;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int is_leapyear(int year) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
|
||||
}
|
||||
|
||||
int month_and_year_to_day_in_year(int month, int year){
|
||||
int day = month_to_day(month);
|
||||
if(is_leapyear(year) && month < 2)
|
||||
return day + 1;
|
||||
|
||||
return day;
|
||||
}
|
||||
|
||||
int target_determination(int month) {
|
||||
switch(month){
|
||||
case 0: return 3;
|
||||
case 1: return 14;
|
||||
case 2: return 14;
|
||||
case 3: return 4;
|
||||
case 4: return 9;
|
||||
case 5: return 6;
|
||||
case 6: return 11;
|
||||
case 7: return 8;
|
||||
case 8: return 5;
|
||||
case 9: return 10;
|
||||
case 10: return 7;
|
||||
case 11: return 12;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int doom_determination(int full_year) {
|
||||
int century = full_year / 100;
|
||||
int anchor = 2 + 5 * (century % 4) % 7;
|
||||
|
||||
int year = full_year % 100;
|
||||
|
||||
if(year % 2)
|
||||
year += 11;
|
||||
|
||||
year /= 2;
|
||||
|
||||
if(year % 2)
|
||||
year += 11;
|
||||
|
||||
return 7 - (year % 7) + anchor;
|
||||
}
|
||||
|
||||
//Determine day of week through the doomsday algorithm.
|
||||
int day_determination(int day, int month, int year) {
|
||||
int doom = doom_determination(year);
|
||||
bool leap = is_leapyear(year);
|
||||
|
||||
int target = target_determination(month);
|
||||
if(leap && month < 2)
|
||||
target++;
|
||||
|
||||
int doom_dif = (day - target) % 7;
|
||||
return (doom + doom_dif) % 7;
|
||||
}
|
||||
|
||||
struct strptime_internal_state {
|
||||
bool has_century;
|
||||
bool has_year;
|
||||
bool has_month;
|
||||
bool has_day_of_month;
|
||||
bool has_day_of_year;
|
||||
bool has_day_of_week;
|
||||
|
||||
bool full_year_given;
|
||||
|
||||
int century;
|
||||
|
||||
size_t format_index;
|
||||
size_t input_index;
|
||||
};
|
||||
|
||||
char *strptime_internal(const char *__restrict input, const char *__restrict format,
|
||||
struct tm *__restrict tm, struct strptime_internal_state *__restrict state) {
|
||||
auto matchLanginfoItem = [&] (int start, size_t num, int &dest, bool &flag) -> bool {
|
||||
for(size_t i = start; i < (start + num); i++) {
|
||||
const char *mon = nl_langinfo(i);
|
||||
size_t len = strlen(mon);
|
||||
if(mlibc::strncasecmp(&input[state->input_index], mon, len))
|
||||
continue;
|
||||
state->input_index += len;
|
||||
dest = i - start;
|
||||
flag = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
auto matchNumericRange = [&] (int start, int end, int &dest, bool *flag) -> bool {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return false;
|
||||
if(product < start || product > end)
|
||||
return false;
|
||||
state->input_index += n;
|
||||
dest = product;
|
||||
if(flag) *flag = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
while(isspace(input[state->input_index]))
|
||||
state->input_index++;
|
||||
|
||||
if(input[state->input_index] == '\0')
|
||||
return NULL;
|
||||
|
||||
while(format[state->format_index] != '\0'){
|
||||
if(format[state->format_index] != '%'){
|
||||
if(isspace(format[state->format_index])){
|
||||
while(isspace(input[state->input_index++]));
|
||||
state->input_index--;
|
||||
}
|
||||
else {
|
||||
if(format[state->format_index] != input[state->input_index++])
|
||||
return NULL;
|
||||
}
|
||||
state->format_index++;
|
||||
continue;
|
||||
}
|
||||
state->format_index++;
|
||||
switch(format[state->format_index]){
|
||||
case '%':
|
||||
if(input[state->input_index++] != '%')
|
||||
return NULL;
|
||||
break;
|
||||
case 'a':
|
||||
case 'A': {
|
||||
if (!matchLanginfoItem(DAY_1, 7, tm->tm_wday, state->has_day_of_week) && \
|
||||
!matchLanginfoItem(ABDAY_1, 7, tm->tm_wday, state->has_day_of_week))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case 'b':
|
||||
case 'B':
|
||||
case 'h': {
|
||||
if (!matchLanginfoItem(MON_1, 12, tm->tm_mon, state->has_month) && \
|
||||
!matchLanginfoItem(ABMON_1, 12, tm->tm_mon, state->has_month))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
__ensure(!"strptime() %c directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'C': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
state->century = product;
|
||||
state->has_century = true;
|
||||
break;
|
||||
}
|
||||
case 'd': //`%d` and `%e` are equivalent
|
||||
case 'e': {
|
||||
if(!matchNumericRange(1, 31, tm->tm_mday, &state->has_day_of_month))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case 'D': { //equivalent to `%m/%d/%y`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%m/%d/%y", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'H': {
|
||||
if(!matchNumericRange(0, 23, tm->tm_hour, nullptr))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case 'I': {
|
||||
if(!matchNumericRange(1, 12, tm->tm_hour, nullptr))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case 'j': {
|
||||
if(!matchNumericRange(1, 366, tm->tm_yday, &state->has_day_of_year))
|
||||
return NULL;
|
||||
tm->tm_yday--;
|
||||
break;
|
||||
}
|
||||
case 'm': {
|
||||
if(!matchNumericRange(1, 12, tm->tm_mon, &state->has_month))
|
||||
return NULL;
|
||||
tm->tm_mon--;
|
||||
break;
|
||||
}
|
||||
case 'M': {
|
||||
if(!matchNumericRange(0, 59, tm->tm_min, nullptr))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
case 't': {
|
||||
size_t n = 0;
|
||||
while(isspace(input[state->input_index++]))
|
||||
n++;
|
||||
if(n == 0)
|
||||
return NULL;
|
||||
state->input_index--;
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
const char *meridian_str = nl_langinfo(AM_STR);
|
||||
size_t len = strlen(meridian_str);
|
||||
if (!mlibc::strncasecmp(&input[state->input_index], meridian_str, len)) {
|
||||
tm->tm_hour %= 12;
|
||||
state->input_index += len;
|
||||
break;
|
||||
}
|
||||
meridian_str = nl_langinfo(PM_STR);
|
||||
len = strlen(meridian_str);
|
||||
if (!mlibc::strncasecmp(&input[state->input_index], meridian_str, len)) {
|
||||
tm->tm_hour %= 12;
|
||||
tm->tm_hour += 12;
|
||||
state->input_index += len;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'r': { //equivalent to `%I:%M:%S %p`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%I:%M:%S %p", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'R': { //equivalent to `%H:%M`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%H:%M", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
if(!matchNumericRange(0, 60, tm->tm_sec, nullptr))
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
case 'T': { //equivalent to `%H:%M:%S`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%H:%M:%S", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'U':
|
||||
__ensure(!"strptime() %U directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'w': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 1 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_wday = product;
|
||||
state->has_day_of_week = true;
|
||||
break;
|
||||
}
|
||||
case 'W':
|
||||
__ensure(!"strptime() %W directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'x':
|
||||
__ensure(!"strptime() %x directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'X':
|
||||
__ensure(!"strptime() %X directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'y': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 2 < n)
|
||||
return NULL;
|
||||
if(product < 69)
|
||||
product += 100;
|
||||
state->input_index += n;
|
||||
tm->tm_year = product;
|
||||
state->has_year = true;
|
||||
break;
|
||||
}
|
||||
case 'Y': {
|
||||
int product = 0, n = 0;
|
||||
sscanf(&input[state->input_index], "%d%n", &product, &n);
|
||||
if(n == 0 || 4 < n)
|
||||
return NULL;
|
||||
state->input_index += n;
|
||||
tm->tm_year = product - 1900;
|
||||
state->has_year = true;
|
||||
state->has_century = true;
|
||||
state->full_year_given = true;
|
||||
state->century = product / 100;
|
||||
break;
|
||||
}
|
||||
case 'F': { //GNU extensions
|
||||
//equivalent to `%Y-%m-%d`
|
||||
size_t pre_fi = state->format_index;
|
||||
state->format_index = 0;
|
||||
|
||||
char *result = strptime_internal(input, "%Y-%m-%d", tm, state);
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
state->format_index = pre_fi;
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
__ensure(!"strptime() %g directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'G':
|
||||
__ensure(!"strptime() %G directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'u': {
|
||||
if(!matchNumericRange(1, 7, tm->tm_wday, nullptr))
|
||||
return NULL;
|
||||
tm->tm_wday--;
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
__ensure(!"strptime() %V directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'z':
|
||||
__ensure(!"strptime() %z directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'Z':
|
||||
__ensure(!"strptime() %Z directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 's': //end of GNU extensions
|
||||
__ensure(!"strptime() %s directive unimplemented.");
|
||||
__builtin_unreachable();
|
||||
break;
|
||||
case 'E': { //locale-dependent date & time representation
|
||||
__ensure(!"strptime() %E* directives unimplemented.");
|
||||
__builtin_unreachable();
|
||||
/*
|
||||
state->format_index++;
|
||||
switch(format[state->format_index]){
|
||||
case 'c':
|
||||
break;
|
||||
case 'C':
|
||||
break;
|
||||
case 'x':
|
||||
break;
|
||||
case 'X':
|
||||
break;
|
||||
case 'y':
|
||||
break;
|
||||
case 'Y':
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
}
|
||||
case 'O': { //locale-dependent numeric symbols
|
||||
__ensure(!"strptime() %O* directives unimplemented.");
|
||||
__builtin_unreachable();
|
||||
/*
|
||||
state->format_index++;
|
||||
switch(format[state->format_index]){
|
||||
case 'd':
|
||||
case 'e':
|
||||
break;
|
||||
case 'H':
|
||||
break;
|
||||
case 'I':
|
||||
break;
|
||||
case 'm':
|
||||
break;
|
||||
case 'M':
|
||||
break;
|
||||
case 'S':
|
||||
break;
|
||||
case 'U':
|
||||
break;
|
||||
case 'w':
|
||||
break;
|
||||
case 'W':
|
||||
break;
|
||||
case 'y':
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
*/
|
||||
}
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
state->format_index++;
|
||||
}
|
||||
|
||||
return (char*)input + state->input_index;
|
||||
}
|
||||
|
||||
} //anonymous namespace
|
||||
|
||||
char *strptime(const char *__restrict s, const char *__restrict format, struct tm *__restrict tm){
|
||||
struct strptime_internal_state state = {};
|
||||
|
||||
char *result = strptime_internal(s, format, tm, &state);
|
||||
|
||||
if(result == NULL)
|
||||
return NULL;
|
||||
|
||||
if(state.has_century && !state.full_year_given){
|
||||
int full_year = state.century * 100;
|
||||
|
||||
if(state.has_year){
|
||||
//Compensate for default century-adjustment of `%j` operand
|
||||
if(tm->tm_year >= 100)
|
||||
full_year += tm->tm_year - 100;
|
||||
else
|
||||
full_year += tm->tm_year;
|
||||
}
|
||||
|
||||
tm->tm_year = full_year - 1900;
|
||||
|
||||
state.has_year = true;
|
||||
}
|
||||
|
||||
if(state.has_month && !state.has_day_of_year){
|
||||
int day = 0;
|
||||
if(state.has_year)
|
||||
day = month_and_year_to_day_in_year(tm->tm_mon, tm->tm_year);
|
||||
else
|
||||
day = month_to_day(tm->tm_mon);
|
||||
|
||||
tm->tm_yday = day + tm->tm_mday - 1;
|
||||
state.has_day_of_year = true;
|
||||
}
|
||||
|
||||
if(state.has_year && !state.has_day_of_week){
|
||||
if(!state.has_month && !state.has_day_of_month){
|
||||
tm->tm_wday = day_determination(0, 0, tm->tm_year + 1900);
|
||||
}
|
||||
else if(state.has_month && state.has_day_of_month){
|
||||
tm->tm_wday = day_determination(tm->tm_mday, tm->tm_mon, tm->tm_year + 1900);
|
||||
}
|
||||
state.has_day_of_week = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -54,6 +54,7 @@ libc_sources += files(
|
|||
'generic/sys-msg.cpp',
|
||||
'generic/sys-sem.cpp',
|
||||
'generic/sys-ipc.cpp',
|
||||
'generic/time.cpp',
|
||||
'generic/wordexp-stubs.cpp',
|
||||
'musl-generic/fnmatch.cpp',
|
||||
'musl-generic/glob.cpp',
|
||||
|
|
|
@ -13,7 +13,6 @@ all_test_cases = [
|
|||
'ansi/strrchr',
|
||||
'ansi/wcsrtombs',
|
||||
'ansi/wmemcmp',
|
||||
'ansi/time',
|
||||
'ansi/timegm',
|
||||
'ansi/ungetc',
|
||||
'ansi/wcsdup',
|
||||
|
@ -54,6 +53,7 @@ all_test_cases = [
|
|||
'posix/system', # This test should be in the ANSI tests, but it depends on sys/wait.h
|
||||
'posix/sigsuspend',
|
||||
'posix/sigaltstack',
|
||||
'posix/time',
|
||||
'posix/realpath',
|
||||
'posix/ffs',
|
||||
'posix/getcwd',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <assert.h>
|
||||
#include <locale.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
@ -87,4 +88,46 @@ int main() {
|
|||
assert(tm.tm_sec == 53);
|
||||
assert(tm.tm_hour == 14);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("24", "%H", &tm);
|
||||
assert(a == NULL);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("0", "%I", &tm);
|
||||
assert(a == NULL);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
setlocale(LC_TIME, "en_US.UTF-8");
|
||||
a = strptime("10 21 PM", "%I %M %p", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_hour == 22);
|
||||
assert(tm.tm_min == 21);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("January", "%h", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_mon == 0);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("2", "%j", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_yday == 1);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("Wednesday", "%A", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_wday == 3);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
|
||||
a = strptime("11:51:13 PM", "%r", &tm);
|
||||
assert(a != NULL);
|
||||
assert(*a == '\0');
|
||||
assert(tm.tm_hour == 23);
|
||||
assert(tm.tm_min == 51);
|
||||
assert(tm.tm_sec == 13);
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
}
|
Loading…
Reference in a new issue