aboutsummaryrefslogtreecommitdiffhomepage
path: root/wutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wutil.cpp')
-rw-r--r--wutil.cpp195
1 files changed, 142 insertions, 53 deletions
diff --git a/wutil.cpp b/wutil.cpp
index 3f70368e..41400e98 100644
--- a/wutil.cpp
+++ b/wutil.cpp
@@ -35,6 +35,8 @@
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
*/
@@ -75,14 +77,32 @@ bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &ou
if (out_is_dir)
{
/* The caller cares if this is a directory, so check */
- bool is_dir;
+ 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);
@@ -96,10 +116,6 @@ bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &ou
is_dir = !!(S_ISDIR(buf.st_mode));
}
}
- else
- {
- is_dir = false;
- }
*out_is_dir = is_dir;
}
return true;
@@ -186,12 +202,6 @@ FILE *wfopen(const wcstring &path, const char *mode)
return result;
}
-FILE *wfreopen(const wcstring &path, const char *mode, FILE *stream)
-{
- cstring tmp = wcs2string(path);
- return freopen(tmp.c_str(), mode, stream);
-}
-
bool set_cloexec(int fd)
{
int flags = fcntl(fd, F_GETFD, 0);
@@ -230,62 +240,48 @@ static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool
return fd;
}
-int wopen(const wcstring &pathname, int flags, mode_t mode)
-{
- // off the main thread, always use wopen_cloexec
- ASSERT_IS_MAIN_THREAD();
- ASSERT_IS_NOT_FORKED_CHILD();
- return wopen_internal(pathname, flags, mode, false);
-}
int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode)
{
return wopen_internal(pathname, flags, mode, true);
}
-
-int wcreat(const wcstring &pathname, mode_t mode)
-{
- cstring tmp = wcs2string(pathname);
- return creat(tmp.c_str(), mode);
-}
-
DIR *wopendir(const wcstring &name)
{
- cstring tmp = wcs2string(name);
+ const cstring tmp = wcs2string(name);
return opendir(tmp.c_str());
}
int wstat(const wcstring &file_name, struct stat *buf)
{
- cstring tmp = wcs2string(file_name);
+ const cstring tmp = wcs2string(file_name);
return stat(tmp.c_str(), buf);
}
int lwstat(const wcstring &file_name, struct stat *buf)
{
- cstring tmp = wcs2string(file_name);
+ const cstring tmp = wcs2string(file_name);
return lstat(tmp.c_str(), buf);
}
int waccess(const wcstring &file_name, int mode)
{
- cstring tmp = wcs2string(file_name);
+ const cstring tmp = wcs2string(file_name);
return access(tmp.c_str(), mode);
}
int wunlink(const wcstring &file_name)
{
- cstring tmp = wcs2string(file_name);
+ const cstring tmp = wcs2string(file_name);
return unlink(tmp.c_str());
}
-void wperror(const wcstring &s)
+void wperror(const wchar_t *s)
{
int e = errno;
- if (!s.empty())
+ if (s[0] != L'\0')
{
- fwprintf(stderr, L"%ls: ", s.c_str());
+ fwprintf(stderr, L"%ls: ", s);
}
fwprintf(stderr, L"%s\n", strerror(e));
}
@@ -317,18 +313,33 @@ 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);
-#else
+#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;
@@ -345,7 +356,6 @@ const char *safe_strerror(int err)
errno = saved_err;
return buff;
}
-#endif
}
void safe_perror(const char *message)
@@ -364,7 +374,7 @@ void safe_perror(const char *message)
safe_append(buff, safe_strerror(err), sizeof buff);
safe_append(buff, "\n", sizeof buff);
- write(STDERR_FILENO, buff, strlen(buff));
+ write_ignore(STDERR_FILENO, buff, strlen(buff));
errno = err;
}
@@ -476,25 +486,10 @@ const wchar_t *wgettext(const wchar_t *in)
{
cstring mbs_in = wcs2string(key);
char *out = fish_gettext(mbs_in.c_str());
- val = new wcstring(format_string(L"%s", out));
+ val = new wcstring(format_string(L"%s", out)); //note that this writes into the map!
}
errno = err;
- return val->c_str();
-}
-
-const wchar_t *wgetenv(const wcstring &name)
-{
- ASSERT_IS_MAIN_THREAD();
- cstring name_narrow = wcs2string(name);
- char *res_narrow = getenv(name_narrow.c_str());
- static wcstring out;
-
- if (!res_narrow)
- return 0;
-
- out = format_string(L"%s", res_narrow);
- return out.c_str();
-
+ return val->c_str(); //looks dangerous but is safe, since the string is stored in the map
}
int wmkdir(const wcstring &name, int mode)
@@ -525,3 +520,97 @@ int fish_wcstoi(const wchar_t *str, wchar_t ** endptr, int base)
}
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;
+}