summaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/common/CMakeLists.txt64
-rw-r--r--src/common/file-utils.cpp359
-rw-r--r--src/common/file-utils.h89
-rw-r--r--src/common/getoptwin.h34
-rw-r--r--src/common/str-utils.cpp217
-rw-r--r--src/common/str-utils.h63
-rw-r--r--src/common/time-utils.cpp96
-rw-r--r--src/common/time-utils.h38
-rw-r--r--src/common/uniwin.h60
9 files changed, 1020 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
new file mode 100644
index 0000000..1ea3445
--- /dev/null
+++ b/src/common/CMakeLists.txt
@@ -0,0 +1,64 @@
+# Copyright (c) 2020 Ribose Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+add_library(rnp-common OBJECT
+ str-utils.cpp
+ file-utils.cpp
+ time-utils.cpp
+)
+
+if(MSVC)
+ find_path(GETOPT_INCLUDE_DIR
+ NAMES getopt.h
+ )
+ find_library(GETOPT_LIBRARY
+ NAMES getopt
+ )
+ find_path(DIRENT_INCLUDE_DIR
+ NAMES dirent.h
+ )
+endif()
+
+target_include_directories(rnp-common
+ PUBLIC
+ "$<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/src/lib>"
+ "$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>"
+ "$<INSTALL_INTERFACE:include>"
+ PRIVATE
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ "${PROJECT_SOURCE_DIR}/src"
+)
+if(MSVC)
+ target_include_directories(rnp-common
+ PRIVATE
+ "${GETOPT_INCLUDE_DIR}"
+ "${DIRENT_INCLUDE_DIR}"
+ )
+endif()
+
+set_target_properties(rnp-common PROPERTIES
+ POSITION_INDEPENDENT_CODE ON
+ CXX_VISIBILITY_PRESET hidden
+)
+
diff --git a/src/common/file-utils.cpp b/src/common/file-utils.cpp
new file mode 100644
index 0000000..d9cdb40
--- /dev/null
+++ b/src/common/file-utils.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2017-2020 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/** File utilities
+ * @file
+ */
+
+#include "file-utils.h"
+#include "config.h"
+#ifdef _MSC_VER
+#include <stdlib.h>
+#include <stdio.h>
+#include "uniwin.h"
+#include <errno.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif // !_MSC_VER
+#include "str-utils.h"
+#include <algorithm>
+#ifdef _WIN32
+#include <random> // for rnp_mkstemp
+#define CATCH_AND_RETURN(v) \
+ catch (...) \
+ { \
+ errno = ENOMEM; \
+ return v; \
+ }
+#else
+#include <string.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdarg.h>
+
+int
+rnp_unlink(const char *filename)
+{
+#ifdef _WIN32
+ try {
+ return _wunlink(wstr_from_utf8(filename).c_str());
+ }
+ CATCH_AND_RETURN(-1)
+#else
+ return unlink(filename);
+#endif
+}
+
+bool
+rnp_file_exists(const char *path)
+{
+ struct stat st;
+ return rnp_stat(path, &st) == 0 && S_ISREG(st.st_mode);
+}
+
+bool
+rnp_dir_exists(const char *path)
+{
+ struct stat st;
+ return rnp_stat(path, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+int
+rnp_open(const char *filename, int oflag, int pmode)
+{
+#ifdef _WIN32
+ try {
+ return _wopen(wstr_from_utf8(filename).c_str(), oflag, pmode);
+ }
+ CATCH_AND_RETURN(-1)
+#else
+ return open(filename, oflag, pmode);
+#endif
+}
+
+FILE *
+rnp_fopen(const char *filename, const char *mode)
+{
+#ifdef _WIN32
+ try {
+ return _wfopen(wstr_from_utf8(filename).c_str(), wstr_from_utf8(mode).c_str());
+ }
+ CATCH_AND_RETURN(NULL)
+#else
+ return fopen(filename, mode);
+#endif
+}
+
+FILE *
+rnp_fdopen(int fildes, const char *mode)
+{
+#ifdef _WIN32
+ return _fdopen(fildes, mode);
+#else
+ return fdopen(fildes, mode);
+#endif
+}
+
+int
+rnp_access(const char *path, int mode)
+{
+#ifdef _WIN32
+ try {
+ return _waccess(wstr_from_utf8(path).c_str(), mode);
+ }
+ CATCH_AND_RETURN(-1)
+#else
+ return access(path, mode);
+#endif
+}
+
+int
+rnp_stat(const char *filename, struct stat *statbuf)
+{
+#ifdef _WIN32
+ static_assert(sizeof(struct stat) == sizeof(struct _stat64i32),
+ "stat is expected to match _stat64i32");
+ try {
+ return _wstat64i32(wstr_from_utf8(filename).c_str(), (struct _stat64i32 *) statbuf);
+ }
+ CATCH_AND_RETURN(-1)
+#else
+ return stat(filename, statbuf);
+#endif
+}
+
+#ifdef _WIN32
+int
+rnp_mkdir(const char *path)
+{
+ try {
+ return _wmkdir(wstr_from_utf8(path).c_str());
+ }
+ CATCH_AND_RETURN(-1)
+}
+#endif
+
+int
+rnp_rename(const char *oldpath, const char *newpath)
+{
+#ifdef _WIN32
+ try {
+ return _wrename(wstr_from_utf8(oldpath).c_str(), wstr_from_utf8(newpath).c_str());
+ }
+ CATCH_AND_RETURN(-1)
+#else
+ return rename(oldpath, newpath);
+#endif
+}
+
+#ifdef _WIN32
+_WDIR *
+#else
+DIR *
+#endif
+rnp_opendir(const char *path)
+{
+#ifdef _WIN32
+ try {
+ return _wopendir(wstr_from_utf8(path).c_str());
+ }
+ CATCH_AND_RETURN(NULL)
+#else
+ return opendir(path);
+#endif
+}
+
+std::string
+#ifdef _WIN32
+rnp_readdir_name(_WDIR *dir)
+{
+ _wdirent *ent;
+ for (;;) {
+ if ((ent = _wreaddir(dir)) == NULL) {
+ return std::string();
+ }
+ if (wcscmp(ent->d_name, L".") && wcscmp(ent->d_name, L"..")) {
+ break;
+ }
+ }
+ try {
+ return wstr_to_utf8(ent->d_name);
+ }
+ CATCH_AND_RETURN(std::string())
+#else
+rnp_readdir_name(DIR *dir)
+{
+ dirent *ent;
+ for (;;) {
+ if ((ent = readdir(dir)) == NULL) {
+ return std::string();
+ }
+ if (strcmp(ent->d_name, ".") && strcmp(ent->d_name, "..")) {
+ break;
+ }
+ }
+ return std::string(ent->d_name);
+#endif
+}
+
+/* return the file modification time */
+int64_t
+rnp_filemtime(const char *path)
+{
+ struct stat st;
+
+ if (rnp_stat(path, &st) != 0) {
+ return 0;
+ } else {
+ return st.st_mtime;
+ }
+}
+
+#ifdef _WIN32
+static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/** @private
+ * generate a temporary file name based on TMPL.
+ *
+ * @param tmpl filename template in UTF-8 ending in XXXXXX
+ * @return file descriptor of newly created and opened file, or -1 on error
+ **/
+int
+rnp_mkstemp(char *tmpl)
+{
+ try {
+ int save_errno = errno;
+ const int mask_length = 6;
+ int len = strlen(tmpl);
+ if (len < mask_length || strcmp(&tmpl[len - mask_length], "XXXXXX")) {
+ errno = EINVAL;
+ return -1;
+ }
+ std::wstring tmpl_w = wstr_from_utf8(tmpl, tmpl + len - mask_length);
+
+ /* This is where the Xs start. */
+ char *XXXXXX = &tmpl[len - mask_length];
+
+ std::random_device rd;
+ std::mt19937_64 rng(rd());
+
+ for (unsigned int countdown = TMP_MAX; --countdown;) {
+ unsigned long long v = rng();
+
+ XXXXXX[0] = letters[v % 36];
+ v /= 36;
+ XXXXXX[1] = letters[v % 36];
+ v /= 36;
+ XXXXXX[2] = letters[v % 36];
+ v /= 36;
+ XXXXXX[3] = letters[v % 36];
+ v /= 36;
+ XXXXXX[4] = letters[v % 36];
+ v /= 36;
+ XXXXXX[5] = letters[v % 36];
+
+ int flags = O_WRONLY | O_CREAT | O_EXCL | O_BINARY;
+ int fd =
+ _wopen((tmpl_w + wstr_from_utf8(XXXXXX)).c_str(), flags, _S_IREAD | _S_IWRITE);
+ if (fd != -1) {
+ errno = save_errno;
+ return fd;
+ } else if (errno != EEXIST)
+ return -1;
+ }
+
+ // We got out of the loop because we ran out of combinations to try.
+ errno = EEXIST;
+ return -1;
+ }
+ CATCH_AND_RETURN(-1)
+}
+#endif // _WIN32
+
+namespace rnp {
+namespace path {
+inline char
+separator()
+{
+#ifdef _WIN32
+ return '\\';
+#else
+ return '/';
+#endif
+}
+
+bool
+exists(const std::string &path, bool is_dir)
+{
+ return is_dir ? rnp_dir_exists(path.c_str()) : rnp_file_exists(path.c_str());
+}
+
+bool
+empty(const std::string &path)
+{
+ auto dir = rnp_opendir(path.c_str());
+ if (!dir) {
+ return true;
+ }
+ bool empty = rnp_readdir_name(dir).empty();
+ rnp_closedir(dir);
+ return empty;
+}
+
+std::string
+HOME(const std::string &sdir)
+{
+ const char *home = getenv("HOME");
+ if (!home) {
+ return "";
+ }
+ return sdir.empty() ? home : append(home, sdir);
+}
+
+static bool
+has_forward_slash(const std::string &path)
+{
+ return std::find(path.begin(), path.end(), '/') != path.end();
+}
+
+std::string
+append(const std::string &path, const std::string &name)
+{
+ bool no_sep = path.empty() || name.empty() || (rnp::is_slash(path.back())) ||
+ (rnp::is_slash(name.front()));
+ if (no_sep) {
+ return path + name;
+ }
+ /* Use forward slash if there is at least one in the path/name. */
+ char sep = has_forward_slash(path) || has_forward_slash(name) ? '/' : separator();
+ return path + sep + name;
+}
+
+} // namespace path
+} // namespace rnp
diff --git a/src/common/file-utils.h b/src/common/file-utils.h
new file mode 100644
index 0000000..1993d17
--- /dev/null
+++ b/src/common/file-utils.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019-2020, [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RNP_FILE_UTILS_H_
+#define RNP_FILE_UTILS_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <string>
+
+bool rnp_file_exists(const char *path);
+bool rnp_dir_exists(const char *path);
+int64_t rnp_filemtime(const char *path);
+int rnp_open(const char *filename, int oflag, int pmode);
+FILE * rnp_fopen(const char *filename, const char *mode);
+FILE * rnp_fdopen(int fildes, const char *mode);
+int rnp_access(const char *path, int mode);
+int rnp_stat(const char *filename, struct stat *statbuf);
+int rnp_rename(const char *oldpath, const char *newpath);
+int rnp_unlink(const char *path);
+
+#ifdef _WIN32
+#define rnp_closedir _wclosedir
+int rnp_mkdir(const char *path);
+_WDIR * rnp_opendir(const char *path);
+std::string rnp_readdir_name(_WDIR *dir);
+#else
+#define rnp_closedir closedir
+DIR * rnp_opendir(const char *path);
+std::string rnp_readdir_name(DIR *dir);
+#endif
+#ifdef _WIN32
+#define RNP_MKDIR(pathname, mode) rnp_mkdir(pathname)
+#else
+#define RNP_MKDIR(pathname, mode) mkdir(pathname, mode)
+#endif
+
+#ifdef _MSC_VER
+#define R_OK 4 /* Test for read permission. */
+#define W_OK 2 /* Test for write permission. */
+#define F_OK 0 /* Test for existence. */
+#endif
+
+/** @private
+ * generate a temporary file name based on TMPL. TMPL must match the
+ * rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed
+ * does not exist at the time of the call to mkstemp. TMPL is
+ * overwritten with the result.get the list item at specified index
+ *
+ * @param tmpl filename template
+ * @return file descriptor of newly created and opened file, or -1 on error
+ **/
+int rnp_mkstemp(char *tmpl);
+
+namespace rnp {
+namespace path {
+inline char separator();
+bool exists(const std::string &path, bool is_dir = false);
+bool empty(const std::string &path);
+std::string HOME(const std::string &sdir = "");
+std::string append(const std::string &path, const std::string &name);
+} // namespace path
+} // namespace rnp
+
+#endif
diff --git a/src/common/getoptwin.h b/src/common/getoptwin.h
new file mode 100644
index 0000000..84a9583
--- /dev/null
+++ b/src/common/getoptwin.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _GETOPTWIN_H
+#define _GETOPTWIN_H 1
+
+#include <getopt.h>
+
+#define _BEGIN_EXTERN_C extern "C" {
+#define _END_EXTERN_C }
+
+#endif /* getoptwin.h */ \ No newline at end of file
diff --git a/src/common/str-utils.cpp b/src/common/str-utils.cpp
new file mode 100644
index 0000000..245e31e
--- /dev/null
+++ b/src/common/str-utils.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2017 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/** String utilities
+ * @file
+ */
+
+#include <cstddef>
+#include <cstring>
+#include <cctype>
+#include <stdexcept>
+#include "str-utils.h"
+#ifdef _WIN32
+#include <locale>
+#include <codecvt>
+#endif
+
+using std::size_t;
+using std::strlen;
+
+namespace rnp {
+char *
+strip_eol(char *s)
+{
+ size_t len = strlen(s);
+
+ while ((len > 0) && ((s[len - 1] == '\n') || (s[len - 1] == '\r'))) {
+ s[--len] = '\0';
+ }
+
+ return s;
+}
+
+bool
+strip_eol(std::string &s)
+{
+ size_t len = s.size();
+ while (len && ((s[len - 1] == '\n') || (s[len - 1] == '\r'))) {
+ len--;
+ }
+ if (len == s.size()) {
+ return false;
+ }
+ s.resize(len);
+ return true;
+}
+
+bool
+is_blank_line(const char *line, size_t len)
+{
+ for (size_t i = 0; i < len && line[i]; i++) {
+ if (line[i] != ' ' && line[i] != '\t' && line[i] != '\r') {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+str_case_eq(const char *s1, const char *s2)
+{
+ while (*s1 && *s2) {
+ if (std::tolower(*s1) != std::tolower(*s2)) {
+ return false;
+ }
+ s1++;
+ s2++;
+ }
+ return !*s1 && !*s2;
+}
+
+bool
+str_case_eq(const std::string &s1, const std::string &s2)
+{
+ if (s1.size() != s2.size()) {
+ return false;
+ }
+ return str_case_eq(s1.c_str(), s2.c_str());
+}
+
+static size_t
+hex_prefix_len(const std::string &str)
+{
+ if ((str.length() >= 2) && (str[0] == '0') && ((str[1] == 'x') || (str[1] == 'X'))) {
+ return 2;
+ }
+ return 0;
+}
+
+bool
+is_hex(const std::string &s)
+{
+ for (size_t i = hex_prefix_len(s); i < s.length(); i++) {
+ auto &ch = s[i];
+ if ((ch >= '0') && (ch <= '9')) {
+ continue;
+ }
+ if ((ch >= 'a') && (ch <= 'f')) {
+ continue;
+ }
+ if ((ch >= 'A') && (ch <= 'F')) {
+ continue;
+ }
+ if ((ch == ' ') || (ch == '\t')) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+std::string
+strip_hex(const std::string &s)
+{
+ std::string res = "";
+ for (size_t idx = hex_prefix_len(s); idx < s.length(); idx++) {
+ auto ch = s[idx];
+ if ((ch == ' ') || (ch == '\t')) {
+ continue;
+ }
+ res.push_back(ch);
+ }
+ return res;
+}
+
+char *
+lowercase(char *s)
+{
+ if (!s) {
+ return s;
+ }
+ for (char *ptr = s; *ptr; ++ptr) {
+ *ptr = tolower(*ptr);
+ }
+ return s;
+}
+
+bool
+str_to_int(const std::string &s, int &val)
+{
+ for (const char &ch : s) {
+ if ((ch < '0') || (ch > '9')) {
+ return false;
+ }
+ }
+ try {
+ val = std::stoi(s);
+ } catch (std::out_of_range const &ex) {
+ return false;
+ }
+ return true;
+}
+
+bool
+is_slash(char c)
+{
+ return (c == '/') || (c == '\\');
+}
+
+} // namespace rnp
+
+#ifdef _WIN32
+std::wstring
+wstr_from_utf8(const char *s)
+{
+ std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8conv;
+ return utf8conv.from_bytes(s);
+}
+
+std::wstring
+wstr_from_utf8(const char *first, const char *last)
+{
+ std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8conv;
+ return utf8conv.from_bytes(first, last);
+}
+
+std::wstring
+wstr_from_utf8(const std::string &s)
+{
+ return wstr_from_utf8(s.c_str());
+}
+
+std::string
+wstr_to_utf8(const wchar_t *ws)
+{
+ std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8conv;
+ return utf8conv.to_bytes(ws);
+}
+
+std::string
+wstr_to_utf8(const std::wstring &ws)
+{
+ return wstr_to_utf8(ws.c_str());
+}
+#endif
diff --git a/src/common/str-utils.h b/src/common/str-utils.h
new file mode 100644
index 0000000..9a4eb73
--- /dev/null
+++ b/src/common/str-utils.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019-2020 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RNP_STR_UTILS_H_
+#define RNP_STR_UTILS_H_
+
+#include <string>
+
+namespace rnp {
+char *strip_eol(char *s);
+/**
+ * @brief Strip EOL characters from the string's end.
+ *
+ * @param s string to check
+ * @return true if EOL was found and stripped, or false otherwise.
+ */
+bool strip_eol(std::string &s);
+bool is_blank_line(const char *line, size_t len);
+bool str_case_eq(const char *s1, const char *s2);
+bool str_case_eq(const std::string &s1, const std::string &s2);
+
+bool is_hex(const std::string &s);
+std::string strip_hex(const std::string &s);
+
+/**
+ * @brief Convert string to lowercase and return it.
+ */
+char *lowercase(char *s);
+bool str_to_int(const std::string &s, int &val);
+bool is_slash(char c);
+} // namespace rnp
+#ifdef _WIN32
+#include <string>
+std::wstring wstr_from_utf8(const char *s);
+std::wstring wstr_from_utf8(const char *first, const char *last);
+std::wstring wstr_from_utf8(const std::string &s);
+std::string wstr_to_utf8(const wchar_t *ws);
+std::string wstr_to_utf8(const std::wstring &ws);
+#endif // _WIN32
+#endif
diff --git a/src/common/time-utils.cpp b/src/common/time-utils.cpp
new file mode 100644
index 0000000..83d934a
--- /dev/null
+++ b/src/common/time-utils.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2021 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/** Time utilities
+ * @file
+ */
+
+#include <stdint.h>
+#include "time-utils.h"
+
+static inline time_t
+adjust_time32(time_t t)
+{
+ return (sizeof(t) == 4 && t < 0) ? INT32_MAX : t;
+}
+
+bool
+rnp_y2k38_warning(time_t t)
+{
+ return (sizeof(t) == 4 && (t < 0 || t == INT32_MAX));
+}
+
+time_t
+rnp_mktime(struct tm *tm)
+{
+ return adjust_time32(mktime(tm));
+}
+
+void
+rnp_gmtime(time_t t, struct tm &tm)
+{
+ time_t adjusted = adjust_time32(t);
+#ifndef _WIN32
+ gmtime_r(&adjusted, &tm);
+#else
+ (void) gmtime_s(&tm, &adjusted);
+#endif
+}
+
+void
+rnp_localtime(time_t t, struct tm &tm)
+{
+ time_t adjusted = adjust_time32(t);
+#ifndef _WIN32
+ localtime_r(&adjusted, &tm);
+#else
+ (void) localtime_s(&tm, &adjusted);
+#endif
+}
+
+std::string
+rnp_ctime(time_t t)
+{
+ char time_buf[26];
+ time_t adjusted = adjust_time32(t);
+#ifndef _WIN32
+ (void) ctime_r(&adjusted, time_buf);
+#else
+ (void) ctime_s(time_buf, sizeof(time_buf), &adjusted);
+#endif
+ return std::string(time_buf);
+}
+
+time_t
+rnp_timeadd(time_t t1, time_t t2)
+{
+ if (sizeof(time_t) == 4) {
+ if (t1 < 0 || t2 < 0) {
+ return INT32_MAX;
+ }
+ return adjust_time32(t1 + t2);
+ }
+ return t1 + t2;
+}
diff --git a/src/common/time-utils.h b/src/common/time-utils.h
new file mode 100644
index 0000000..525309e
--- /dev/null
+++ b/src/common/time-utils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RNP_TIME_UTILS_H_
+#define RNP_TIME_UTILS_H_
+
+#include <time.h>
+#include <string>
+time_t rnp_mktime(struct tm *tm);
+void rnp_gmtime(time_t t, struct tm &tm);
+void rnp_localtime(time_t t, struct tm &tm);
+bool rnp_y2k38_warning(time_t t);
+std::string rnp_ctime(time_t t);
+time_t rnp_timeadd(time_t t1, time_t t2);
+#endif
diff --git a/src/common/uniwin.h b/src/common/uniwin.h
new file mode 100644
index 0000000..095c325
--- /dev/null
+++ b/src/common/uniwin.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 [Ribose Inc](https://www.ribose.com).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#if !defined(_UNISTD_H) && defined(_MSC_VER)
+#define _UNISTD_H 1
+
+/* Taken partially from
+ * https://stackoverflow.com/a/826027/1202830
+ */
+
+#include <io.h>
+#include "getoptwin.h"
+#include <direct.h> /* for _getcwd() and _chdir() */
+
+#ifdef _WIN64
+#define ssize_t __int64
+#else
+#define ssize_t long
+#endif
+
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+#define STDERR_FILENO 2
+
+#define NOMINMAX 1 /* to retain std::min and std::max */
+#include <windows.h>
+#include <dirent.h> /* for S_ISREG and S_ISDIR */
+
+#define strncasecmp strnicmp
+#define strcasecmp stricmp
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN _MAX_PATH
+#endif
+
+typedef unsigned short mode_t;
+
+#endif \ No newline at end of file