diff options
Diffstat (limited to 'compat/strptime.c')
-rw-r--r-- | compat/strptime.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/compat/strptime.c b/compat/strptime.c new file mode 100644 index 0000000..4044fc9 --- /dev/null +++ b/compat/strptime.c @@ -0,0 +1,214 @@ +/* + * This comes from the musl C library which has been licensed under the permissive MIT license. + * + * Downloaded from https://git.musl-libc.org/cgit/musl/plain/src/time/strptime.c, + * commit 98e688a9da5e7b2925dda17a2d6820dddf1fb287. + * + * Lobotomized to remove references to nl_langinfo(). + * Adjusted coding style to fit libyang's uncrustify rules. + * */ + +#include <ctype.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +char * +strptime(const char * restrict s, const char * restrict f, struct tm * restrict tm) +{ + int i, w, neg, adj, min, range, *dest, dummy; + int want_century = 0, century = 0, relyear = 0; + + while (*f) { + if (*f != '%') { + if (isspace(*f)) { + for ( ; *s && isspace(*s); s++) {} + } else if (*s != *f) { + return 0; + } else { + s++; + } + f++; + continue; + } + f++; + if (*f == '+') { + f++; + } + if (isdigit(*f)) { + char *new_f; + + w = strtoul(f, &new_f, 10); + f = new_f; + } else { + w = -1; + } + adj = 0; + switch (*f++) { + case 'a': + case 'A': + case 'b': + case 'B': + case 'h': + case 'c': + goto fail_nl_langinfo; + case 'C': + dest = ¢ury; + if (w < 0) { + w = 2; + } + want_century |= 2; + goto numeric_digits; + case 'd': + case 'e': + dest = &tm->tm_mday; + min = 1; + range = 31; + goto numeric_range; + case 'D': + s = strptime(s, "%m/%d/%y", tm); + if (!s) { + return 0; + } + break; + case 'H': + dest = &tm->tm_hour; + min = 0; + range = 24; + goto numeric_range; + case 'I': + dest = &tm->tm_hour; + min = 1; + range = 12; + goto numeric_range; + case 'j': + dest = &tm->tm_yday; + min = 1; + range = 366; + adj = 1; + goto numeric_range; + case 'm': + dest = &tm->tm_mon; + min = 1; + range = 12; + adj = 1; + goto numeric_range; + case 'M': + dest = &tm->tm_min; + min = 0; + range = 60; + goto numeric_range; + case 'n': + case 't': + for ( ; *s && isspace(*s); s++) {} + break; + case 'p': + case 'r': + goto fail_nl_langinfo; + case 'R': + s = strptime(s, "%H:%M", tm); + if (!s) { + return 0; + } + break; + case 'S': + dest = &tm->tm_sec; + min = 0; + range = 61; + goto numeric_range; + case 'T': + s = strptime(s, "%H:%M:%S", tm); + if (!s) { + return 0; + } + break; + case 'U': + case 'W': + /* Throw away result, for now. (FIXME?) */ + dest = &dummy; + min = 0; + range = 54; + goto numeric_range; + case 'w': + dest = &tm->tm_wday; + min = 0; + range = 7; + goto numeric_range; + case 'x': + case 'X': + goto fail_nl_langinfo; + case 'y': + dest = &relyear; + w = 2; + want_century |= 1; + goto numeric_digits; + case 'Y': + dest = &tm->tm_year; + if (w < 0) { + w = 4; + } + adj = 1900; + want_century = 0; + goto numeric_digits; + case '%': + if (*s++ != '%') { + return 0; + } + break; + default: + return 0; +numeric_range: + if (!isdigit(*s)) { + return 0; + } + *dest = 0; + for (i = 1; i <= min + range && isdigit(*s); i *= 10) { + *dest = *dest * 10 + *s++ - '0'; + } + if (*dest - min >= range) { + return 0; + } + *dest -= adj; + switch ((char *)dest - (char *)tm) { + case offsetof(struct tm, tm_yday): + ; + } + goto update; +numeric_digits: + neg = 0; + if (*s == '+') { + s++; + } else if (*s == '-') { + neg = 1, s++; + } + if (!isdigit(*s)) { + return 0; + } + for (*dest = i = 0; i < w && isdigit(*s); i++) { + *dest = *dest * 10 + *s++ - '0'; + } + if (neg) { + *dest = -*dest; + } + *dest -= adj; + goto update; +update: + // FIXME + ; + } + } + if (want_century) { + tm->tm_year = relyear; + if (want_century & 2) { + tm->tm_year += century * 100 - 1900; + } else if (tm->tm_year <= 68) { + tm->tm_year += 100; + } + } + return (char *)s; +fail_nl_langinfo: + fprintf(stderr, "strptime: nl_langinfo not available"); + return NULL; +} |