diff --git a/LICENSE b/LICENSE index 241792ccd3..94730761c8 100644 --- a/LICENSE +++ b/LICENSE @@ -5897,6 +5897,7 @@ libs/libm/libm/scalbnf.c libs/libm/libm/scalbn.c libs/libm/libm/scalbnl.c libs/libc/stdlib/lib_strtof.c +libs/libc/time/lib_strptime.c ====================== Copyright © 2005-2020 Rich Felker, et al. @@ -6398,29 +6399,6 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -libs/libc/time/lib_strptime.c -======================================= - - Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. All rights reserved. - - This code was contributed to The NetBSD Foundation by Klaus Klein. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. All advertising materials mentioning features or use of this software - must display the following acknowledgement: - This product includes software developed by the NetBSD - Foundation, Inc. and its contributors. - 4. Neither the name of The NetBSD Foundation nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - libs/libc/unistd/lib_gethostname.c libs/libc/unistd/lib_sethostname.c include/sys/uio.h diff --git a/libs/libc/time/CMakeLists.txt b/libs/libc/time/CMakeLists.txt index f01d6d86fd..cd577ece29 100644 --- a/libs/libc/time/CMakeLists.txt +++ b/libs/libc/time/CMakeLists.txt @@ -44,4 +44,8 @@ else() list(APPEND SRCS lib_timegm.c lib_gmtime.c lib_gmtimer.c) endif() +if(CONFIG_ALLOW_MIT_COMPONENTS) + list(APPEND SRCS lib_strptime.c) +endif() + target_sources(c PRIVATE ${SRCS}) diff --git a/libs/libc/time/Make.defs b/libs/libc/time/Make.defs index 78d23a911f..22a0856da2 100644 --- a/libs/libc/time/Make.defs +++ b/libs/libc/time/Make.defs @@ -34,6 +34,10 @@ else CSRCS += lib_timegm.c lib_gmtime.c lib_gmtimer.c endif +ifdef CONFIG_ALLOW_MIT_COMPONENTS +CSRCS += lib_strptime.c +endif + # Add the time directory to the build DEPPATH += --dep-path time diff --git a/libs/libc/time/lib_strptime.c b/libs/libc/time/lib_strptime.c new file mode 100644 index 0000000000..e2c56887ef --- /dev/null +++ b/libs/libc/time/lib_strptime.c @@ -0,0 +1,617 @@ +/**************************************************************************** + * libs/libc/time/lib_strptime.c + * + * musl as a whole is licensed under the following standard MIT license: + * + * Copyright © 2005-2020 Rich Felker, et al. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: strptime + * + * Description: + * Parses a string representing date and time according to a specified + * format and stores the result in a struct tm structure. + * + * Input Parameters: + * s - Pointer to the input string to be parsed. + * f - Pointer to the format string specifying how to parse the input + * string. + * tm - Pointer to a struct tm structure where the parsed date and time + * will be stored. + * + * Returned Value: + * Returns a pointer to the character in the input string s immediately + * following the last character processed. + * + ****************************************************************************/ + +FAR char *strptime(FAR const char *restrict s, FAR const char *restrict f, + FAR struct tm *restrict tm) +{ + int i; + int w; + int neg; + int adj; + int min; + int range; + FAR int *dest; + int dummy; + int want_century = 0; + int century = 0; + int relyear = 0; + #ifdef CONFIG_LIBC_LOCALE + FAR const char *ex; + size_t len; + #endif + + if (!s || !f || !tm) + { + return NULL; + } + + while (*f) + { + if (*f != '%') + { + /* Handle literal characters in the format string */ + + if (isspace(*f)) + { + /* Skip whitespace in the input string */ + + for (; *s && isspace(*s); s++); + } + else if (*s != *f) + { + /* Mismatch between format string and input string */ + + return 0; + } + else + { + s++; + } + + f++; + continue; + } + + f++; + + /* Handle optional width specifier */ + + if (*f == '+') + { + f++; + } + + if (isdigit(*f)) + { + char *new_f; + w = strtoul(f, &new_f, 10); + f = new_f; + } + else + { + w = -1; + } + + adj = 0; + + /* Handle format specifiers */ + + switch (*f++) + { + 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; + if (!isdigit(*s)) + { + return NULL; + } + + goto numeric_range; + + case 'D': + + /* Date in mm/dd/yy format */ + + s = strptime(s, "%m/%d/%y", tm); + if (!s) + { + return NULL; + } + break; + + case 'F': + + /* Date in yyyy-mm-dd format with width-limitation handling */ + + i = 0; + char tmp[20]; + if (*s == '-' || *s == '+') + { + tmp[i++] = *s++; + } + + while (*s == '0' && isdigit(s[1])) + { + s++; + } + + for (; *s && i < (size_t)w && i + 1 < sizeof tmp; i++) + { + tmp[i] = *s++; + } + + tmp[i] = 0; + char *p = strptime(tmp, "%12Y-%m-%d", tm); + if (!p) + { + return NULL; + } + + s -= tmp + i - p; + 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': + + /* Skip whitespace */ + + for (; *s && isspace(*s); s++); + break; + + case 'R': + + /* Time in HH:MM format */ + + s = strptime(s, "%H:%M", tm); + if (!s) + { + return NULL; + } + + break; + + case 's': + + /* Seconds since epoch (effect on tm is unspecified) */ + + if (*s == '-') + { + s++; + } + + if (!isdigit(*s)) + { + return NULL; + } + + while (isdigit(*s)) + { + s++; + } + + break; + + case 'S': + dest = &tm->tm_sec; + min = 0; + range = 61; + goto numeric_range; + + case 'T': + + /* Time in HH:MM:SS format */ + + s = strptime(s, "%H:%M:%S", tm); + if (!s) + { + return NULL; + } + + break; + + case 'U': case 'W': + + /* These specifiers are ignored */ + + dest = &dummy; + min = 0; + range = 54; + goto numeric_range; + + case 'V': + dest = &dummy; + min = 1; + range = 53; + goto numeric_range; + + case 'g': + dest = &dummy; + w = 2; + goto numeric_digits; + + case 'G': + dest = &dummy; + if (w < 0) + { + w = 4; + } + + goto numeric_digits; + + case 'u': + dest = &tm->tm_wday; + min = 1; + range = 7; + goto numeric_range; + + case 'w': + dest = &tm->tm_wday; + min = 0; + range = 7; + goto numeric_range; + + 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 'z': + + /* Timezone offset from UTC */ + + if (*s == '+' || *s == '-') + { + neg = (*s == '-'); + s++; + } + + else + { + return NULL; + } + + for (i = 0; i < 4; i++) + { + if (!isdigit(s[1 + i])) + { + return 0; + } + } + + tm->tm_gmtoff = (s[1] - '0') * 36000 + (s[2] - '0') * 3600 + + (s[3] - '0') * 600 + (s[4] - '0') * 60; + if (neg) + { + tm->tm_gmtoff = -tm->tm_gmtoff; + } + + s += 5; + break; + +#ifdef CONFIG_LIBC_LOCALTIME + case 'Z': + + /* Timezone abbreviation */ + + if (!strncmp(s, tzname[0], len = strlen(tzname[0]))) + { + tm->tm_isdst = 0; + s += len; + } + else if (!strncmp(s, tzname[1], len = strlen(tzname[1]))) + { + tm->tm_isdst = 1; + s += len; + } + else + { + /* Skip unknown timezone abbreviations */ + + while ((*s | 32) - 'a' <= 'z' - 'a') s++; + } + + break; +#endif + + case '%': + + /* Literal '%' character */ + + if (*s++ != '%') + { + return NULL; + } + break; + + default: + return NULL; + + numeric_range: + + /* Process numeric values with a range check */ + + if (!isdigit(*s)) + { + return NULL; + } + + *dest = 0; + for (i = 1; i <= min + range && isdigit(*s); i *= 10) + { + *dest = *dest * 10 + *s++ - '0'; + } + + if (*dest < min || *dest > min + range) + { + return NULL; + } + + *dest -= adj; + goto update; + + numeric_digits: + + /* Process numeric values with a fixed width */ + + neg = 0; + if (*s == '+') + { + s++; + } + else if (*s == '-') + { + neg = 1; + s++; + } + + if (!isdigit(*s)) + { + return NULL; + } + + for (*dest = i = 0; i < w && isdigit(*s); i++) + { + *dest = *dest * 10 + *s++ - '0'; + } + + if (neg) + { + *dest = -*dest; + } + + *dest -= adj; + if (*dest < 0 || (*dest > 99 && w == 2)) + { + return NULL; + } + + goto update; + +#ifdef CONFIG_LIBC_LOCALE + case 'a': case 'A': + dest = &tm->tm_wday; + min = ABDAY_1; + range = 7; + goto symbolic_range; + + case 'b': case 'B': case 'h': + dest = &tm->tm_mon; + min = ABMON_1; + range = 12; + goto symbolic_range; + + case 'c': + + /* Locale-specific date and time format */ + + s = strptime(s, nl_langinfo(D_T_FMT), tm); + if (!s) + { + return NULL; + } + + break; + + case 'p': + + /* AM/PM */ + + ex = nl_langinfo(AM_STR); + len = strlen(ex); + if (!strncasecmp(s, ex, len)) + { + tm->tm_hour %= 12; + s += len; + break; + } + + ex = nl_langinfo(PM_STR); + len = strlen(ex); + if (!strncasecmp(s, ex, len)) + { + tm->tm_hour %= 12; + tm->tm_hour += 12; + s += len; + break; + } + + return 0; + + case 'r': + + /* Time in 12-hour format with AM/PM */ + + s = strptime(s, nl_langinfo(T_FMT_AMPM), tm); + if (!s) + { + return NULL; + } + + break; + + case 'x': + + /* Date in locale-specific format */ + + s = strptime(s, nl_langinfo(D_FMT), tm); + if (!s) + { + return NULL; + } + + break; + + case 'X': + + /* Time in locale-specific format */ + + s = strptime(s, nl_langinfo(T_FMT), tm); + if (!s) + { + return NULL; + } + + break; + + symbolic_range: + + /* Process symbolic names for days of the week and months */ + + for (i = 2 * range - 1; i >= 0; i--) + { + ex = nl_langinfo(min + i); + len = strlen(ex); + if (strncasecmp(s, ex, len)) + { + continue; + } + + s += len; + *dest = i % range; + break; + } + + if (i < 0) + { + return NULL; + } + + goto update; +#endif + + update: + + /* Placeholder for additional updates (if needed) */ + + ; + } + } + + /* Finalize year calculations */ + + 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 (FAR char *)s; +}