Gitweb links:

...log 
http://git.netsurf-browser.org/netsurf.git/shortlog/414f7451b49b2ead00d451489e99b7b3fd60f1cd
...commit 
http://git.netsurf-browser.org/netsurf.git/commit/414f7451b49b2ead00d451489e99b7b3fd60f1cd
...tree 
http://git.netsurf-browser.org/netsurf.git/tree/414f7451b49b2ead00d451489e99b7b3fd60f1cd

The branch, tlsa/date has been updated
  discards  3310ae44bedcab7f60acaafc4023114bf20abd56 (commit)
  discards  cb97660ac74a4a204a0cb4a4fa8e75b47bd33c55 (commit)
  discards  1ccf8eca02b97e948f8d796755f0a752911acf55 (commit)
  discards  a708ffaf3d6a9bb4024abfb2c3abc9e627266e57 (commit)
  discards  198a0a5721a2a92ae98e06a52a79a35e375001b8 (commit)
  discards  a8416cf1bdb28172ea9d54b7c71a47f2ac2cd2b1 (commit)
       via  414f7451b49b2ead00d451489e99b7b3fd60f1cd (commit)
       via  e79635c090937077d39e6b3f63717a0f596a4b58 (commit)
       via  a1668f6c1e0c9e88f62f6082c6206c4d33f25cce (commit)
       via  efeeacca8621076f285ead0fc7e95d61938daea6 (commit)
       via  16b66d3af521e79c8c003fb51ca6b8c64fec4d86 (commit)
       via  477b65eac08a8b28c7dc4607ae51af28a31b90fc (commit)
       via  6a3f7a386ed7912d19971dec9235ad98692c22c1 (commit)

This update added new revisions after undoing existing revisions.  That is
to say, the old revision is not a strict subset of the new revision.  This
situation occurs when you --force push a change and generate a repository
containing something like this:

 * -- * -- B -- O -- O -- O (3310ae44bedcab7f60acaafc4023114bf20abd56)
            \
             N -- N -- N (414f7451b49b2ead00d451489e99b7b3fd60f1cd)

When this happens we assume that you've already had alert emails for all
of the O revisions, and so we here report only the revisions in the N
branch from the common base, B.

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=414f7451b49b2ead00d451489e99b7b3fd60f1cd
commit 414f7451b49b2ead00d451489e99b7b3fd60f1cd
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    Tests: Add some basic tests for date string parsing.

diff --git a/test/Makefile b/test/Makefile
index 0253f4d..d62e2fd 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 0000000..7fdbea4
--- /dev/null
+++ b/test/time.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2016 Michael Drake <[email protected]>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * Test time operations.
+ */
+
+#include <stdlib.h>
+#include <check.h>
+
+#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"
+       },
+};
+
+
+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;
+}


commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=e79635c090937077d39e6b3f63717a0f596a4b58
commit e79635c090937077d39e6b3f63717a0f596a4b58
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    Time: Add date string to time_t parser.

diff --git a/utils/time.c b/utils/time.c
index 6d53b16..f4ac2a1 100644
--- a/utils/time.c
+++ b/utils/time.c
@@ -25,17 +25,19 @@
  * \brief Implementation of time operations.
  */
 
-#include <errno.h>
 #include <stdio.h>
-#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
 
+#ifdef USE_CURL
 #include <curl/curl.h>
+#endif
 
+#include "utils/ascii.h"
 #include "utils/errors.h"
 #include "utils/time.h"
 
 
-
 /**
  * Weekdays
  *
@@ -181,11 +183,815 @@ nserror nsc_snptimet(const char *str, size_t size, 
time_t *timep)
 }
 
 
+#ifndef USE_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 +1002,5 @@ nserror nsc_strntimet(const char *str, size_t size, time_t 
*timep)
 
        return NSERROR_OK;
 }
+
+#endif


commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=a1668f6c1e0c9e88f62f6082c6206c4d33f25cce
commit a1668f6c1e0c9e88f62f6082c6206c4d33f25cce
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    url: Use ascii module, rather than ctype for locale safetly.

diff --git a/utils/url.c b/utils/url.c
index 7a7b7a1..ee84850 100644
--- a/utils/url.c
+++ b/utils/url.c
@@ -35,7 +35,9 @@
 #include <assert.h>
 #include <string.h>
 #include <stdlib.h>
+#include <stdbool.h>
 
+#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;
                        }


commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=efeeacca8621076f285ead0fc7e95d61938daea6
commit efeeacca8621076f285ead0fc7e95d61938daea6
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    nsurl: Update to use ascii_* helper functions.

diff --git a/utils/nsurl.c b/utils/nsurl.c
index 6582264..c5c614c 100644
--- a/utils/nsurl.c
+++ b/utils/nsurl.c
@@ -32,12 +32,12 @@
  */
 
 #include <assert.h>
