summaryrefslogtreecommitdiffstats
path: root/src/common/file-utils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/file-utils.cpp')
-rw-r--r--src/common/file-utils.cpp359
1 files changed, 359 insertions, 0 deletions
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