summaryrefslogtreecommitdiff
path: root/src/third_party/timelib-2022.02/parse_zoneinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/third_party/timelib-2022.02/parse_zoneinfo.c')
-rw-r--r--src/third_party/timelib-2022.02/parse_zoneinfo.c354
1 files changed, 354 insertions, 0 deletions
diff --git a/src/third_party/timelib-2022.02/parse_zoneinfo.c b/src/third_party/timelib-2022.02/parse_zoneinfo.c
new file mode 100644
index 00000000000..35348f69413
--- /dev/null
+++ b/src/third_party/timelib-2022.02/parse_zoneinfo.c
@@ -0,0 +1,354 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017-2018 MongoDB, Inc.
+ * Copyright (c) 2015 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "timelib.h"
+#include "timelib_private.h"
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+# include "win_dirent.h"
+#endif
+
+#ifndef MAXPATHLEN
+# ifdef _WIN32
+# define MAXPATHLEN 2048
+# elif PATH_MAX
+# define MAXPATHLEN PATH_MAX
+# elif defined(MAX_PATH)
+# define MAXPATHLEN MAX_PATH
+# else
+# define MAXPATHLEN 256
+# endif
+#endif
+
+#if _MSC_VER
+# define TIMELIB_DIR_SEPARATOR "\\"
+#else
+# define TIMELIB_DIR_SEPARATOR "/"
+#endif
+
+#define TIMELIB_NAME_SEPARATOR "/"
+
+/* Filter out some non-tzdata files and the posix/right databases, if
+ * present. */
+static int index_filter(const struct dirent *ent)
+{
+ return strcmp(ent->d_name, ".") != 0
+ && strcmp(ent->d_name, "..") != 0
+ && strcmp(ent->d_name, "posix") != 0
+ && strcmp(ent->d_name, "posixrules") != 0
+ && strcmp(ent->d_name, "right") != 0
+ && strcmp(ent->d_name, "localtime") != 0
+ && strstr(ent->d_name, ".list") == NULL
+ && strstr(ent->d_name, ".tab") == NULL;
+}
+
+static int timelib_alphasort(const struct dirent **a, const struct dirent **b)
+{
+ return strcmp((*a)->d_name, (*b)->d_name);
+}
+
+
+static int sysdbcmp(const void *first, const void *second)
+{
+ const timelib_tzdb_index_entry *alpha = first, *beta = second;
+
+ return timelib_strcasecmp(alpha->id, beta->id);
+}
+
+/* Returns true if the passed-in stat structure describes a
+ * probably-valid timezone file. */
+static int is_valid_tzfile(const struct stat *st, int fd)
+{
+ if (fd) {
+ char buf[20];
+ if (read(fd, buf, 20) != 20) {
+ return 0;
+ }
+ lseek(fd, SEEK_SET, 0);
+ if (memcmp(buf, "TZif", 4)) {
+ return 0;
+ }
+ }
+ return S_ISREG(st->st_mode) && st->st_size > 20;
+}
+
+/* Return the contents of the tzfile if found, else NULL. On success, the
+ * length of the mapped data is placed in *length. */
+static char *read_tzfile(const char *directory, const char *timezone, size_t *length)
+{
+ char *fname;
+ size_t fname_len;
+ char *buffer;
+ struct stat st;
+ int fd;
+#ifdef _WIN32
+ int read_bytes;
+#else
+ ssize_t read_bytes;
+#endif
+
+ if (timezone[0] == '\0' || strstr(timezone, "..") != NULL) {
+ return NULL;
+ }
+
+ fname_len = strlen(directory) + strlen(TIMELIB_DIR_SEPARATOR) + strlen(timezone) + 1;
+ fname = timelib_malloc(fname_len);
+ if (snprintf(fname, fname_len, "%s%s%s", directory, TIMELIB_DIR_SEPARATOR, timezone) < 0) {
+ timelib_free(fname);
+ return NULL;
+ }
+
+ /* O_BINARY is required to properly read the file on windows */
+#ifdef _WIN32
+ fd = open(fname, O_RDONLY | O_BINARY);
+#else
+ fd = open(fname, O_RDONLY);
+#endif
+ timelib_free(fname);
+
+ if (fd == -1) {
+ return NULL;
+ } else if (fstat(fd, &st) != 0 || !is_valid_tzfile(&st, fd)) {
+ close(fd);
+ return NULL;
+ }
+
+ *length = st.st_size;
+
+ buffer = timelib_calloc(1, *length + 1);
+ read_bytes = read(fd, buffer, *length);
+ close(fd);
+
+ if (read_bytes == -1 || read_bytes != st.st_size) {
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static int timelib_scandir(const char *directory_name, struct dirent ***namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, const struct dirent **))
+{
+ DIR *dir;
+ struct dirent **entries = NULL;
+ int entries_size = 0;
+ int entries_count = 0;
+ char entry_container[sizeof(struct dirent) + MAXPATHLEN];
+ struct dirent *entry = (struct dirent *)&entry_container;
+
+ dir = opendir(directory_name);
+ if (!dir) {
+ return -1;
+ }
+
+ while ((entry = readdir(dir)) != NULL) {
+ int new_entry_size = 0;
+ struct dirent *new_entry = NULL;
+
+ /* Skip if the filter matches */
+ if (filter && (*filter)(entry) == 0) {
+ continue;
+ }
+
+ /* Make sure our entries array is large enough */
+ if (entries_count == entries_size) {
+ struct dirent **new_entries;
+
+ if (entries_size == 0) {
+ entries_size = 16;
+ } else {
+ entries_size *= 2;
+ }
+
+ new_entries = (struct dirent **) timelib_realloc(entries, entries_size * sizeof(struct dirent *));
+ if (!new_entries) {
+ return -1;
+ }
+ entries = new_entries;
+ }
+
+ /* Add our new entry to the list */
+ new_entry_size = sizeof(struct dirent) + (strlen(entry->d_name) + 1);
+ new_entry = (struct dirent *) timelib_malloc(new_entry_size);
+
+ if (new_entry == NULL) {
+ goto cleanup;
+ }
+
+ entries[entries_count++] = (struct dirent *) memcpy(new_entry, entry, new_entry_size);
+ }
+
+ closedir(dir);
+
+ *namelist = entries;
+
+ if (compar) {
+ qsort(*namelist, entries_count, sizeof(struct dirent *), (int (*) (const void *, const void *)) compar);
+ }
+
+ return entries_count;
+
+cleanup:
+ while (entries_count-- > 0) {
+ timelib_free(entries[entries_count]);
+ }
+ timelib_free(entries);
+ return -1;
+}
+
+/* Create the zone identifier index by trawling the filesystem. */
+static int create_zone_index(const char *directory, timelib_tzdb *db)
+{
+ size_t dirstack_size, dirstack_top;
+ size_t index_size, index_next;
+ timelib_tzdb_index_entry *db_index;
+ char **dirstack;
+ size_t data_size = 0;
+ unsigned char *tmp_data = NULL;
+
+ /* LIFO stack to hold directory entries to scan; each slot is a
+ * directory name relative to the zoneinfo prefix. */
+ dirstack_size = 32;
+ dirstack = timelib_malloc(dirstack_size * sizeof(*dirstack));
+ dirstack_top = 1;
+ dirstack[0] = timelib_strdup("");
+
+ /* Index array. */
+ index_size = 64;
+ db_index = timelib_malloc(index_size * sizeof(timelib_tzdb_index_entry));
+ index_next = 0;
+
+ do {
+ struct dirent **ents;
+ char name[PATH_MAX], *top;
+ int count;
+
+ /* Pop the top stack entry, and iterate through its contents. */
+ top = dirstack[--dirstack_top];
+ snprintf(name, sizeof(name), "%s/%s", directory, top);
+
+ count = timelib_scandir(name, &ents, index_filter, timelib_alphasort);
+ if (count == -1) {
+ timelib_free(dirstack);
+ timelib_free(db_index);
+ return -errno;
+ }
+
+ while (count > 0) {
+ struct stat st;
+ const char *leaf = ents[count - 1]->d_name;
+
+ snprintf(name, sizeof(name), "%s%s%s%s%s", directory, TIMELIB_NAME_SEPARATOR, top, TIMELIB_NAME_SEPARATOR, leaf);
+
+ if (strlen(name) && stat(name, &st) == 0) {
+ /* Name, relative to the zoneinfo prefix. */
+ const char *root = top;
+
+ if (root[0] == '/') {
+ root++;
+ }
+
+ snprintf(name, sizeof(name), "%s%s%s", root, *root ? TIMELIB_NAME_SEPARATOR : "", leaf);
+
+ if (S_ISDIR(st.st_mode)) {
+ if (dirstack_top == dirstack_size) {
+ dirstack_size *= 2;
+ dirstack = timelib_realloc(dirstack, dirstack_size * sizeof(*dirstack));
+ }
+ dirstack[dirstack_top++] = timelib_strdup(name);
+ } else {
+ if (index_next == index_size) {
+ index_size *= 2;
+ db_index = timelib_realloc(db_index, index_size * sizeof(timelib_tzdb_index_entry));
+ }
+
+ db_index[index_next].id = timelib_strdup(name);
+
+ {
+ size_t length;
+ char *tzfile_data = read_tzfile(directory, name, &length);
+
+ if (tzfile_data) {
+ tmp_data = timelib_realloc(tmp_data, data_size + length);
+ memcpy(tmp_data + data_size, tzfile_data, length);
+ db_index[index_next].pos = data_size;
+ data_size += length;
+ timelib_free(tzfile_data);
+
+ index_next++;
+ } else {
+ timelib_free(db_index[index_next].id);
+ }
+ }
+ }
+ }
+
+ timelib_free(ents[--count]);
+ }
+
+ if (count != -1) {
+ timelib_free(ents);
+ }
+ timelib_free(top);
+ } while (dirstack_top);
+
+ qsort(db_index, index_next, sizeof(*db_index), sysdbcmp);
+
+ db->index = db_index;
+ db->index_size = index_next;
+ db->data = tmp_data;
+
+ timelib_free(dirstack);
+
+ return 0;
+}
+
+timelib_tzdb *timelib_zoneinfo(const char *directory)
+{
+ timelib_tzdb *tmp = timelib_malloc(sizeof(timelib_tzdb));
+
+ tmp->version = "0.system";
+ tmp->data = NULL;
+ if (create_zone_index(directory, tmp) < 0) {
+ timelib_free(tmp);
+ return NULL;
+ }
+ return tmp;
+}
+
+void timelib_zoneinfo_dtor(timelib_tzdb *tzdb)
+{
+ int i;
+
+ for (i = 0; i < tzdb->index_size; i++) {
+ timelib_free(tzdb->index[i].id);
+ }
+ timelib_free((timelib_tzdb_index_entry*) tzdb->index);
+ timelib_free((char*) tzdb->data);
+ timelib_free(tzdb);
+}