aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/wutil.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2015-07-24 00:50:58 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2015-07-24 00:59:27 -0700
commitb4f53143b0e05fd3061cdf2e65e17a6a2904090b (patch)
tree4785bf31f7b89fc2420aa740d9a6967dc6c6f9b1 /src/wutil.cpp
parent9c2fdc6da57032c4448b59de5872086eea626b74 (diff)
Migrate source files into src/ directory
This change moves source files into a src/ directory, and puts object files into an obj/ directory. The Makefile and xcode project are updated accordingly. Fixes #1866
Diffstat (limited to 'src/wutil.cpp')
-rw-r--r--src/wutil.cpp616
1 files changed, 616 insertions, 0 deletions
diff --git a/src/wutil.cpp b/src/wutil.cpp
new file mode 100644
index 00000000..2faec257
--- /dev/null
+++ b/src/wutil.cpp
@@ -0,0 +1,616 @@
+/** \file wutil.c
+ Wide character equivalents of various standard unix
+ functions.
+*/
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <string>
+#include <map>
+
+
+#if HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#include "fallback.h"
+#include "util.h"
+
+#include "common.h"
+#include "wutil.h"
+
+typedef std::string cstring;
+
+const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, -1, (uint32_t)-1};
+
+/**
+ Minimum length of the internal covnersion buffers
+*/
+#define TMP_LEN_MIN 256
+
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+/**
+ Fallback length of MAXPATHLEN. Just a hopefully sane value...
+*/
+#define PATH_MAX 4096
+#endif
+#endif
+
+/* Lock to protect wgettext */
+static pthread_mutex_t wgettext_lock;
+
+/* Maps string keys to (immortal) pointers to string values */
+typedef std::map<wcstring, wcstring *> wgettext_map_t;
+static std::map<wcstring, wcstring *> wgettext_map;
+
+void wutil_init()
+{
+}
+
+void wutil_destroy()
+{
+}
+
+bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir)
+{
+ struct dirent *d = readdir(dir);
+ if (!d) return false;
+
+ out_name = str2wcstring(d->d_name);
+ if (out_is_dir)
+ {
+ /* The caller cares if this is a directory, so check */
+ bool is_dir = false;
+
+ /* We may be able to skip stat, if the readdir can tell us the file type directly */
+ bool check_with_stat = true;
+#ifdef HAVE_STRUCT_DIRENT_D_TYPE
+ if (d->d_type == DT_DIR)
+ {
+ /* Known directory */
+ is_dir = true;
+ check_with_stat = false;
+ }
+ else if (d->d_type == DT_LNK || d->d_type == DT_UNKNOWN)
+ {
+ /* We want to treat symlinks to directories as directories. Use stat to resolve it. */
+ check_with_stat = true;
+ }
+ else
+ {
+ /* Regular file */
+ is_dir = false;
+ check_with_stat = false;
+ }
+#endif // HAVE_STRUCT_DIRENT_D_TYPE
+ if (check_with_stat)
+ {
+ /* We couldn't determine the file type from the dirent; check by stat'ing it */
+ cstring fullpath = wcs2string(dir_path);
+ fullpath.push_back('/');
+ fullpath.append(d->d_name);
+ struct stat buf;
+ if (stat(fullpath.c_str(), &buf) != 0)
+ {
+ is_dir = false;
+ }
+ else
+ {
+ is_dir = !!(S_ISDIR(buf.st_mode));
+ }
+ }
+ *out_is_dir = is_dir;
+ }
+ return true;
+}
+
+bool wreaddir(DIR *dir, std::wstring &out_name)
+{
+ struct dirent *d = readdir(dir);
+ if (!d) return false;
+
+ out_name = str2wcstring(d->d_name);
+ return true;
+}
+
+
+wchar_t *wgetcwd(wchar_t *buff, size_t sz)
+{
+ char *buffc = (char *)malloc(sz*MAX_UTF8_BYTES);
+ char *res;
+ wchar_t *ret = 0;
+
+ if (!buffc)
+ {
+ errno = ENOMEM;
+ return 0;
+ }
+
+ res = getcwd(buffc, sz*MAX_UTF8_BYTES);
+ if (res)
+ {
+ if ((size_t)-1 != mbstowcs(buff, buffc, sizeof(wchar_t) * sz))
+ {
+ ret = buff;
+ }
+ }
+
+ free(buffc);
+
+ return ret;
+}
+
+int wchdir(const wcstring &dir)
+{
+ cstring tmp = wcs2string(dir);
+ return chdir(tmp.c_str());
+}
+
+FILE *wfopen(const wcstring &path, const char *mode)
+{
+ int permissions = 0, options = 0;
+ size_t idx = 0;
+ switch (mode[idx++])
+ {
+ case 'r':
+ permissions = O_RDONLY;
+ break;
+ case 'w':
+ permissions = O_WRONLY;
+ options = O_CREAT | O_TRUNC;
+ break;
+ case 'a':
+ permissions = O_WRONLY;
+ options = O_CREAT | O_APPEND;
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ break;
+ }
+ /* Skip binary */
+ if (mode[idx] == 'b')
+ idx++;
+
+ /* Consider append option */
+ if (mode[idx] == '+')
+ permissions = O_RDWR;
+
+ int fd = wopen_cloexec(path, permissions | options, 0666);
+ if (fd < 0)
+ return NULL;
+ FILE *result = fdopen(fd, mode);
+ if (result == NULL)
+ close(fd);
+ return result;
+}
+
+bool set_cloexec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+ if (flags < 0)
+ {
+ return false;
+ }
+ else if (flags & FD_CLOEXEC)
+ {
+ return true;
+ }
+ else
+ {
+ return fcntl(fd, F_SETFD, flags | FD_CLOEXEC) >= 0;
+ }
+}
+
+static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec)
+{
+ ASSERT_IS_NOT_FORKED_CHILD();
+ cstring tmp = wcs2string(pathname);
+ /* Prefer to use O_CLOEXEC. It has to both be defined and nonzero. */
+#ifdef O_CLOEXEC
+ if (cloexec && (O_CLOEXEC != 0))
+ {
+ flags |= O_CLOEXEC;
+ cloexec = false;
+ }
+#endif
+ int fd = ::open(tmp.c_str(), flags, mode);
+ if (cloexec && fd >= 0 && ! set_cloexec(fd))
+ {
+ close(fd);
+ fd = -1;
+ }
+ return fd;
+
+}
+
+int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode)
+{
+ return wopen_internal(pathname, flags, mode, true);
+}
+
+DIR *wopendir(const wcstring &name)
+{
+ const cstring tmp = wcs2string(name);
+ return opendir(tmp.c_str());
+}
+
+int wstat(const wcstring &file_name, struct stat *buf)
+{
+ const cstring tmp = wcs2string(file_name);
+ return stat(tmp.c_str(), buf);
+}
+
+int lwstat(const wcstring &file_name, struct stat *buf)
+{
+ const cstring tmp = wcs2string(file_name);
+ return lstat(tmp.c_str(), buf);
+}
+
+int waccess(const wcstring &file_name, int mode)
+{
+ const cstring tmp = wcs2string(file_name);
+ return access(tmp.c_str(), mode);
+}
+
+int wunlink(const wcstring &file_name)
+{
+ const cstring tmp = wcs2string(file_name);
+ return unlink(tmp.c_str());
+}
+
+void wperror(const wchar_t *s)
+{
+ int e = errno;
+ if (s[0] != L'\0')
+ {
+ fwprintf(stderr, L"%ls: ", s);
+ }
+ fwprintf(stderr, L"%s\n", strerror(e));
+}
+
+int make_fd_nonblocking(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ int err = 0;
+ if (!(flags & O_NONBLOCK))
+ {
+ err = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ }
+ return err == -1 ? errno : 0;
+}
+
+int make_fd_blocking(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ int err = 0;
+ if (flags & O_NONBLOCK)
+ {
+ err = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+ }
+ return err == -1 ? errno : 0;
+}
+
+static inline void safe_append(char *buffer, const char *s, size_t buffsize)
+{
+ strncat(buffer, s, buffsize - strlen(buffer) - 1);
+}
+
+// In general, strerror is not async-safe, and therefore we cannot use it directly
+// So instead we have to grub through sys_nerr and sys_errlist directly
+// On GNU toolchain, this will produce a deprecation warning from the linker (!!),
+// which appears impossible to suppress!
+const char *safe_strerror(int err)
+{
+#if defined(__UCLIBC__)
+ // uClibc does not have sys_errlist, however, its strerror is believed to be async-safe
+ // See #808
+ return strerror(err);
+#elif defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST)
+#ifdef HAVE_SYS_ERRLIST
+ if (err >= 0 && err < sys_nerr && sys_errlist[err] != NULL)
+ {
+ return sys_errlist[err];
+ }
+#elif defined(HAVE__SYS__ERRS)
+ extern const char _sys_errs[];
+ extern const int _sys_index[];
+ extern int _sys_num_err;
+
+ if (err >= 0 && err < _sys_num_err) {
+ return &_sys_errs[_sys_index[err]];
+ }
+#endif // either HAVE__SYS__ERRS or HAVE_SYS_ERRLIST
+ else
+#endif // defined(HAVE__SYS__ERRS) || defined(HAVE_SYS_ERRLIST)
+ {
+ int saved_err = errno;
+
+ /* Use a shared buffer for this case */
+ static char buff[384];
+ char errnum_buff[64];
+ format_long_safe(errnum_buff, err);
+
+ buff[0] = '\0';
+ safe_append(buff, "unknown error (errno was ", sizeof buff);
+ safe_append(buff, errnum_buff, sizeof buff);
+ safe_append(buff, ")", sizeof buff);
+
+ errno = saved_err;
+ return buff;
+ }
+}
+
+void safe_perror(const char *message)
+{
+ // Note we cannot use strerror, because on Linux it uses gettext, which is not safe
+ int err = errno;
+
+ char buff[384];
+ buff[0] = '\0';
+
+ if (message)
+ {
+ safe_append(buff, message, sizeof buff);
+ safe_append(buff, ": ", sizeof buff);
+ }
+ safe_append(buff, safe_strerror(err), sizeof buff);
+ safe_append(buff, "\n", sizeof buff);
+
+ write_ignore(STDERR_FILENO, buff, strlen(buff));
+ errno = err;
+}
+
+#ifdef HAVE_REALPATH_NULL
+
+wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path)
+{
+ cstring narrow_path = wcs2string(pathname);
+ char *narrow_res = realpath(narrow_path.c_str(), NULL);
+
+ if (!narrow_res)
+ return NULL;
+
+ wchar_t *res;
+ wcstring wide_res = str2wcstring(narrow_res);
+ if (resolved_path)
+ {
+ wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX);
+ res = resolved_path;
+ }
+ else
+ {
+ res = wcsdup(wide_res.c_str());
+ }
+
+ free(narrow_res);
+
+ return res;
+}
+
+#else
+
+wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path)
+{
+ cstring tmp = wcs2string(pathname);
+ char narrow_buff[PATH_MAX];
+ char *narrow_res = realpath(tmp.c_str(), narrow_buff);
+ wchar_t *res;
+
+ if (!narrow_res)
+ return 0;
+
+ const wcstring wide_res = str2wcstring(narrow_res);
+ if (resolved_path)
+ {
+ wcslcpy(resolved_path, wide_res.c_str(), PATH_MAX);
+ res = resolved_path;
+ }
+ else
+ {
+ res = wcsdup(wide_res.c_str());
+ }
+ return res;
+}
+
+#endif
+
+
+wcstring wdirname(const wcstring &path)
+{
+ char *tmp = wcs2str(path.c_str());
+ char *narrow_res = dirname(tmp);
+ wcstring result = format_string(L"%s", narrow_res);
+ free(tmp);
+ return result;
+}
+
+wcstring wbasename(const wcstring &path)
+{
+ char *tmp = wcs2str(path.c_str());
+ char *narrow_res = basename(tmp);
+ wcstring result = format_string(L"%s", narrow_res);
+ free(tmp);
+ return result;
+}
+
+/* Really init wgettext */
+static void wgettext_really_init()
+{
+ pthread_mutex_init(&wgettext_lock, NULL);
+ fish_bindtextdomain(PACKAGE_NAME, LOCALEDIR);
+ fish_textdomain(PACKAGE_NAME);
+}
+
+/**
+ For wgettext: Internal init function. Automatically called when a translation is first requested.
+*/
+static void wgettext_init_if_necessary()
+{
+ static pthread_once_t once = PTHREAD_ONCE_INIT;
+ pthread_once(&once, wgettext_really_init);
+}
+
+const wchar_t *wgettext(const wchar_t *in)
+{
+ if (!in)
+ return in;
+
+ // preserve errno across this since this is often used in printing error messages
+ int err = errno;
+
+ wgettext_init_if_necessary();
+
+ wcstring key = in;
+ scoped_lock lock(wgettext_lock);
+
+ wcstring *& val = wgettext_map[key];
+ if (val == NULL)
+ {
+ cstring mbs_in = wcs2string(key);
+ char *out = fish_gettext(mbs_in.c_str());
+ val = new wcstring(format_string(L"%s", out)); //note that this writes into the map!
+ }
+ errno = err;
+ return val->c_str(); //looks dangerous but is safe, since the string is stored in the map
+}
+
+int wmkdir(const wcstring &name, int mode)
+{
+ cstring name_narrow = wcs2string(name);
+ return mkdir(name_narrow.c_str(), mode);
+}
+
+int wrename(const wcstring &old, const wcstring &newv)
+{
+ cstring old_narrow = wcs2string(old);
+ cstring new_narrow =wcs2string(newv);
+ return rename(old_narrow.c_str(), new_narrow.c_str());
+}
+
+int fish_wcstoi(const wchar_t *str, wchar_t ** endptr, int base)
+{
+ long ret = wcstol(str, endptr, base);
+ if (ret > INT_MAX)
+ {
+ ret = INT_MAX;
+ errno = ERANGE;
+ }
+ else if (ret < INT_MIN)
+ {
+ ret = INT_MIN;
+ errno = ERANGE;
+ }
+ return (int)ret;
+}
+
+file_id_t file_id_t::file_id_from_stat(const struct stat *buf)
+{
+ assert(buf != NULL);
+
+ file_id_t result = {};
+ result.device = buf->st_dev;
+ result.inode = buf->st_ino;
+ result.size = buf->st_size;
+ result.change_seconds = buf->st_ctime;
+
+#if STAT_HAVE_NSEC
+ result.change_nanoseconds = buf->st_ctime_nsec;
+#elif defined(__APPLE__)
+ result.change_nanoseconds = buf->st_ctimespec.tv_nsec;
+#elif defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE)
+ result.change_nanoseconds = buf->st_ctim.tv_nsec;
+#else
+ result.change_nanoseconds = 0;
+#endif
+
+#if defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
+ result.generation = buf->st_gen;
+#else
+ result.generation = 0;
+#endif
+ return result;
+}
+
+
+file_id_t file_id_for_fd(int fd)
+{
+ file_id_t result = kInvalidFileID;
+ struct stat buf = {};
+ if (0 == fstat(fd, &buf))
+ {
+ result = file_id_t::file_id_from_stat(&buf);
+ }
+ return result;
+}
+
+file_id_t file_id_for_path(const wcstring &path)
+{
+ file_id_t result = kInvalidFileID;
+ struct stat buf = {};
+ if (0 == wstat(path, &buf))
+ {
+ result = file_id_t::file_id_from_stat(&buf);
+ }
+ return result;
+
+}
+
+bool file_id_t::operator==(const file_id_t &rhs) const
+{
+ return device == rhs.device &&
+ inode == rhs.inode &&
+ size == rhs.size &&
+ change_seconds == rhs.change_seconds &&
+ change_nanoseconds == rhs.change_nanoseconds &&
+ generation == rhs.generation;
+}
+
+bool file_id_t::operator!=(const file_id_t &rhs) const
+{
+ return ! (*this == rhs);
+}
+
+template<typename T>
+int compare(T a, T b)
+{
+ if (a < b)
+ {
+ return -1;
+ }
+ else if (a > b)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+bool file_id_t::operator<(const file_id_t &rhs) const
+{
+ /* Compare each field, stopping when we get to a non-equal field */
+ int ret = 0;
+ if (! ret) ret = compare(device, rhs.device);
+ if (! ret) ret = compare(inode, rhs.inode);
+ if (! ret) ret = compare(size, rhs.size);
+ if (! ret) ret = compare(generation, rhs.generation);
+ if (! ret) ret = compare(change_seconds, rhs.change_seconds);
+ if (! ret) ret = compare(change_nanoseconds, rhs.change_nanoseconds);
+ return ret < 0;
+}