-#include <ctype.h>
 #include <libwapcaplet/libwapcaplet.h>
 #include <stdlib.h>
 #include <string.h>
 #include <strings.h>
 
+#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
  */
@@ -208,79 +186,6 @@ inline static char digit2uppercase_hex(unsigned char 
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
  *
  * \param c character to classify.
@@ -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
                                                 */


commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=16b66d3af521e79c8c003fb51ca6b8c64fec4d86
commit 16b66d3af521e79c8c003fb51ca6b8c64fec4d86
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    Utils: Add a set of ASCII string parsing helpers.
    
    These are not affected by the current locale.

diff --git a/utils/ascii.h b/utils/ascii.h
new file mode 100644
index 0000000..f08e756
--- /dev/null
+++ b/utils/ascii.h
@@ -0,0 +1,358 @@
+/*
+ * Copyright 2016 Michael Drake <[email protected]>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \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 <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+
+/**
+ * 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


commitdiff 
http://git.netsurf-browser.org/netsurf.git/commit/?id=477b65eac08a8b28c7dc4607ae51af28a31b90fc
commit 477b65eac08a8b28c7dc4607ae51af28a31b90fc
Author: Michael Drake <[email protected]>
Commit: Michael Drake <[email protected]>

    Time: Expose arrays of weekday and month names.

diff --git a/utils/time.c b/utils/time.c
index cf8acc0..6d53b16 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;
 }


-----------------------------------------------------------------------

Summary of changes:
 frontends/amiga/gui.c |   59 ++++++++++++----------------
 test/time.c           |   20 ++++++++++
 utils/ascii.h         |   69 ++++++++++++++++++++++++++++++---
 utils/time.c          |  102 +++++++++++++++++++++----------------------------
 4 files changed, 151 insertions(+), 99 deletions(-)

diff --git a/frontends/amiga/gui.c b/frontends/amiga/gui.c
index 7e5eadc..0f2f33c 100644
--- a/frontends/amiga/gui.c
+++ b/frontends/amiga/gui.c
@@ -164,6 +164,10 @@
 #define EXTRADOWN (IECODE_5TH_BUTTON)
 #define EXTRAUP   (IECODE_5TH_BUTTON | IECODE_UP_PREFIX)
 
+/* Left OR Right Shift/Alt keys */
+#define NSA_QUAL_SHIFT (IEQUALIFIER_RSHIFT | IEQUALIFIER_LSHIFT)
+#define NSA_QUAL_ALT (IEQUALIFIER_RALT | IEQUALIFIER_LALT)
+
 #ifdef __amigaos4__
 #define NSA_STATUS_TEXT GA_Text
 #else
@@ -1238,45 +1242,33 @@ int ami_key_to_nskey(ULONG keycode, struct InputEvent 
*ie)
        switch(keycode)
        {
                case RAWKEY_CRSRUP:
-                       if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
-                       {
+                       if(ie->ie_Qualifier & NSA_QUAL_SHIFT) {
                                nskey = NS_KEY_PAGE_UP;
-                       }
-                       else if(ie->ie_Qualifier & IEQUALIFIER_RALT)
-                       {
+                       } else if(ie->ie_Qualifier & NSA_QUAL_ALT) {
                                nskey = NS_KEY_TEXT_START;
                        }
                        else nskey = NS_KEY_UP;
                break;
                case RAWKEY_CRSRDOWN:
-                       if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
-                       {
+                       if(ie->ie_Qualifier & NSA_QUAL_SHIFT) {
                                nskey = NS_KEY_PAGE_DOWN;
-                       }
-                       else if(ie->ie_Qualifier & IEQUALIFIER_RALT)
-                       {
+                       } else if(ie->ie_Qualifier & NSA_QUAL_ALT) {
                                nskey = NS_KEY_TEXT_END;
                        }
                        else nskey = NS_KEY_DOWN;
                break;
                case RAWKEY_CRSRLEFT:
-                       if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
-                       {
+                       if(ie->ie_Qualifier & NSA_QUAL_SHIFT) {
                                nskey = NS_KEY_LINE_START;
-                       }
-                       else if(ie->ie_Qualifier & IEQUALIFIER_RALT)
-                       {
+                       }else if(ie->ie_Qualifier & NSA_QUAL_ALT) {
                                nskey = NS_KEY_WORD_LEFT;
                        }
                        else nskey = NS_KEY_LEFT;
                break;
                case RAWKEY_CRSRRIGHT:
-                       if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
-                       {
+                       if(ie->ie_Qualifier & NSA_QUAL_SHIFT) {
                                nskey = NS_KEY_LINE_END;
-                       }
-                       else if(ie->ie_Qualifier & IEQUALIFIER_RALT)
-                       {
+                       }else if(ie->ie_Qualifier & NSA_QUAL_ALT) {
                                nskey = NS_KEY_WORD_RIGHT;
                        }
                        else nskey = NS_KEY_RIGHT;
@@ -1297,25 +1289,25 @@ int ami_key_to_nskey(ULONG keycode, struct InputEvent 
*ie)
                        nskey = NS_KEY_TEXT_END;
                break;
                case RAWKEY_BACKSPACE:
-                       if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
-                       {
+                       if(ie->ie_Qualifier & NSA_QUAL_SHIFT) {
                                nskey = NS_KEY_DELETE_LINE_START;
+                       } else {
+                               nskey = NS_KEY_DELETE_LEFT;
                        }
-                       else nskey = NS_KEY_DELETE_LEFT;
                break;
                case RAWKEY_DEL:
-                       if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
-                       {
+                       if(ie->ie_Qualifier & NSA_QUAL_SHIFT) {
                                nskey = NS_KEY_DELETE_LINE_END;
+                       } else {
+                               nskey = NS_KEY_DELETE_RIGHT;
                        }
-                       else nskey = NS_KEY_DELETE_RIGHT;
                break;
                case RAWKEY_TAB:
-                       if(ie->ie_Qualifier & IEQUALIFIER_RSHIFT)
-                       {
+                       if(ie->ie_Qualifier & NSA_QUAL_SHIFT) {
                                nskey = NS_KEY_SHIFT_TAB;
+                       } else {
+                               nskey = NS_KEY_TAB;
                        }
-                       else nskey = NS_KEY_TAB;
                break;
                case RAWKEY_F5:
                case RAWKEY_F8:
@@ -1369,18 +1361,15 @@ static void ami_update_quals(struct gui_window_2 *gwin)
 #endif
        gwin->key_state = 0;
 
-       if((quals & IEQUALIFIER_LSHIFT) || (quals & IEQUALIFIER_RSHIFT)) 
-       {
+       if(quals & NSA_QUAL_SHIFT) {
                gwin->key_state |= BROWSER_MOUSE_MOD_1;
        }
 
-       if(quals & IEQUALIFIER_CONTROL) 
-       {
+       if(quals & IEQUALIFIER_CONTROL) {
                gwin->key_state |= BROWSER_MOUSE_MOD_2;
        }
 
-       if((quals & IEQUALIFIER_LALT) || (quals & IEQUALIFIER_RALT)) 
-       {
+       if(quals & NSA_QUAL_ALT) {
                gwin->key_state |= BROWSER_MOUSE_MOD_3;
        }
 }
diff --git a/test/time.c b/test/time.c
index 6010938..7fdbea4 100644
--- a/test/time.c
+++ b/test/time.c
@@ -45,10 +45,18 @@ static const struct test_string_pair date_string_tests[] = {
                .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"
        },
@@ -61,6 +69,10 @@ static const struct test_string_pair date_string_tests[] = {
                .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"
        },
@@ -85,6 +97,10 @@ static const struct test_string_pair date_string_tests[] = {
                .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"
        },
@@ -316,6 +332,10 @@ static const struct test_bad_string 
date_bad_string_tests[] = {
                .test = "dsflihs9l84toswuhfsif74f",
                .res  = NSERROR_INVALID
        },
+       {
+               .test = "Foosday, 16 Dec 1977 23:45:12 GMT",
+               .res  = NSERROR_INVALID
+       },
 };
 
 /**
diff --git a/utils/ascii.h b/utils/ascii.h
index 4812dc6..f08e756 100644
--- a/utils/ascii.h
+++ b/utils/ascii.h
@@ -89,6 +89,17 @@ static inline bool ascii_is_digit(char c)
 }
 
 /**
+ * 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.
@@ -251,11 +262,13 @@ 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++))) {
-                       return false;
+               if (ascii_to_lower(*s1) != ascii_to_lower(*s2)) {
+                       break;
                }
+               s1++;
+               s2++;
        }
-       return true;
+       return (ascii_to_lower(*s1) == ascii_to_lower(*s2));
 }
 
 /**
@@ -269,11 +282,55 @@ static inline bool ascii_strings_equal(
                const char *s1, const char *s2)
 {
        while (*s1 != '\0') {
-               if (*(s1++) != *(s2++)) {
-                       return false;
+               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 true;
+       return s1 - s;
 }
 
 /**
diff --git a/utils/time.c b/utils/time.c
index c57554a..f4ac2a1 100644
--- a/utils/time.c
+++ b/utils/time.c
@@ -537,11 +537,7 @@ enum nsc_date_component_flags {
  * Context for date parsing.
  */
 struct nsc_date_parse_ctx {
-       enum {
-               PARSE_STATE_NONE,
-               PARSE_STATE_HAD_PLUS,
-               PARSE_STATE_HAD_MINUS
-       } state; /**< Used for handling neumenrical timezone */
+       char prev; /**< Used for handling neumenrical timezone */
        uint8_t secs;
        uint8_t mins;
        uint8_t hours;
@@ -585,16 +581,16 @@ static bool flags_chk_all(
  * 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.
- * \param[in]     len    Number of consecutive alphabetical characters.
  * \return true iff weekday component is found, else false.
  */
 static inline bool time__parse_weekday(
                const char *str,
-               enum nsc_date_component_flags *flags,
-               size_t len)
+               size_t len,
+               enum nsc_date_component_flags *flags)
 {
        const char * const *names = (len == 3) ?
                        weekdays_short : weekdays_long;
@@ -604,7 +600,7 @@ static inline bool time__parse_weekday(
        }
 
        for (uint32_t i = 0; i < NSC_TIME_WEEKDAY__COUNT; i++) {
-               if (ascii_strings_equal_caseless(names[i], str)) {
+               if (ascii_strings_count_equal_caseless(names[i], str) == len) {
                        *flags |= NSC_COMPONENT_FLAGS_HAVE_WEEKDAY;
                        return true;
                }
@@ -618,6 +614,7 @@ static inline bool time__parse_weekday(
  * 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.
@@ -627,6 +624,7 @@ static inline bool time__parse_weekday(
  */
 static inline bool time__parse_month(
                const char *str,
+               size_t len,
                enum nsc_date_component_flags *flags,
                struct nsc_date_parse_ctx *ctx)
 {
@@ -635,7 +633,7 @@ static inline bool time__parse_month(
        }
 
        for (uint32_t i = 0; i < NSC_TIME_MONTH__COUNT; i++) {
-               if (ascii_strings_equal_caseless(months[i], str)) {
+               if (ascii_strings_count_equal_caseless(months[i], str) == len) {
                        *flags |= NSC_COMPONENT_FLAGS_HAVE_MONTHS;
                        ctx->month = i;
                        return true;
@@ -650,6 +648,7 @@ static inline bool time__parse_month(
  * 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.
@@ -659,6 +658,7 @@ static inline bool time__parse_month(
  */
 static inline bool time__parse_timezone(
                const char *str,
+               size_t len,
                enum nsc_date_component_flags *flags,
                struct nsc_date_parse_ctx *ctx)
 {
@@ -667,7 +667,8 @@ static inline bool time__parse_timezone(
        }
 
        for (uint32_t i = 0; i < NSC_TIME_ZONE__COUNT; i++) {
-               if (ascii_strings_equal_caseless(timezones[i], str)) {
+               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;
@@ -682,21 +683,21 @@ static inline bool time__parse_timezone(
  * 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.
- * \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.
  * \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 *len)
+               struct nsc_date_parse_ctx *ctx)
 {
        size_t l;
 
@@ -743,19 +744,19 @@ static inline bool time__parse_hh_mm_ss(
  * - 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.
- * \param[in]     len    The number of characters until the first non-digit.
  * \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,
-               size_t len)
+               struct nsc_date_parse_ctx *ctx)
 {
        int value;
 
@@ -776,12 +777,11 @@ static inline bool time__parse_number(
 
        case 4:
                if (!flags_chk(*flags, NSC_COMPONENT_FLAGS_HAVE_TIMEZONE)) {
-                       if (ctx->state != PARSE_STATE_NONE &&
-                                       value <= 1400) {
+                       if (ascii_is_sign(ctx->prev) && value <= 1400) {
                                ctx->timezone_offset_mins =
                                                value / 100 * 60 +
                                                value % 100;
-                               if (ctx->state == PARSE_STATE_HAD_PLUS) {
+                               if (ctx->prev == '+') {
                                        ctx->timezone_offset_mins *= -1;
                                }
                                *flags |= NSC_COMPONENT_FLAGS_HAVE_TIMEZONE;
@@ -916,13 +916,13 @@ 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 = {
-               .state  = PARSE_STATE_NONE,
-               .secs   = 0,
-               .mins   = 0,
-               .hours  = 0,
-               .day    = 0,
-               .month  = 0,
-               .years  = 0,
+               .prev  = '\0',
+               .secs  = 0,
+               .mins  = 0,
+               .hours = 0,
+               .day   = 0,
+               .month = 0,
+               .years = 0,
                .timezone_offset_mins = 0
        };
 
@@ -933,46 +933,28 @@ static nserror time__get_date(const char *str, time_t 
*time)
        /* Parse */
        while (*str != '\0' &&
                        !flags_chk_all(flags, NSC_COMPONENT_FLAGS__HAVE_ALL)) {
+               size_t len = 1;
 
-               if (ascii_is_space(*str)) {
-                       str++;
+               if (ascii_is_alpha(*str)) {
+                       len += ascii_count_alpha(str + 1);
 
-               } else if (ascii_is_alpha(*str)) {
-                       size_t len = ascii_count_alpha(str + 1) + 1;
-
-                       if (!time__parse_weekday(str, &flags, len) &&
-                           !time__parse_month(str, &flags, &ctx) &&
-                           !time__parse_timezone(str, &flags, &ctx)) {
+                       if (!time__parse_weekday(str, len, &flags) &&
+                           !time__parse_month(str, len, &flags, &ctx) &&
+                           !time__parse_timezone(str, len, &flags, &ctx)) {
                                return NSERROR_INVALID;
                        }
 
-                       str += len;
-
                } else if (ascii_is_digit(*str)) {
-                       size_t len = ascii_count_digit(str + 1) + 1;
+                       len += ascii_count_digit(str + 1);
 
-                       if (!time__parse_hh_mm_ss(str, &flags, &ctx, &len) &&
-                           !time__parse_number(str, &flags, &ctx, len)) {
+                       if (!time__parse_hh_mm_ss(str, &len, &flags, &ctx) &&
+                           !time__parse_number(str, len, &flags, &ctx)) {
                                return NSERROR_INVALID;
                        }
 
-                       str += len;
-
-               } else if (*str == '+') {
-                       ctx.state = PARSE_STATE_HAD_PLUS;
-                       str++;
-                       continue;
-
-               } else if (*str == '-') {
-                       ctx.state = PARSE_STATE_HAD_MINUS;
-                       str++;
-                       continue;
-
-               } else {
-                       str++;
                }
-
-               ctx.state = PARSE_STATE_NONE;
+               ctx.prev = *str;
+               str += len;
        }
 
        /* The initial values of 0 are used if hours, mins, secs, and timezone
@@ -1006,6 +988,10 @@ 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) {


-- 
NetSurf Browser

_______________________________________________
netsurf-commits mailing list
[email protected]
http://listmaster.pepperfish.net/cgi-bin/mailman/listinfo/netsurf-commits-netsurf-browser.org

Reply via email to