From 477b65eac08a8b28c7dc4607ae51af28a31b90fc Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 7 Aug 2016 15:32:10 +0100 Subject: Time: Expose arrays of weekday and month names. --- utils/time.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/utils/time.c b/utils/time.c index cf8acc0d6..6d53b16e1 100644 --- a/utils/time.c +++ b/utils/time.c @@ -35,20 +35,86 @@ #include "utils/time.h" + +/** + * Weekdays + * + * Must be calender order. + */ +enum nsc_time_weekdays { + NSC_TIME_WEEKDAY_SUN, + NSC_TIME_WEEKDAY_MON, + NSC_TIME_WEEKDAY_TUE, + NSC_TIME_WEEKDAY_WED, + NSC_TIME_WEEKDAY_THU, + NSC_TIME_WEEKDAY_FRI, + NSC_TIME_WEEKDAY_SAT, + NSC_TIME_WEEKDAY__COUNT +}; +/** + * Months + * + * Must be calender order. + */ +enum nsc_time_months { + NSC_TIME_MONTH_JAN, + NSC_TIME_MONTH_FEB, + NSC_TIME_MONTH_MAR, + NSC_TIME_MONTH_APR, + NSC_TIME_MONTH_MAY, + NSC_TIME_MONTH_JUN, + NSC_TIME_MONTH_JUL, + NSC_TIME_MONTH_AUG, + NSC_TIME_MONTH_SEP, + NSC_TIME_MONTH_OCT, + NSC_TIME_MONTH_NOV, + NSC_TIME_MONTH_DEC, + NSC_TIME_MONTH__COUNT, +}; + + +/** + * Array of short weekday names. + */ +static const char * const weekdays_short[NSC_TIME_WEEKDAY__COUNT] = { + [NSC_TIME_WEEKDAY_SUN] = "Sun", + [NSC_TIME_WEEKDAY_MON] = "Mon", + [NSC_TIME_WEEKDAY_TUE] = "Tue", + [NSC_TIME_WEEKDAY_WED] = "Wed", + [NSC_TIME_WEEKDAY_THU] = "Thu", + [NSC_TIME_WEEKDAY_FRI] = "Fri", + [NSC_TIME_WEEKDAY_SAT] = "Sat" +}; +/** + * Array of month names. + */ +static const char * const months[NSC_TIME_MONTH__COUNT] = { + [NSC_TIME_MONTH_JAN] = "Jan", + [NSC_TIME_MONTH_FEB] = "Feb", + [NSC_TIME_MONTH_MAR] = "Mar", + [NSC_TIME_MONTH_APR] = "Apr", + [NSC_TIME_MONTH_MAY] = "May", + [NSC_TIME_MONTH_JUN] = "Jun", + [NSC_TIME_MONTH_JUL] = "Jul", + [NSC_TIME_MONTH_AUG] = "Aug", + [NSC_TIME_MONTH_SEP] = "Sep", + [NSC_TIME_MONTH_OCT] = "Oct", + [NSC_TIME_MONTH_NOV] = "Nov", + [NSC_TIME_MONTH_DEC] = "Dec" +}; + + /* exported interface documented in utils/time.h */ const char *rfc1123_date(time_t t) { static char ret[30]; struct tm *tm = gmtime(&t); - const char *days[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }, - *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; snprintf(ret, sizeof ret, "%s, %02d %s %d %02d:%02d:%02d GMT", - days[tm->tm_wday], tm->tm_mday, months[tm->tm_mon], - tm->tm_year + 1900, tm->tm_hour, tm->tm_min, - tm->tm_sec); + weekdays_short[tm->tm_wday], tm->tm_mday, + months[tm->tm_mon], tm->tm_year + 1900, + tm->tm_hour, tm->tm_min, tm->tm_sec); return ret; } -- cgit v1.2.3 From 16b66d3af521e79c8c003fb51ca6b8c64fec4d86 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 7 Aug 2016 17:29:13 +0100 Subject: Utils: Add a set of ASCII string parsing helpers. These are not affected by the current locale. --- utils/ascii.h | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 utils/ascii.h diff --git a/utils/ascii.h b/utils/ascii.h new file mode 100644 index 000000000..f08e756a0 --- /dev/null +++ b/utils/ascii.h @@ -0,0 +1,358 @@ +/* + * Copyright 2016 Michael Drake + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file utils/ascii.h + * \brief Helpers for ASCII string handling. + * + * These helpers for string parsing will have the correct effect for parsing + * ASCII text (as used by most web specs), regardless of system locale. + */ + +#ifndef _NETSURF_UTILS_ASCII_H_ +#define _NETSURF_UTILS_ASCII_H_ + +#include +#include +#include + +/** + * Test whether a character is a whitespace character. + * + * \param[in] c Character to test. + * \return true iff `c` is whitespace, else false. + */ +static inline bool ascii_is_space(char c) +{ + return (c == ' ' || c == '\t' || + c == '\n' || c == '\v' || + c == '\f' || c == '\r'); +} + +/** + * Test whether a character is lower-case alphabetical. + * + * \param[in] c Character to test. + * \return true iff `c` is lower-case alphabetical, else false. + */ +static inline bool ascii_is_alpha_lower(char c) +{ + return (c >= 'a' && c <= 'z'); +} + +/** + * Test whether a character is upper-case alphabetical. + * + * \param[in] c Character to test. + * \return true iff `c` is upper-case alphabetical, else false. + */ +static inline bool ascii_is_alpha_upper(char c) +{ + return (c >= 'A' && c <= 'Z'); +} + +/** + * Test whether a character is alphabetical (upper or lower case). + * + * \param[in] c Character to test. + * \return true iff `c` is alphabetical, else false. + */ +static inline bool ascii_is_alpha(char c) +{ + return (ascii_is_alpha_lower(c) || ascii_is_alpha_upper(c)); +} + +/** + * Test whether a character is a decimal digit. + * + * \param[in] c Character to test. + * \return true iff `c` is a decimal digit, else false. + */ +static inline bool ascii_is_digit(char c) +{ + return (c >= '0' && c <= '9'); +} + +/** + * Test whether a character is a positive/negative numerical sign. + * + * \param[in] c Character to test. + * \return true iff `c` is a sign, else false. + */ +static inline bool ascii_is_sign(char c) +{ + return (c == '-' || c == '+'); +} + +/** + * Test whether a character is alphanumerical (upper or lower case). + * + * \param[in] c Character to test. + * \return true iff `c` is alphanumerical, else false. + */ +static inline bool ascii_is_alphanumerical(char c) +{ + return (ascii_is_alpha(c) || ascii_is_digit(c)); +} + +/** + * Test whether a character is hexadecimal (lower case). + * + * \param[in] c Character to test. + * \return true iff `c` is hexadecimal, else false. + */ +static inline bool ascii_is_hex_lower(char c) +{ + return (ascii_is_digit(c) || + (c >= 'a' && c <= 'f')); +} + +/** + * Test whether a character is hexadecimal (upper case). + * + * \param[in] c Character to test. + * \return true iff `c` is hexadecimal, else false. + */ +static inline bool ascii_is_hex_upper(char c) +{ + return (ascii_is_digit(c) || + (c >= 'A' && c <= 'F')); +} + +/** + * Test whether a character is hexadecimal (upper or lower case). + * + * \param[in] c Character to test. + * \return true iff `c` is hexadecimal, else false. + */ +static inline bool ascii_is_hex(char c) +{ + return (ascii_is_digit(c) || + (c >= 'A' && c <= 'F') || + (c >= 'a' && c <= 'f')); +} + +/** + * Convert an upper case character to lower case. + * + * If the given character is not upper case alphabetical, it is returned + * unchanged. + * + * \param[in] c Character to convert. + * \return lower case conversion of `c` else `c`. + */ +static inline char ascii_to_lower(char c) +{ + return (ascii_is_alpha_upper(c)) ? (c + 'a' - 'A') : c; +} + +/** + * Convert a lower case character to upper case. + * + * If the given character is not lower case alphabetical, it is returned + * unchanged. + * + * \param[in] c Character to convert. + * \return upper case conversion of `c` else `c`. + */ +static inline char ascii_to_upper(char c) +{ + return (ascii_is_alpha_lower(c)) ? (c + 'A' - 'a') : c; +} + +/** + * Count consecutive lower case alphabetical characters in string. + * + * \param[in] str String to count characters in. + * \return number of consecutive lower case characters at start of `str`. + */ +static inline size_t ascii_count_alpha_lower(const char *str) +{ + size_t count = 0; + while (ascii_is_alpha_lower(*(str++))) { + count++; + } + return count; +} + +/** + * Count consecutive upper case alphabetical characters in string. + * + * \param[in] str String to count characters in. + * \return number of consecutive upper case characters at start of `str`. + */ +static inline size_t ascii_count_alpha_upper(const char *str) +{ + size_t count = 0; + while (ascii_is_alpha_upper(*(str++))) { + count++; + } + return count; +} + +/** + * Count consecutive alphabetical characters in string (upper or lower case). + * + * \param[in] str String to count characters in. + * \return number of consecutive alphabetical characters at start of `str`. + */ +static inline size_t ascii_count_alpha(const char *str) +{ + size_t count = 0; + while (ascii_is_alpha(*(str++))) { + count++; + } + return count; +} + +/** + * Count consecutive decial digit characters in string. + * + * \param[in] str String to count characters in. + * \return number of consecutive decimal digit characters at start of `str`. + */ +static inline size_t ascii_count_digit(const char *str) +{ + size_t count = 0; + while (ascii_is_digit(*(str++))) { + count++; + } + return count; +} + +/** + * Count consecutive characters either decimal digit or colon in string. + * + * \param[in] str String to count characters in. + * \return number of consecutive decimal or ':' characters at start of `str`. + */ +static inline size_t ascii_count_digit_or_colon(const char *str) +{ + size_t count = 0; + while (ascii_is_digit(*str) || *str == ':') { + count++; + str++; + } + return count; +} + +/** + * Test for string equality (case insensitive). + * + * \param[in] s1 First string to compare. + * \param[in] s2 Second string to compare. + * \return true iff strings are equivalent, else false. + */ +static inline bool ascii_strings_equal_caseless( + const char *s1, const char *s2) +{ + while (*s1 != '\0') { + if (ascii_to_lower(*s1) != ascii_to_lower(*s2)) { + break; + } + s1++; + s2++; + } + return (ascii_to_lower(*s1) == ascii_to_lower(*s2)); +} + +/** + * Test for string equality (case sensitive). + * + * \param[in] s1 First string to compare. + * \param[in] s2 Second string to compare. + * \return true iff strings are equal, else false. + */ +static inline bool ascii_strings_equal( + const char *s1, const char *s2) +{ + while (*s1 != '\0') { + if (*s1 != *s2) { + break; + } + s1++; + s2++; + } + return (*s1 == *s2); +} + +/** + * Count consecutive equal ascii characters (case insensitive). + * + * \param[in] s1 First string to compare. + * \param[in] s2 Second string to compare. + * \return number of equivalent characters. + */ +static inline size_t ascii_strings_count_equal_caseless( + const char *s1, const char *s2) +{ + const char *s = s1; + while (*s1 != '\0') { + if (ascii_to_lower(*s1) != ascii_to_lower(*s2)) { + break; + } + s1++; + s2++; + } + return s1 - s; +} + +/** + * Count consecutive equal ascii characters (case sensitive). + * + * \param[in] s1 First string to compare. + * \param[in] s2 Second string to compare. + * \return number of equal characters. + */ +static inline size_t ascii_strings_count_equal( + const char *s1, const char *s2) +{ + const char *s = s1; + while (*s1 != '\0') { + if (*s1 != *s2) { + break; + } + s1++; + s2++; + } + return s1 - s; +} + +/** + * Parse an int out of a string. + * + * \param[in] str String to parse integer out of. + * \param[out] res Returns parsed integer. + * \return The number of characters consumed in `str`. + * Returning 0 indicates failure to parse an integer out of the string. + */ +static inline size_t ascii_string_to_int(const char *str, int *res) +{ + char *end = NULL; + long long temp = strtoll(str, &end, 10); + + if (end == str || errno == ERANGE || + temp < INT_MIN || temp > INT_MAX) { + return 0; + } + + *res = temp; + return end - str; +} + +#endif -- cgit v1.2.3 From efeeacca8621076f285ead0fc7e95d61938daea6 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Fri, 12 Aug 2016 09:11:17 +0100 Subject: nsurl: Update to use ascii_* helper functions. --- utils/nsurl.c | 127 +++++++--------------------------------------------------- 1 file changed, 15 insertions(+), 112 deletions(-) diff --git a/utils/nsurl.c b/utils/nsurl.c index 6582264b8..c5c614c55 100644 --- a/utils/nsurl.c +++ b/utils/nsurl.c @@ -32,12 +32,12 @@ */ #include -#include #include #include #include #include +#include "utils/ascii.h" #include "utils/corestrings.h" #include "utils/errors.h" #include "utils/idna.h" @@ -48,28 +48,6 @@ /* Define to enable NSURL debugging */ #undef NSURL_DEBUG -/** ascii character codes */ -enum ascii_codepoints { - ASCII_NUL = 0, - ASCII_SPC = 0x20, - ASCII_FF = 0x0C, - ASCII_NL = 0x0A, - ASCII_CR = 0x0D, - ASCII_HT = 0x09, - ASCII_VT = 0x0B, - ASCII_PLUS = 0x2b, - ASCII_MINUS = 0x2d, - ASCII_FULLSTOP = 0x2e, - ASCII_SLASH = 0x2F, - ASCII_0 = 0x30, - ASCII_9 = 0x39, - ASCII_COLON = 0x3A, - ASCII_A = 0x41, - ASCII_Z = 0x5A, - ASCII_a = 0x61, - ASCII_z = 0x7A -}; - /** * nsurl scheme type */ @@ -207,79 +185,6 @@ inline static char digit2uppercase_hex(unsigned char digit) { return "0123456789ABCDEF"[digit]; } -/** - * determines if a character is a whitespace in the ascii character encoding - * - * whitespace characters are space, form feed, new line, carrige - * return, horizontal tab and vertical tab. - * - * \param c character to classify - * \return zero if the character is not whitespace else 1 - */ -inline static int is_ascii_space(int c) -{ - if (c == ASCII_SPC || - c == ASCII_FF || - c == ASCII_NL || - c == ASCII_CR || - c == ASCII_HT || - c == ASCII_VT) { - return 1; - } - return 0; -} - -/** - * determine if a character is alphabetical in the ascii character encoding - * - * characters in the range A-Z and a-z are considered alphabetical. - * - * \param c character to classify - * \return zero if the character is not alphabetical else 1 - */ -inline static int is_ascii_alpha(int c) -{ - if (((c >= ASCII_A) && (c <= ASCII_Z)) || - ((c >= ASCII_a) && (c <= ASCII_z))) { - return 1; - } - return 0; -} - -/** - * determine if a character is a number in the ascii character encoding - * - * characters in the range 0-9 are considered numbers. - * - * \param c character to classify - * \return 1 if the character is a number else 0 - */ -inline static int is_ascii_digit(int c) -{ - if ((c >= ASCII_0) && (c <= ASCII_9)) { - return 1; - } - return 0; -} - -/** - * determine if a character is alphanumerical in the ascii character encoding - * - * characters in the range A-Z, a-z and 0-9 are considered alphanumeric. - * - * \param c character to classify - * \return zero if the character is not alphanumerical else 1 - */ -inline static int is_ascii_alnum(int c) -{ - if (((c >= ASCII_0) && (c <= ASCII_9)) || - ((c >= ASCII_A) && (c <= ASCII_Z)) || - ((c >= ASCII_a) && (c <= ASCII_z))) { - return 1; - } - return 0; -} - /** * determine if a character is unreserved * @@ -397,7 +302,7 @@ static void nsurl__get_string_markers(const char * const url_s, 0, 0, 0, 0, NSURL_SCHEME_OTHER }; /* Skip any leading whitespace in url_s */ - while (is_ascii_space(*pos)) + while (ascii_is_space(*pos)) pos++; /* Record start point */ @@ -406,7 +311,7 @@ static void nsurl__get_string_markers(const char * const url_s, marker.scheme_end = marker.authority = marker.colon_first = marker.at = marker.colon_last = marker.path = marker.start; - if (*pos == ASCII_NUL) { + if (*pos == '\0') { /* Nothing but whitespace, early exit */ marker.query = marker.fragment = marker.end = marker.path; *markers = marker; @@ -414,14 +319,12 @@ static void nsurl__get_string_markers(const char * const url_s, } /* Get scheme */ - if (is_ascii_alpha(*pos)) { + if (ascii_is_alpha(*pos)) { pos++; - while (*pos != ASCII_COLON && *pos != ASCII_NUL) { - if (!is_ascii_alnum(*pos) && - (*pos != ASCII_PLUS) && - (*pos != ASCII_MINUS) && - (*pos != ASCII_FULLSTOP)) { + while (*pos != ':' && *pos != '\0') { + if (!ascii_is_alphanumerical(*pos) && (*pos != '+') && + (*pos != '-') && (*pos != '.')) { /* This character is not valid in the * scheme */ break; @@ -429,7 +332,7 @@ static void nsurl__get_string_markers(const char * const url_s, pos++; } - if (*pos == ASCII_COLON) { + if (*pos == ':') { /* This delimits the end of the scheme */ size_t off; @@ -607,9 +510,9 @@ static void nsurl__get_string_markers(const char * const url_s, /* We got to the end of url_s. * Need to skip back over trailing whitespace to find end of URL */ pos--; - if (pos >= url_s && is_ascii_space(*pos)) { + if (pos >= url_s && ascii_is_space(*pos)) { trailing_whitespace = true; - while (pos >= url_s && is_ascii_space(*pos)) + while (pos >= url_s && ascii_is_space(*pos)) pos--; } @@ -790,7 +693,7 @@ static inline int nsurl__get_ascii_offset(char c1, char c2) int offset; /* Use 1st char as most significant hex digit */ - if (is_ascii_digit(c1)) + if (ascii_is_digit(c1)) offset = 16 * (c1 - '0'); else if (c1 >= 'a' && c1 <= 'f') offset = 16 * (c1 - 'a' + 10); @@ -801,7 +704,7 @@ static inline int nsurl__get_ascii_offset(char c1, char c2) return -1; /* Use 2nd char as least significant hex digit and sum */ - if (is_ascii_digit(c2)) + if (ascii_is_digit(c2)) offset += c2 - '0'; else if (c2 >= 'a' && c2 <= 'f') offset += c2 - 'a' + 10; @@ -953,7 +856,7 @@ static nserror nsurl__create_from_section(const char * const url_s, length += 2; } else if ((section == URL_SCHEME || section == URL_HOST) && - isupper(*pos)) { + ascii_is_alpha_upper(*pos)) { /* Lower case this letter */ if (copy_len > 0) { @@ -963,7 +866,7 @@ static nserror nsurl__create_from_section(const char * const url_s, copy_len = 0; } /* Copy lower cased letter into normalised URL */ - *(pos_norm++) = tolower(*pos); + *(pos_norm++) = ascii_to_lower(*pos); pos_url_s = pos + 1; } else { @@ -1058,7 +961,7 @@ static nserror nsurl__create_from_section(const char * const url_s, */ sec_start += colon - pegs->at; while (++sec_start < norm_start + length) { - if (!is_ascii_digit(*sec_start)) { + if (!ascii_is_digit(*sec_start)) { /* Character after port isn't a * digit; not a port separator */ -- cgit v1.2.3 From a1668f6c1e0c9e88f62f6082c6206c4d33f25cce Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Fri, 12 Aug 2016 09:18:27 +0100 Subject: url: Use ascii module, rather than ctype for locale safetly. --- utils/url.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/url.c b/utils/url.c index 7a7b7a196..ee8485040 100644 --- a/utils/url.c +++ b/utils/url.c @@ -35,7 +35,9 @@ #include #include #include +#include +#include "utils/ascii.h" #include "utils/config.h" #include "utils/log.h" #include "utils/url.h" @@ -92,7 +94,7 @@ nserror url_unescape(const char *str, size_t length, char c1 = *(str + 1); char c2 = *(str + 2); - if (c == '%' && isxdigit(c1) && isxdigit(c2)) { + if (c == '%' && ascii_is_hex(c1) && ascii_is_hex(c2)) { c = xdigit_to_hex(c1) << 4 | xdigit_to_hex(c2); str += 2; } -- cgit v1.2.3 From f23405f667e7514cff93ebc83cb6d1f7ab9005c0 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sun, 7 Aug 2016 17:31:28 +0100 Subject: Time: Add date string to time_t parser. --- utils/time.c | 813 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 810 insertions(+), 3 deletions(-) diff --git a/utils/time.c b/utils/time.c index 6d53b16e1..14f4004b9 100644 --- a/utils/time.c +++ b/utils/time.c @@ -25,17 +25,19 @@ * \brief Implementation of time operations. */ -#include #include -#include +#include +#include +#ifdef WITH_CURL #include +#endif +#include "utils/ascii.h" #include "utils/errors.h" #include "utils/time.h" - /** * Weekdays * @@ -181,11 +183,814 @@ nserror nsc_snptimet(const char *str, size_t size, time_t *timep) } +#ifndef WITH_CURL + + +/** + * Array of long weekday names. + */ +static const char * const weekdays_long[NSC_TIME_WEEKDAY__COUNT] = { + [NSC_TIME_WEEKDAY_SUN] = "Sunday", + [NSC_TIME_WEEKDAY_MON] = "Monday", + [NSC_TIME_WEEKDAY_TUE] = "Tuesday", + [NSC_TIME_WEEKDAY_WED] = "Wednesday", + [NSC_TIME_WEEKDAY_THU] = "Thursday", + [NSC_TIME_WEEKDAY_FRI] = "Friday", + [NSC_TIME_WEEKDAY_SAT] = "Saturday" +}; + +/** + * Timezone offsets in mins + * + * Order doesn't matter. + */ +enum nsc_time_zone_offsets { + /* Timezones */ + NSC_TIME_ZONE_OFFSET_IDLE = -12 * 60, + NSC_TIME_ZONE_OFFSET_NZST = -12 * 60, + NSC_TIME_ZONE_OFFSET_NZT = -12 * 60, + NSC_TIME_ZONE_OFFSET_EAST = -10 * 60, + NSC_TIME_ZONE_OFFSET_GST = -10 * 60, + NSC_TIME_ZONE_OFFSET_JST = - 9 * 60, + NSC_TIME_ZONE_OFFSET_CCT = - 8 * 60, + NSC_TIME_ZONE_OFFSET_WAST = - 7 * 60, + NSC_TIME_ZONE_OFFSET_EET = - 2 * 60, + NSC_TIME_ZONE_OFFSET_CET = - 1 * 60, + NSC_TIME_ZONE_OFFSET_FWT = - 1 * 60, + NSC_TIME_ZONE_OFFSET_MET = - 1 * 60, + NSC_TIME_ZONE_OFFSET_MEWT = - 1 * 60, + NSC_TIME_ZONE_OFFSET_GMT = 0, + NSC_TIME_ZONE_OFFSET_UTC = 0, + NSC_TIME_ZONE_OFFSET_WET = 0, + NSC_TIME_ZONE_OFFSET_WAT = 1 * 60, + NSC_TIME_ZONE_OFFSET_AST = 4 * 60, + NSC_TIME_ZONE_OFFSET_EST = 5 * 60, + NSC_TIME_ZONE_OFFSET_CST = 6 * 60, + NSC_TIME_ZONE_OFFSET_MST = 7 * 60, + NSC_TIME_ZONE_OFFSET_PST = 8 * 60, + NSC_TIME_ZONE_OFFSET_YST = 9 * 60, + NSC_TIME_ZONE_OFFSET_AHST = 10 * 60, + NSC_TIME_ZONE_OFFSET_CAT = 10 * 60, + NSC_TIME_ZONE_OFFSET_HST = 10 * 60, + NSC_TIME_ZONE_OFFSET_IDLW = 12 * 60, + + /* Daylight saving modified timezones */ + NSC_TIME_ZONE_OFFSET_NZDT = NSC_TIME_ZONE_OFFSET_NZT - 60, + NSC_TIME_ZONE_OFFSET_EADT = NSC_TIME_ZONE_OFFSET_EAST - 60, + NSC_TIME_ZONE_OFFSET_WADT = NSC_TIME_ZONE_OFFSET_WAST - 60, + NSC_TIME_ZONE_OFFSET_CEST = NSC_TIME_ZONE_OFFSET_CET - 60, + NSC_TIME_ZONE_OFFSET_FST = NSC_TIME_ZONE_OFFSET_FWT - 60, + NSC_TIME_ZONE_OFFSET_MEST = NSC_TIME_ZONE_OFFSET_MET - 60, + NSC_TIME_ZONE_OFFSET_MESZ = NSC_TIME_ZONE_OFFSET_MET - 60, + NSC_TIME_ZONE_OFFSET_BST = NSC_TIME_ZONE_OFFSET_GMT - 60, + NSC_TIME_ZONE_OFFSET_ADT = NSC_TIME_ZONE_OFFSET_AST - 60, + NSC_TIME_ZONE_OFFSET_EDT = NSC_TIME_ZONE_OFFSET_EST - 60, + NSC_TIME_ZONE_OFFSET_CDT = NSC_TIME_ZONE_OFFSET_CST - 60, + NSC_TIME_ZONE_OFFSET_MDT = NSC_TIME_ZONE_OFFSET_MST - 60, + NSC_TIME_ZONE_OFFSET_PDT = NSC_TIME_ZONE_OFFSET_PST - 60, + NSC_TIME_ZONE_OFFSET_YDT = NSC_TIME_ZONE_OFFSET_YST - 60, + NSC_TIME_ZONE_OFFSET_HDT = NSC_TIME_ZONE_OFFSET_HST - 60, + + /* Military timezones */ + NSC_TIME_ZONE_OFFSET_Y = -12 * 60, + NSC_TIME_ZONE_OFFSET_X = -11 * 60, + NSC_TIME_ZONE_OFFSET_W = -10 * 60, + NSC_TIME_ZONE_OFFSET_V = - 9 * 60, + NSC_TIME_ZONE_OFFSET_U = - 8 * 60, + NSC_TIME_ZONE_OFFSET_T = - 7 * 60, + NSC_TIME_ZONE_OFFSET_S = - 6 * 60, + NSC_TIME_ZONE_OFFSET_R = - 5 * 60, + NSC_TIME_ZONE_OFFSET_Q = - 4 * 60, + NSC_TIME_ZONE_OFFSET_P = - 3 * 60, + NSC_TIME_ZONE_OFFSET_O = - 2 * 60, + NSC_TIME_ZONE_OFFSET_N = - 1 * 60, + NSC_TIME_ZONE_OFFSET_Z = 0 * 60, + NSC_TIME_ZONE_OFFSET_A = 1 * 60, + NSC_TIME_ZONE_OFFSET_B = 2 * 60, + NSC_TIME_ZONE_OFFSET_C = 3 * 60, + NSC_TIME_ZONE_OFFSET_D = 4 * 60, + NSC_TIME_ZONE_OFFSET_E = 5 * 60, + NSC_TIME_ZONE_OFFSET_F = 6 * 60, + NSC_TIME_ZONE_OFFSET_G = 7 * 60, + NSC_TIME_ZONE_OFFSET_H = 8 * 60, + NSC_TIME_ZONE_OFFSET_I = 9 * 60, + NSC_TIME_ZONE_OFFSET_K = 10 * 60, + NSC_TIME_ZONE_OFFSET_L = 11 * 60, + NSC_TIME_ZONE_OFFSET_M = 12 * 60, +}; + +/** + * List of timezones. + * + * The order here is the order they appear in the `timezone_mins` array. + * So there is value in putting the most common timezones first. + */ +enum nsc_time_zones { + NSC_TIME_ZONE_IDLE, + NSC_TIME_ZONE_NZST, + NSC_TIME_ZONE_NZT, + NSC_TIME_ZONE_EAST, + NSC_TIME_ZONE_GST, + NSC_TIME_ZONE_JST, + NSC_TIME_ZONE_CCT, + NSC_TIME_ZONE_WAST, + NSC_TIME_ZONE_EET, + NSC_TIME_ZONE_CET, + NSC_TIME_ZONE_FWT, + NSC_TIME_ZONE_MET, + NSC_TIME_ZONE_MEWT, + NSC_TIME_ZONE_GMT, + NSC_TIME_ZONE_UTC, + NSC_TIME_ZONE_WET, + NSC_TIME_ZONE_WAT, + NSC_TIME_ZONE_AST, + NSC_TIME_ZONE_EST, + NSC_TIME_ZONE_CST, + NSC_TIME_ZONE_MST, + NSC_TIME_ZONE_PST, + NSC_TIME_ZONE_YST, + NSC_TIME_ZONE_AHST, + NSC_TIME_ZONE_CAT, + NSC_TIME_ZONE_HST, + NSC_TIME_ZONE_IDLW, + NSC_TIME_ZONE_NZDT, + NSC_TIME_ZONE_EADT, + NSC_TIME_ZONE_WADT, + NSC_TIME_ZONE_CEST, + NSC_TIME_ZONE_FST, + NSC_TIME_ZONE_MEST, + NSC_TIME_ZONE_MESZ, + NSC_TIME_ZONE_BST, + NSC_TIME_ZONE_ADT, + NSC_TIME_ZONE_EDT, + NSC_TIME_ZONE_CDT, + NSC_TIME_ZONE_MDT, + NSC_TIME_ZONE_PDT, + NSC_TIME_ZONE_YDT, + NSC_TIME_ZONE_HDT, + NSC_TIME_ZONE_Y, + NSC_TIME_ZONE_X, + NSC_TIME_ZONE_W, + NSC_TIME_ZONE_V, + NSC_TIME_ZONE_U, + NSC_TIME_ZONE_T, + NSC_TIME_ZONE_S, + NSC_TIME_ZONE_R, + NSC_TIME_ZONE_Q, + NSC_TIME_ZONE_P, + NSC_TIME_ZONE_O, + NSC_TIME_ZONE_N, + NSC_TIME_ZONE_Z, + NSC_TIME_ZONE_A, + NSC_TIME_ZONE_B, + NSC_TIME_ZONE_C, + NSC_TIME_ZONE_D, + NSC_TIME_ZONE_E, + NSC_TIME_ZONE_F, + NSC_TIME_ZONE_G, + NSC_TIME_ZONE_H, + NSC_TIME_ZONE_I, + NSC_TIME_ZONE_K, + NSC_TIME_ZONE_L, + NSC_TIME_ZONE_M, + NSC_TIME_ZONE__COUNT +}; + +/** + * Array of minute offsets for timezones. + */ +static const int16_t timezone_mins[NSC_TIME_ZONE__COUNT] = { + [NSC_TIME_ZONE_IDLE] = NSC_TIME_ZONE_OFFSET_IDLE, + [NSC_TIME_ZONE_NZST] = NSC_TIME_ZONE_OFFSET_NZST, + [NSC_TIME_ZONE_NZT] = NSC_TIME_ZONE_OFFSET_NZT, + [NSC_TIME_ZONE_EAST] = NSC_TIME_ZONE_OFFSET_EAST, + [NSC_TIME_ZONE_GST] = NSC_TIME_ZONE_OFFSET_GST, + [NSC_TIME_ZONE_JST] = NSC_TIME_ZONE_OFFSET_JST, + [NSC_TIME_ZONE_CCT] = NSC_TIME_ZONE_OFFSET_CCT, + [NSC_TIME_ZONE_WAST] = NSC_TIME_ZONE_OFFSET_WAST, + [NSC_TIME_ZONE_EET] = NSC_TIME_ZONE_OFFSET_EET, + [NSC_TIME_ZONE_CET] = NSC_TIME_ZONE_OFFSET_CET, + [NSC_TIME_ZONE_FWT] = NSC_TIME_ZONE_OFFSET_FWT, + [NSC_TIME_ZONE_MET] = NSC_TIME_ZONE_OFFSET_MET, + [NSC_TIME_ZONE_MEWT] = NSC_TIME_ZONE_OFFSET_MEWT, + [NSC_TIME_ZONE_GMT] = NSC_TIME_ZONE_OFFSET_GMT, + [NSC_TIME_ZONE_UTC] = NSC_TIME_ZONE_OFFSET_UTC, + [NSC_TIME_ZONE_WET] = NSC_TIME_ZONE_OFFSET_WET, + [NSC_TIME_ZONE_WAT] = NSC_TIME_ZONE_OFFSET_WAT, + [NSC_TIME_ZONE_AST] = NSC_TIME_ZONE_OFFSET_AST, + [NSC_TIME_ZONE_EST] = NSC_TIME_ZONE_OFFSET_EST, + [NSC_TIME_ZONE_CST] = NSC_TIME_ZONE_OFFSET_CST, + [NSC_TIME_ZONE_MST] = NSC_TIME_ZONE_OFFSET_MST, + [NSC_TIME_ZONE_PST] = NSC_TIME_ZONE_OFFSET_PST, + [NSC_TIME_ZONE_YST] = NSC_TIME_ZONE_OFFSET_YST, + [NSC_TIME_ZONE_AHST] = NSC_TIME_ZONE_OFFSET_AHST, + [NSC_TIME_ZONE_CAT] = NSC_TIME_ZONE_OFFSET_CAT, + [NSC_TIME_ZONE_HST] = NSC_TIME_ZONE_OFFSET_HST, + [NSC_TIME_ZONE_IDLW] = NSC_TIME_ZONE_OFFSET_IDLW, + [NSC_TIME_ZONE_NZDT] = NSC_TIME_ZONE_OFFSET_NZDT, + [NSC_TIME_ZONE_EADT] = NSC_TIME_ZONE_OFFSET_EADT, + [NSC_TIME_ZONE_WADT] = NSC_TIME_ZONE_OFFSET_WADT, + [NSC_TIME_ZONE_CEST] = NSC_TIME_ZONE_OFFSET_CEST, + [NSC_TIME_ZONE_FST] = NSC_TIME_ZONE_OFFSET_FST, + [NSC_TIME_ZONE_MEST] = NSC_TIME_ZONE_OFFSET_MEST, + [NSC_TIME_ZONE_MESZ] = NSC_TIME_ZONE_OFFSET_MESZ, + [NSC_TIME_ZONE_BST] = NSC_TIME_ZONE_OFFSET_BST, + [NSC_TIME_ZONE_ADT] = NSC_TIME_ZONE_OFFSET_ADT, + [NSC_TIME_ZONE_EDT] = NSC_TIME_ZONE_OFFSET_EDT, + [NSC_TIME_ZONE_CDT] = NSC_TIME_ZONE_OFFSET_CDT, + [NSC_TIME_ZONE_MDT] = NSC_TIME_ZONE_OFFSET_MDT, + [NSC_TIME_ZONE_PDT] = NSC_TIME_ZONE_OFFSET_PDT, + [NSC_TIME_ZONE_YDT] = NSC_TIME_ZONE_OFFSET_YDT, + [NSC_TIME_ZONE_HDT] = NSC_TIME_ZONE_OFFSET_HDT, + [NSC_TIME_ZONE_Y] = NSC_TIME_ZONE_OFFSET_Y, + [NSC_TIME_ZONE_X] = NSC_TIME_ZONE_OFFSET_X, + [NSC_TIME_ZONE_W] = NSC_TIME_ZONE_OFFSET_W, + [NSC_TIME_ZONE_V] = NSC_TIME_ZONE_OFFSET_V, + [NSC_TIME_ZONE_U] = NSC_TIME_ZONE_OFFSET_U, + [NSC_TIME_ZONE_T] = NSC_TIME_ZONE_OFFSET_T, + [NSC_TIME_ZONE_S] = NSC_TIME_ZONE_OFFSET_S, + [NSC_TIME_ZONE_R] = NSC_TIME_ZONE_OFFSET_R, + [NSC_TIME_ZONE_Q] = NSC_TIME_ZONE_OFFSET_Q, + [NSC_TIME_ZONE_P] = NSC_TIME_ZONE_OFFSET_P, + [NSC_TIME_ZONE_O] = NSC_TIME_ZONE_OFFSET_O, + [NSC_TIME_ZONE_N] = NSC_TIME_ZONE_OFFSET_N, + [NSC_TIME_ZONE_Z] = NSC_TIME_ZONE_OFFSET_Z, + [NSC_TIME_ZONE_A] = NSC_TIME_ZONE_OFFSET_A, + [NSC_TIME_ZONE_B] = NSC_TIME_ZONE_OFFSET_B, + [NSC_TIME_ZONE_C] = NSC_TIME_ZONE_OFFSET_C, + [NSC_TIME_ZONE_D] = NSC_TIME_ZONE_OFFSET_D, + [NSC_TIME_ZONE_E] = NSC_TIME_ZONE_OFFSET_E, + [NSC_TIME_ZONE_F] = NSC_TIME_ZONE_OFFSET_F, + [NSC_TIME_ZONE_G] = NSC_TIME_ZONE_OFFSET_G, + [NSC_TIME_ZONE_H] = NSC_TIME_ZONE_OFFSET_H, + [NSC_TIME_ZONE_I] = NSC_TIME_ZONE_OFFSET_I, + [NSC_TIME_ZONE_K] = NSC_TIME_ZONE_OFFSET_K, + [NSC_TIME_ZONE_L] = NSC_TIME_ZONE_OFFSET_L, + [NSC_TIME_ZONE_M] = NSC_TIME_ZONE_OFFSET_M +}; + +/** + * Array of timezone names. Order does not matter. + */ +static const char * const timezones[NSC_TIME_ZONE__COUNT] = { + [NSC_TIME_ZONE_IDLE] = "IDLE", + [NSC_TIME_ZONE_NZST] = "NZST", + [NSC_TIME_ZONE_NZT] = "NZT", + [NSC_TIME_ZONE_EAST] = "EAST", + [NSC_TIME_ZONE_GST] = "GST", + [NSC_TIME_ZONE_JST] = "JST", + [NSC_TIME_ZONE_CCT] = "CCT", + [NSC_TIME_ZONE_WAST] = "WAST", + [NSC_TIME_ZONE_EET] = "EET", + [NSC_TIME_ZONE_CET] = "CET", + [NSC_TIME_ZONE_FWT] = "FWT", + [NSC_TIME_ZONE_MET] = "MET", + [NSC_TIME_ZONE_MEWT] = "MEWT", + [NSC_TIME_ZONE_GMT] = "GMT", + [NSC_TIME_ZONE_UTC] = "UTC", + [NSC_TIME_ZONE_WET] = "WET", + [NSC_TIME_ZONE_WAT] = "WAT", + [NSC_TIME_ZONE_AST] = "AST", + [NSC_TIME_ZONE_EST] = "EST", + [NSC_TIME_ZONE_CST] = "CST", + [NSC_TIME_ZONE_MST] = "MST", + [NSC_TIME_ZONE_PST] = "PST", + [NSC_TIME_ZONE_YST] = "YST", + [NSC_TIME_ZONE_AHST] = "AHST", + [NSC_TIME_ZONE_CAT] = "CAT", + [NSC_TIME_ZONE_HST] = "HST", + [NSC_TIME_ZONE_IDLW] = "IDLW", + [NSC_TIME_ZONE_NZDT] = "NZDT", + [NSC_TIME_ZONE_EADT] = "EADT", + [NSC_TIME_ZONE_WADT] = "WADT", + [NSC_TIME_ZONE_CEST] = "CEST", + [NSC_TIME_ZONE_FST] = "FST", + [NSC_TIME_ZONE_MEST] = "MEST", + [NSC_TIME_ZONE_MESZ] = "MESZ", + [NSC_TIME_ZONE_BST] = "BST", + [NSC_TIME_ZONE_ADT] = "ADT", + [NSC_TIME_ZONE_EDT] = "EDT", + [NSC_TIME_ZONE_CDT] = "CDT", + [NSC_TIME_ZONE_MDT] = "MDT", + [NSC_TIME_ZONE_PDT] = "PDT", + [NSC_TIME_ZONE_YDT] = "YDT", + [NSC_TIME_ZONE_HDT] = "HDT", + [NSC_TIME_ZONE_Y] = "Y", + [NSC_TIME_ZONE_X] = "X", + [NSC_TIME_ZONE_W] = "W", + [NSC_TIME_ZONE_V] = "V", + [NSC_TIME_ZONE_U] = "U", + [NSC_TIME_ZONE_T] = "T", + [NSC_TIME_ZONE_S] = "S", + [NSC_TIME_ZONE_R] = "R", + [NSC_TIME_ZONE_Q] = "Q", + [NSC_TIME_ZONE_P] = "P", + [NSC_TIME_ZONE_O] = "O", + [NSC_TIME_ZONE_N] = "N", + [NSC_TIME_ZONE_Z] = "Z", + [NSC_TIME_ZONE_A] = "A", + [NSC_TIME_ZONE_B] = "B", + [NSC_TIME_ZONE_C] = "C", + [NSC_TIME_ZONE_D] = "D", + [NSC_TIME_ZONE_E] = "E", + [NSC_TIME_ZONE_F] = "F", + [NSC_TIME_ZONE_G] = "G", + [NSC_TIME_ZONE_H] = "H", + [NSC_TIME_ZONE_I] = "I", + [NSC_TIME_ZONE_K] = "K", + [NSC_TIME_ZONE_L] = "L", + [NSC_TIME_ZONE_M] = "M" +}; + +/** + * Flags for tracking the components of a date that have been parsed. + */ +enum nsc_date_component_flags { + NSC_COMPONENT_FLAGS_NONE = 0, + NSC_COMPONENT_FLAGS_HAVE_YEARS = (1 << 0), + NSC_COMPONENT_FLAGS_HAVE_MONTHS = (1 << 1), + NSC_COMPONENT_FLAGS_HAVE_DAYS = (1 << 2), + NSC_COMPONENT_FLAGS_HAVE_HOURS = (1 << 3), + NSC_COMPONENT_FLAGS_HAVE_MINS = (1 << 4), + NSC_COMPONENT_FLAGS_HAVE_SECS = (1 << 5), + NSC_COMPONENT_FLAGS_HAVE_TIMEZONE = (1 << 6), + NSC_COMPONENT_FLAGS_HAVE_WEEKDAY = (1 << 7), + NSC_COMPONENT_FLAGS__HAVE_YYYYMMDD = + NSC_COMPONENT_FLAGS_HAVE_YEARS | + NSC_COMPONENT_FLAGS_HAVE_MONTHS | + NSC_COMPONENT_FLAGS_HAVE_DAYS, + NSC_COMPONENT_FLAGS__HAVE_HHMMSS = + NSC_COMPONENT_FLAGS_HAVE_HOURS | + NSC_COMPONENT_FLAGS_HAVE_MINS | + NSC_COMPONENT_FLAGS_HAVE_SECS, + NSC_COMPONENT_FLAGS__HAVE_ALL = + NSC_COMPONENT_FLAGS_HAVE_YEARS | + NSC_COMPONENT_FLAGS_HAVE_MONTHS | + NSC_COMPONENT_FLAGS_HAVE_DAYS | + NSC_COMPONENT_FLAGS_HAVE_HOURS | + NSC_COMPONENT_FLAGS_HAVE_MINS | + NSC_COMPONENT_FLAGS_HAVE_SECS | + NSC_COMPONENT_FLAGS_HAVE_TIMEZONE +}; + +/** + * Context for date parsing. + */ +struct nsc_date_parse_ctx { + char prev; /**< Used for handling neumenrical timezone */ + uint8_t secs; + uint8_t mins; + uint8_t hours; + uint8_t day; + uint8_t month; + uint16_t years; + int16_t timezone_offset_mins; +}; + + +/** + * Helper for testing whether any of the flags in mask are set. + * + * \param[in] flags Flags to to check. + * \param[in] mask The set of flags to check for in `flags`. + * \return true iff any flags in `mask` are set in `flags`, else false. + */ +static bool flags_chk( + enum nsc_date_component_flags flags, + enum nsc_date_component_flags mask) +{ + return flags & mask; +} + +/** + * Helper for testing whether all of the flags in mask are set. + * + * \param[in] flags Flags to to check. + * \param[in] mask The set of flags to check for in `flags`. + * \return true iff all flags in `mask` are set in `flags`, else false. + */ +static bool flags_chk_all( + enum nsc_date_component_flags flags, + enum nsc_date_component_flags mask) +{ + return (flags & mask) == mask; +} + + +/** + * Test for a weekday name in a string (case insensitive). + * + * \param[in] str String to parse a weekday name in. + * \param[in] len Number of consecutive alphabetical characters. + * \param[in,out] flags Flags indicating which date components have been + * found in `str` already. If a weekday component + * is found, the weekday flag is set. + * \return true iff weekday component is found, else false. + */ +static inline bool time__parse_weekday( + const char *str, + size_t len, + enum nsc_date_component_flags *flags) +{ + const char * const *names = (len == 3) ? + weekdays_short : weekdays_long; + + if (flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_WEEKDAY)) { + return false; + } + + for (uint32_t i = 0; i < NSC_TIME_WEEKDAY__COUNT; i++) { + if (ascii_strings_count_equal_caseless(names[i], str) == len) { + *flags |= NSC_COMPONENT_FLAGS_HAVE_WEEKDAY; + return true; + } + } + + return false; +} + + +/** + * Attempt to parse a month name in a string (case insensitive). + * + * \param[in] str String to parse a month name in. + * \param[in] len Number of consecutive alphabetical characters. + * \param[in,out] flags Flags indicating which date components have been + * found in `str` already. If a month component + * is found, the month flag is set. + * \param[in,out] ctx Current date parsing context. If month component + * is found, the context's month value is set. + * \return true iff month name component is found, else false. + */ +static inline bool time__parse_month( + const char *str, + size_t len, + enum nsc_date_component_flags *flags, + struct nsc_date_parse_ctx *ctx) +{ + if (flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_MONTHS)) { + return false; + } + + for (uint32_t i = 0; i < NSC_TIME_MONTH__COUNT; i++) { + if (ascii_strings_count_equal_caseless(months[i], str) == len) { + *flags |= NSC_COMPONENT_FLAGS_HAVE_MONTHS; + ctx->month = i; + return true; + } + } + + return false; +} + + +/** + * Attempt to parse a timezone name in a string (case insensitive). + * + * \param[in] str String to parse a timezone name in. + * \param[in] len Number of consecutive alphabetical characters. + * \param[in,out] flags Flags indicating which date components have been + * found in `str` already. If a timezone component + * is found, the timezone flag is set. + * \param[in,out] ctx Current date parsing context. If timezone component + * is found, the context's timezone value is set. + * \return true iff timezone name component is found, else false. + */ +static inline bool time__parse_timezone( + const char *str, + size_t len, + enum nsc_date_component_flags *flags, + struct nsc_date_parse_ctx *ctx) +{ + if (flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_TIMEZONE)) { + return false; + } + + for (uint32_t i = 0; i < NSC_TIME_ZONE__COUNT; i++) { + if (ascii_strings_count_equal_caseless( + timezones[i], str) == len) { + *flags |= NSC_COMPONENT_FLAGS_HAVE_TIMEZONE; + ctx->timezone_offset_mins = timezone_mins[i]; + return true; + } + } + + return false; +} + + +/** + * Attempt to parse an "hh:mm:ss" time from a string. + * + * \param[in] str String to parse a time in. + * \param[in,out] len The number of characters until the first non-digit. + * Iff a time component is found, updated to the number + * of comsumend characters. + * \param[in,out] flags Flags indicating which date components have been + * found in `str` already. If a time component + * is found, the hours, mins and secs flags are set. + * \param[in,out] ctx Current date parsing context. If time component + * is found, the context's time values are set. + * \return true iff time component is found, else false. + */ +static inline bool time__parse_hh_mm_ss( + const char *str, + size_t *len, + enum nsc_date_component_flags *flags, + struct nsc_date_parse_ctx *ctx) +{ + size_t l; + + if (*len != 2 || flags_chk(*flags, NSC_COMPONENT_FLAGS__HAVE_HHMMSS)) { + return false; + } + + l = *len + ascii_count_digit_or_colon(str + *len); + if (l == 8) { + int h, m, s, count; + count = sscanf(str, "%02d:%02d:%02d", &h, &m, &s); + if (count == 3) { + ctx->hours = h; + ctx->mins = m; + ctx->secs = s; + *flags |= NSC_COMPONENT_FLAGS__HAVE_HHMMSS; + *len = l; + return true; + } + } else if (l == 5) { + int h, m, count; + count = sscanf(str, "%02d:%02d", &h, &m); + if (count == 2) { + ctx->hours = h; + ctx->mins = m; + ctx->secs = 0; + *flags |= NSC_COMPONENT_FLAGS__HAVE_HHMMSS; + *len = l; + return true; + } + } + + return false; +} + + +/** + * Attempt to parse a number from a date string. + * + * How the number is treated depends on various things: + * + * - its character length, + * - its value, + * - which date components have already been parsed + * + * \param[in] str String to parse a time in. + * \param[in] len The number of characters until the first non-digit. + * \param[in,out] flags Flags indicating which date components have been + * found in `str` already. If any component is found, + * their flags are set. + * \param[in,out] ctx Current date parsing context. If any component + * is found, the appropriate context values are set. + * \return true iff a component is found, else false. + */ +static inline bool time__parse_number( + const char *str, + size_t len, + enum nsc_date_component_flags *flags, + struct nsc_date_parse_ctx *ctx) +{ + int value; + + if (len != ascii_string_to_int(str, &value)) { + return false; + } + + switch (len) { + case 8: + if (!flags_chk(*flags, NSC_COMPONENT_FLAGS__HAVE_YYYYMMDD)) { + ctx->years = value / 10000; + ctx->month = (value % 10000) / 100 - 1; + ctx->day = value % 100 - 1; + *flags |= NSC_COMPONENT_FLAGS__HAVE_YYYYMMDD; + return true; + } + break; + + case 4: + if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_TIMEZONE)) { + if (ascii_is_sign(ctx->prev) && value <= 1400) { + ctx->timezone_offset_mins = + value / 100 * 60 + + value % 100; + if (ctx->prev == '+') { + ctx->timezone_offset_mins *= -1; + } + *flags |= NSC_COMPONENT_FLAGS_HAVE_TIMEZONE; + return true; + } + } + if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_YEARS)) { + ctx->years = value; + *flags |= NSC_COMPONENT_FLAGS_HAVE_YEARS; + return true; + } + break; + + case 2: + case 1: + if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_DAYS) && + value > 0 && value <= 31) { + ctx->day = value - 1; + *flags |= NSC_COMPONENT_FLAGS_HAVE_DAYS; + return true; + } + if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_YEARS)) { + ctx->years = (value > 70) ? + value + 1900 : + value + 2000; + *flags |= NSC_COMPONENT_FLAGS_HAVE_YEARS; + return true; + } + break; + + default: + break; + } + + return false; +} + +/** + * Get number of leap days up until end of given year. + * + * \param[in] year Year to count leap years up to. + * \return Number of leap days up to end of `year`. + */ +static inline int time__get_leap_days(int year) +{ + return (year / 4) - (year / 100) + (year / 400); +} + + +/** + * Helper to convert a date string context to a time_t. + * + * \param[in] ctx Current date parsing context. + * \param[in] flags Flags indicating which date components have been set. + * \param[out] time Returns the number of seconds since 1 Jan 1970 00:00 UTC. + * \return NSERROR_OK on success, appropriate error otherwise. + */ +static nserror time__ctx_to_time_t( + const struct nsc_date_parse_ctx *ctx, + enum nsc_date_component_flags flags, + time_t *time) +{ + enum { + NSC_MONTH_DAYS_JAN = 31, + NSC_MONTH_DAYS_FEB = 28, /**< Leap years handled separatly */ + NSC_MONTH_DAYS_MAR = 31, + NSC_MONTH_DAYS_APR = 30, + NSC_MONTH_DAYS_MAY = 31, + NSC_MONTH_DAYS_JUN = 30, + NSC_MONTH_DAYS_JUL = 31, + NSC_MONTH_DAYS_AUG = 31, + NSC_MONTH_DAYS_SEP = 30, + NSC_MONTH_DAYS_OCT = 31, + NSC_MONTH_DAYS_NOV = 30, + NSC_MONTH_DAYS_DEC = 31 + }; + enum { + NSC_MONTH_OFF_JAN = 0, + NSC_MONTH_OFF_FEB = NSC_MONTH_OFF_JAN + NSC_MONTH_DAYS_JAN, + NSC_MONTH_OFF_MAR = NSC_MONTH_OFF_FEB + NSC_MONTH_DAYS_FEB, + NSC_MONTH_OFF_APR = NSC_MONTH_OFF_MAR + NSC_MONTH_DAYS_MAR, + NSC_MONTH_OFF_MAY = NSC_MONTH_OFF_APR + NSC_MONTH_DAYS_APR, + NSC_MONTH_OFF_JUN = NSC_MONTH_OFF_MAY + NSC_MONTH_DAYS_MAY, + NSC_MONTH_OFF_JUL = NSC_MONTH_OFF_JUN + NSC_MONTH_DAYS_JUN, + NSC_MONTH_OFF_AUG = NSC_MONTH_OFF_JUL + NSC_MONTH_DAYS_JUL, + NSC_MONTH_OFF_SEP = NSC_MONTH_OFF_AUG + NSC_MONTH_DAYS_AUG, + NSC_MONTH_OFF_OCT = NSC_MONTH_OFF_SEP + NSC_MONTH_DAYS_SEP, + NSC_MONTH_OFF_NOV = NSC_MONTH_OFF_OCT + NSC_MONTH_DAYS_OCT, + NSC_MONTH_OFF_DEC = NSC_MONTH_OFF_NOV + NSC_MONTH_DAYS_NOV + }; + static const uint16_t month_offsets[NSC_TIME_MONTH__COUNT] = { + [NSC_TIME_MONTH_JAN] = NSC_MONTH_OFF_JAN, + [NSC_TIME_MONTH_FEB] = NSC_MONTH_OFF_FEB, + [NSC_TIME_MONTH_MAR] = NSC_MONTH_OFF_MAR, + [NSC_TIME_MONTH_APR] = NSC_MONTH_OFF_APR, + [NSC_TIME_MONTH_MAY] = NSC_MONTH_OFF_MAY, + [NSC_TIME_MONTH_JUN] = NSC_MONTH_OFF_JUN, + [NSC_TIME_MONTH_JUL] = NSC_MONTH_OFF_JUL, + [NSC_TIME_MONTH_AUG] = NSC_MONTH_OFF_AUG, + [NSC_TIME_MONTH_SEP] = NSC_MONTH_OFF_SEP, + [NSC_TIME_MONTH_OCT] = NSC_MONTH_OFF_OCT, + [NSC_TIME_MONTH_NOV] = NSC_MONTH_OFF_NOV, + [NSC_TIME_MONTH_DEC] = NSC_MONTH_OFF_DEC + }; + int year_days = (ctx->years - 1970) * 365; + int month_days = month_offsets[ctx->month]; + int year = (ctx->month < NSC_TIME_MONTH_FEB) ? + ctx->years - 1 : ctx->years; + int leap_days = time__get_leap_days(year) - time__get_leap_days(1969); + int total_days = year_days + month_days + ctx->day + leap_days; + + int mins = (int)ctx->mins + (int)ctx->timezone_offset_mins; + + *time = (((((time_t)(total_days)) * 24) + + ctx->hours) * 60 + + mins) * 60 + + ctx->secs; + return NSERROR_OK; +} + + +/** + * Parse a date string to a `time_t`. + * + * \param[in] str String to parse. + * \param[out] time Returns the number of seconds since 1 Jan 1970 00:00 UTC. + * \return `NSERROR_OK` on success, else + * `NSERROR_INVALID` if the string parsing failed, + * appropriate error otherwise. + */ +static nserror time__get_date(const char *str, time_t *time) +{ + enum nsc_date_component_flags flags = NSC_COMPONENT_FLAGS_NONE; + struct nsc_date_parse_ctx ctx = { + .prev = '\0', + .secs = 0, + .mins = 0, + .hours = 0, + .day = 0, + .month = 0, + .years = 0, + .timezone_offset_mins = 0 + }; + + if (str == NULL || time == NULL) { + return NSERROR_BAD_PARAMETER; + } + + /* Parse */ + while (*str != '\0' && + !flags_chk_all(flags, NSC_COMPONENT_FLAGS__HAVE_ALL)) { + size_t len = 1; + + if (ascii_is_alpha(*str)) { + len += ascii_count_alpha(str + 1); + + if (!time__parse_weekday(str, len, &flags) && + !time__parse_month(str, len, &flags, &ctx) && + !time__parse_timezone(str, len, &flags, &ctx)) { + return NSERROR_INVALID; + } + + } else if (ascii_is_digit(*str)) { + len += ascii_count_digit(str + 1); + + if (!time__parse_hh_mm_ss(str, &len, &flags, &ctx) && + !time__parse_number(str, len, &flags, &ctx)) { + return NSERROR_INVALID; + } + } + ctx.prev = *str; + str += len; + } + + /* The initial values of 0 are used if hours, mins, secs, and timezone + * are not found */ + flags |= NSC_COMPONENT_FLAGS__HAVE_HHMMSS; + flags |= NSC_COMPONENT_FLAGS_HAVE_TIMEZONE; + + /* Validate */ + if (!flags_chk_all(flags, NSC_COMPONENT_FLAGS__HAVE_ALL)) { + return NSERROR_INVALID; + } + if (ctx.secs > 60 || ctx.mins > 59 || ctx.hours > 23 || + ctx.day > 31 || ctx.month > 11) { + return NSERROR_INVALID; + } + + /* Convert */ + return time__ctx_to_time_t(&ctx, flags, time); +} + +/* exported function documented in utils/time.h */ +nserror nsc_strntimet(const char *str, size_t size, time_t *timep) +{ + return time__get_date(str, timep); +} + +# else + /* exported function documented in utils/time.h */ nserror nsc_strntimet(const char *str, size_t size, time_t *timep) { time_t result; + if (str == NULL || timep == NULL) { + return NSERROR_BAD_PARAMETER; + } + result = curl_getdate(str, NULL); if (result == -1) { @@ -196,3 +1001,5 @@ nserror nsc_strntimet(const char *str, size_t size, time_t *timep) return NSERROR_OK; } + +#endif -- cgit v1.2.3 From 78d53fff1f7cd6dc3a40c01ac3b31b0eb7ab7e21 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Thu, 11 Aug 2016 18:43:31 +0100 Subject: Tests: Add some basic tests for date string parsing. --- test/Makefile | 14 +- test/time.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 436 insertions(+), 1 deletion(-) create mode 100644 test/time.c diff --git a/test/Makefile b/test/Makefile index 0253f4dc0..d62e2fd8d 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,7 +1,16 @@ # # NetSurf unit tests -TESTS := nsurl urldbtest nsoption bloom hashtable urlescape utils messages #llcache +TESTS := \ + nsurl \ + urldbtest \ + nsoption \ + bloom \ + hashtable \ + urlescape \ + utils \ + messages \ + time #llcache # nsurl sources nsurl_SRCS := utils/corestrings.c utils/nsurl.c utils/idna.c \ @@ -44,6 +53,9 @@ urlescape_SRCS := utils/url.c test/log.c test/urlescape.c utils_SRCS := utils/utils.c utils/messages.c utils/hashtable.c \ test/log.c test/utils.c +# time test sources +time_SRCS := utils/time.c test/log.c test/time.c + # Coverage builds need additional flags ifeq ($(MAKECMDGOALS),coverage) COV_CFLAGS ?= -fprofile-arcs -ftest-coverage -O0 diff --git a/test/time.c b/test/time.c new file mode 100644 index 000000000..34cfac0f0 --- /dev/null +++ b/test/time.c @@ -0,0 +1,423 @@ +/* + * Copyright 2016 Michael Drake + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file + * Test time operations. + */ + +#include +#include + +#include "utils/errors.h" +#include "utils/time.h" + +#define NELEMS(x) (sizeof(x) / sizeof((x)[0])) + +struct test_string_pair { + const char* test; + const char* expected; +}; + +struct test_bad_string { + const char* test; + nserror res; +}; + +static const struct test_string_pair date_string_tests[] = { + { + .test = "Thu, 01 Jan 1970 00:00:00 GMT", + .expected = "Thu, 01 Jan 1970 00:00:00 GMT" + }, + { + .test = "Thursday, 01 Jan 1970 00:00:00 GMT", + .expected = "Thu, 01 Jan 1970 00:00:00 GMT" + }, + { + .test = "Tue, 16 Feb 1999 19:45:12 GMT", + .expected = "Tue, 16 Feb 1999 19:45:12 GMT" + }, + { + .test = "Sunday, 16 Mar 1980 19:45:12 GMT", + .expected = "Sun, 16 Mar 1980 19:45:12 GMT" + }, + { + .test = "Sun, 16 Mar 1980 19:45:12 GMT", + .expected = "Sun, 16 Mar 1980 19:45:12 GMT" + }, + { + .test = "Tue, 16 Apr 2013 19:45:12 GMT", + .expected = "Tue, 16 Apr 2013 19:45:12 GMT" + }, + { + .test = "Tue, 16 May 2000 19:45:12 GMT", + .expected = "Tue, 16 May 2000 19:45:12 GMT" + }, + { + .test = "Tuesday, 12 Jun 2001 12:12:12 GMT", + .expected = "Tue, 12 Jun 2001 12:12:12 GMT" + }, + { + .test = "Tue, 12 Jun 2001 12:12:12 GMT", + .expected = "Tue, 12 Jun 2001 12:12:12 GMT" + }, + { + .test = "Thu, 16 Jul 2207 12:45:12 GMT", + .expected = "Thu, 16 Jul 2207 12:45:12 GMT" + }, + { + .test = "Thu, 16 Aug 2007 19:45:12 GMT", + .expected = "Thu, 16 Aug 2007 19:45:12 GMT" + }, + { + .test = "Tue, 16 Sep 3456 00:45:12 GMT", + .expected = "Tue, 16 Sep 3456 00:45:12 GMT" + }, + { + .test = "Sun, 16 Oct 1988 19:45:59 GMT", + .expected = "Sun, 16 Oct 1988 19:45:59 GMT" + }, + { + .test = "Tue, 16 Nov 1971 19:59:12 GMT", + .expected = "Tue, 16 Nov 1971 19:59:12 GMT" + }, + { + .test = "Friday, 16 Dec 1977 23:45:12 GMT", + .expected = "Fri, 16 Dec 1977 23:45:12 GMT" + }, + { + .test = "Fri, 16 Dec 1977 23:45:12 GMT", + .expected = "Fri, 16 Dec 1977 23:45:12 GMT" + }, + { + .test = " 16 Dec 1977 23:45:12 GMT", + .expected = "Fri, 16 Dec 1977 23:45:12 GMT" + }, + { + .test = " 16 Dec 1977 23:45 GMT", + .expected = "Fri, 16 Dec 1977 23:45:00 GMT" + }, + { + .test = "23:59 16 Dec 1977 GMT", + .expected = "Fri, 16 Dec 1977 23:59:00 GMT" + }, + { + .test = "23:59 16 Dec 1977 UTC", + .expected = "Fri, 16 Dec 1977 23:59:00 GMT" + }, + { + .test = "1977 GMT 23:59 16 Dec", + .expected = "Fri, 16 Dec 1977 23:59:00 GMT" + }, + { + .test = "1977 Dec GMT 16", + .expected = "Fri, 16 Dec 1977 00:00:00 GMT" + }, + { + .test = "1977 Dec 12", + .expected = "Mon, 12 Dec 1977 00:00:00 GMT" + }, + { + .test = "1977 12 Dec", + .expected = "Mon, 12 Dec 1977 00:00:00 GMT" + }, + { + .test = "Dec 1977 12", + .expected = "Mon, 12 Dec 1977 00:00:00 GMT" + }, + { + .test = "12 Dec 1977", + .expected = "Mon, 12 Dec 1977 00:00:00 GMT" + }, + { + .test = "12 Dec 77", + .expected = "Mon, 12 Dec 1977 00:00:00 GMT" + }, + { + .test = "12 77 Dec", + .expected = "Mon, 12 Dec 1977 00:00:00 GMT" + }, + { + .test = "77 12 Dec", + .expected = "Mon, 12 Dec 1977 00:00:00 GMT" + }, + { + .test = "12 12 Dec", + .expected = "Wed, 12 Dec 2012 00:00:00 GMT" + }, + { + .test = "5 12 Dec", + .expected = "Wed, 05 Dec 2012 00:00:00 GMT" + }, + { + .test = "12 5 Dec", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "12/5/Dec", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "Dec-12/2005/", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "12-5-Dec", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "2005-12-Dec", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "2005-Dec-12", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "2005-dec-12", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "2005-dEC-12", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "20051212", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "20051212 GMT", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "20051212 +0000", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "20051212 UTC", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "20051212 00:00 UTC", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "00:00 20051212 UTC", + .expected = "Mon, 12 Dec 2005 00:00:00 GMT" + }, + { + .test = "00:00:59 20051212 UTC", + .expected = "Mon, 12 Dec 2005 00:00:59 GMT" + }, + { + .test = "00:00:60 20051212 UTC", /* leap second */ + .expected = "Mon, 12 Dec 2005 00:01:00 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 GMT", + .expected = "Thu, 11 Aug 2016 08:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 UTC", + .expected = "Thu, 11 Aug 2016 08:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 +0000", + .expected = "Thu, 11 Aug 2016 08:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 -0000", + .expected = "Thu, 11 Aug 2016 08:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 +0001", + .expected = "Thu, 11 Aug 2016 08:46:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 -0001", + .expected = "Thu, 11 Aug 2016 08:48:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 +0030", + .expected = "Thu, 11 Aug 2016 08:17:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 -0030", + .expected = "Thu, 11 Aug 2016 09:17:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 +0059", + .expected = "Thu, 11 Aug 2016 07:48:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 -0059", + .expected = "Thu, 11 Aug 2016 09:46:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 +0100", + .expected = "Thu, 11 Aug 2016 07:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 -0100", + .expected = "Thu, 11 Aug 2016 09:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 +1200", + .expected = "Wed, 10 Aug 2016 20:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 -1200", + .expected = "Thu, 11 Aug 2016 20:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 +0060", + .expected = "Thu, 11 Aug 2016 07:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 -0060", + .expected = "Thu, 11 Aug 2016 09:47:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 +0070", + .expected = "Thu, 11 Aug 2016 07:37:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 -0070", + .expected = "Thu, 11 Aug 2016 09:57:30 GMT" + }, + { + .test = "Thu, 11 Aug 2016 08:47:30 BST", + .expected = "Thu, 11 Aug 2016 07:47:30 GMT" + }, + { + .test = "14-Aug-2015 11:58:16 GMT", + .expected = "Fri, 14 Aug 2015 11:58:16 GMT" + }, +}; + + +static const struct test_bad_string date_bad_string_tests[] = { + { + .test = NULL, + .res = NSERROR_BAD_PARAMETER + }, + { + .test = "", + .res = NSERROR_INVALID + }, + { + .test = "Th", + .res = NSERROR_INVALID + }, + { + .test = "5", + .res = NSERROR_INVALID + }, + { + .test = "5", + .res = NSERROR_INVALID + }, + { + .test = "dsflihs9l84toswuhfsif74f", + .res = NSERROR_INVALID + }, + { + .test = "Foosday, 16 Dec 1977 23:45:12 GMT", + .res = NSERROR_INVALID + }, +}; + +/** + * Date string comparason test + */ +START_TEST(date_string_compare) +{ + nserror res; + time_t time_out; + const struct test_string_pair *t = &date_string_tests[_i]; + + res = nsc_strntimet(t->test, strlen(t->test), &time_out); + ck_assert(res == NSERROR_OK); + ck_assert_str_eq(rfc1123_date(time_out), t->expected); +} +END_TEST + +/** + * Date string conversion bad data test + */ +START_TEST(date_bad_string) +{ + nserror res; + time_t time_out; + const struct test_bad_string *t = &date_bad_string_tests[_i]; + + res = nsc_strntimet(t->test, + t->test != NULL ? strlen(t->test) : 0, + &time_out); + ck_assert(res != NSERROR_OK); + ck_assert(res == t->res); +} +END_TEST + + +/* suite generation */ +static Suite *time_suite(void) +{ + Suite *s; + TCase *tc_date_string_compare; + TCase *tc_date_bad_string; + + s = suite_create("time"); + + /* date parsing: string comparason */ + tc_date_string_compare = tcase_create( + "date string to time_t"); + + /* date parsing: bad string handling */ + tc_date_bad_string = tcase_create( + "date string to time_t (bad input)"); + + tcase_add_loop_test(tc_date_string_compare, + date_string_compare, + 0, NELEMS(date_string_tests)); + suite_add_tcase(s, tc_date_string_compare); + + tcase_add_loop_test(tc_date_bad_string, + date_bad_string, + 0, NELEMS(date_bad_string_tests)); + suite_add_tcase(s, tc_date_bad_string); + + return s; +} + +int main(int argc, char **argv) +{ + int number_failed; + Suite *s; + SRunner *sr; + + s = time_suite(); + + sr = srunner_create(s); + srunner_run_all(sr, CK_ENV); + + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} -- cgit v1.2.3 From fe00eac8cb0740b74ec2f586d98e741c84299d90 Mon Sep 17 00:00:00 2001 From: Michael Drake Date: Sat, 13 Aug 2016 13:09:39 +0100 Subject: Buildsystem: Make curl/openssl usage build-time options. --- Makefile | 19 +++++++++++-------- Makefile.defaults | 8 ++++++++ content/fetch.c | 2 ++ content/fetchers/Makefile | 6 ++++-- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 226bf4f3d..60810e153 100644 --- a/Makefile +++ b/Makefile @@ -511,14 +511,6 @@ $(eval $(call pkg_config_find_and_add,libcss,CSS)) $(eval $(call pkg_config_find_and_add,libdom,DOM)) $(eval $(call pkg_config_find_and_add,libnsutils,nsutils)) $(eval $(call pkg_config_find_and_add,libutf8proc,utf8proc)) -$(eval $(call pkg_config_find_and_add,openssl,OpenSSL)) -# freemint does not support pkg-config for libcurl -ifeq ($(HOST),mint) - CFLAGS += $(shell curl-config --cflags) - LDFLAGS += $(shell curl-config --libs) -else - $(eval $(call pkg_config_find_and_add,libcurl,Curl)) -endif # Common libraries without pkg-config support LDFLAGS += -lz @@ -529,9 +521,20 @@ LDFLAGS += -lz NETSURF_FEATURE_PNG_CFLAGS := -DWITH_PNG NETSURF_FEATURE_BMP_CFLAGS := -DWITH_BMP NETSURF_FEATURE_GIF_CFLAGS := -DWITH_GIF +NETSURF_FEATURE_CURL_CFLAGS := -DWITH_CURL NETSURF_FEATURE_NSSVG_CFLAGS := -DWITH_NS_SVG +NETSURF_FEATURE_OPENSSL_CFLAGS := -DWITH_OPENSSL NETSURF_FEATURE_ROSPRITE_CFLAGS := -DWITH_NSSPRITE +$(eval $(call pkg_config_find_and_add_enabled,OPENSSL,openssl,OpenSSL)) +# freemint does not support pkg-config for libcurl +ifeq ($(HOST),mint) + CFLAGS += $(shell curl-config --cflags) + LDFLAGS += $(shell curl-config --libs) +else + $(eval $(call pkg_config_find_and_add_enabled,CURL,libcurl,Curl)) +endif + $(eval $(call pkg_config_find_and_add_enabled,PNG,libpng,PNG)) $(eval $(call pkg_config_find_and_add_enabled,BMP,libnsbmp,BMP)) $(eval $(call pkg_config_find_and_add_enabled,GIF,libnsgif,GIF)) diff --git a/Makefile.defaults b/Makefile.defaults index 196e19236..1651a2d21 100644 --- a/Makefile.defaults +++ b/Makefile.defaults @@ -31,6 +31,14 @@ # Options relating to all versions of NetSurf # ---------------------------------------------------------------------------- +# Enable NetSurf's use of libcurl for fetching over http(s) +# Valid options: YES, NO +NETSURF_USE_CURL := YES + +# Enable NetSurf's use of openssl for fetching over https +# Valid options: YES, NO +NETSURF_USE_OPENSSL := YES + # Enable NetSurf's use of libnsbmp for displaying BMPs and ICOs # Valid options: YES, NO NETSURF_USE_BMP := YES diff --git a/content/fetch.c b/content/fetch.c index 11adf9cf4..93c7de2ad 100644 --- a/content/fetch.c +++ b/content/fetch.c @@ -290,10 +290,12 @@ nserror fetcher_init(void) { nserror ret; +#ifdef WITH_CURL ret = fetch_curl_register(); if (ret != NSERROR_OK) { return ret; } +#endif ret = fetch_data_register(); if (ret != NSERROR_OK) { diff --git a/content/fetchers/Makefile b/content/fetchers/Makefile index 855154232..9c8479320 100644 --- a/content/fetchers/Makefile +++ b/content/fetchers/Makefile @@ -1,8 +1,10 @@ # Content fetchers sources -S_FETCHERS := curl.c data.c file.c about.c resource.c +S_FETCHERS_YES := data.c file.c about.c resource.c +S_FETCHERS_NO := +S_FETCHERS_$(NETSURF_USE_CURL) += curl.c -S_FETCHERS := $(addprefix content/fetchers/,$(S_FETCHERS)) +S_FETCHERS := $(addprefix content/fetchers/,$(S_FETCHERS_YES)) # The following files depend on the testament content/fetchers/about.c: testament $(OBJROOT)/testament.h -- cgit v1.2.3