2015-09-04 17:25:17 +00:00
|
|
|
/* Filesystem API for OctaSTD. Currently POSIX only.
|
|
|
|
*
|
|
|
|
* This file is part of OctaSTD. See COPYING.md for futher information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef OSTD_FILESYSTEM_HH
|
|
|
|
#define OSTD_FILESYSTEM_HH
|
|
|
|
|
2015-09-06 15:07:14 +00:00
|
|
|
#include "ostd/platform.hh"
|
2016-01-27 19:34:45 +00:00
|
|
|
#include "ostd/internal/win32.hh"
|
2015-09-06 15:07:14 +00:00
|
|
|
|
2016-01-27 19:34:45 +00:00
|
|
|
#ifdef OSTD_PLATFORM_POSIX
|
2016-01-26 19:01:27 +00:00
|
|
|
#include <unistd.h>
|
2015-09-04 17:25:17 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
#include <sys/stat.h>
|
2016-07-06 17:31:21 +00:00
|
|
|
#else
|
|
|
|
#include <direct.h>
|
2015-09-06 15:07:14 +00:00
|
|
|
#endif
|
2015-09-04 17:25:17 +00:00
|
|
|
|
|
|
|
#include "ostd/types.hh"
|
|
|
|
#include "ostd/range.hh"
|
|
|
|
#include "ostd/vector.hh"
|
|
|
|
#include "ostd/string.hh"
|
|
|
|
#include "ostd/array.hh"
|
|
|
|
#include "ostd/algorithm.hh"
|
|
|
|
|
|
|
|
namespace ostd {
|
|
|
|
|
|
|
|
enum class FileType {
|
2015-09-04 17:55:48 +00:00
|
|
|
unknown, regular, fifo, character, directory, block, symlink, socket
|
2015-09-04 17:25:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct FileInfo;
|
|
|
|
|
|
|
|
#ifdef OSTD_PLATFORM_WIN32
|
2016-01-22 18:12:39 +00:00
|
|
|
static constexpr char PathSeparator = '\\';
|
2015-09-04 17:25:17 +00:00
|
|
|
#else
|
2016-01-22 18:12:39 +00:00
|
|
|
static constexpr char PathSeparator = '/';
|
2015-09-04 17:25:17 +00:00
|
|
|
#endif
|
|
|
|
|
2015-09-06 15:07:14 +00:00
|
|
|
#ifdef OSTD_PLATFORM_WIN32
|
|
|
|
namespace detail {
|
2016-06-23 18:18:35 +00:00
|
|
|
inline time_t filetime_to_time_t(FILETIME const &ft) {
|
2015-09-06 15:07:14 +00:00
|
|
|
ULARGE_INTEGER ul;
|
|
|
|
ul.LowPart = ft.dwLowDateTime;
|
|
|
|
ul.HighPart = ft.dwHighDateTime;
|
2016-07-06 17:31:21 +00:00
|
|
|
return static_cast<time_t>((ul.QuadPart / 10000000ULL) - 11644473600ULL);
|
2015-09-06 15:07:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-09-04 17:25:17 +00:00
|
|
|
inline void path_normalize(CharRange) {
|
|
|
|
/* TODO */
|
|
|
|
}
|
|
|
|
|
|
|
|
struct FileInfo {
|
|
|
|
FileInfo() {}
|
|
|
|
|
2016-06-23 18:18:35 +00:00
|
|
|
FileInfo(FileInfo const &i):
|
2015-09-04 17:25:17 +00:00
|
|
|
p_slash(i.p_slash), p_dot(i.p_dot), p_type(i.p_type),
|
|
|
|
p_path(i.p_path), p_atime(i.p_atime), p_mtime(i.p_mtime),
|
|
|
|
p_ctime(i.p_ctime) {}
|
|
|
|
|
|
|
|
FileInfo(FileInfo &&i):
|
|
|
|
p_slash(i.p_slash), p_dot(i.p_dot), p_type(i.p_type),
|
|
|
|
p_path(move(i.p_path)), p_atime(i.p_atime), p_mtime(i.p_mtime),
|
|
|
|
p_ctime(i.p_ctime) {
|
|
|
|
i.p_slash = i.p_dot = npos;
|
|
|
|
i.p_type = FileType::unknown;
|
|
|
|
i.p_atime = i.p_ctime = i.p_mtime = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
FileInfo(ConstCharRange path) {
|
|
|
|
init_from_str(path);
|
|
|
|
}
|
|
|
|
|
2016-06-23 18:18:35 +00:00
|
|
|
FileInfo &operator=(FileInfo const &i) {
|
2015-09-04 17:25:17 +00:00
|
|
|
p_slash = i.p_slash;
|
|
|
|
p_dot = i.p_dot;
|
|
|
|
p_type = i.p_type;
|
|
|
|
p_path = i.p_path;
|
|
|
|
p_atime = i.p_atime;
|
|
|
|
p_mtime = i.p_mtime;
|
|
|
|
p_ctime = i.p_ctime;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
FileInfo &operator=(FileInfo &&i) {
|
|
|
|
swap(i);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
ConstCharRange path() const { return p_path.iter(); }
|
|
|
|
|
|
|
|
ConstCharRange filename() const {
|
|
|
|
return path().slice((p_slash == npos) ? 0 : (p_slash + 1),
|
|
|
|
p_path.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
ConstCharRange stem() const {
|
|
|
|
return path().slice((p_slash == npos) ? 0 : (p_slash + 1),
|
|
|
|
(p_dot == npos) ? p_path.size() : p_dot);
|
|
|
|
}
|
|
|
|
|
|
|
|
ConstCharRange extension() const {
|
|
|
|
return (p_dot == npos) ? ConstCharRange()
|
|
|
|
: path().slice(p_dot, p_path.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
FileType type() const { return p_type; }
|
|
|
|
|
|
|
|
void normalize() {
|
|
|
|
path_normalize(p_path.iter());
|
|
|
|
init_from_str(p_path.iter());
|
|
|
|
}
|
|
|
|
|
|
|
|
time_t atime() const { return p_atime; }
|
|
|
|
time_t mtime() const { return p_mtime; }
|
|
|
|
time_t ctime() const { return p_ctime; }
|
|
|
|
|
|
|
|
void swap(FileInfo &i) {
|
|
|
|
detail::swap_adl(i.p_slash, p_slash);
|
|
|
|
detail::swap_adl(i.p_dot, p_dot);
|
|
|
|
detail::swap_adl(i.p_type, p_type);
|
|
|
|
detail::swap_adl(i.p_path, p_path);
|
|
|
|
detail::swap_adl(i.p_atime, p_atime);
|
|
|
|
detail::swap_adl(i.p_mtime, p_mtime);
|
|
|
|
detail::swap_adl(i.p_ctime, p_ctime);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void init_from_str(ConstCharRange path) {
|
2016-07-07 18:46:53 +00:00
|
|
|
/* TODO: implement a version that will follow symbolic links */
|
2016-07-07 16:06:57 +00:00
|
|
|
p_path = path;
|
2015-09-06 15:07:14 +00:00
|
|
|
#ifdef OSTD_PLATFORM_WIN32
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA attr;
|
2016-07-07 16:06:57 +00:00
|
|
|
if (!GetFileAttributesEx(p_path.data(), GetFileExInfoStandard, &attr) ||
|
2015-09-06 15:07:14 +00:00
|
|
|
attr.dwFileAttributes == INVALID_FILE_ATTRIBUTES)
|
|
|
|
#else
|
2015-09-04 17:25:17 +00:00
|
|
|
struct stat st;
|
2016-07-07 18:46:53 +00:00
|
|
|
if (lstat(p_path.data(), &st) < 0)
|
2015-09-06 15:07:14 +00:00
|
|
|
#endif
|
|
|
|
{
|
2015-09-04 17:25:17 +00:00
|
|
|
p_slash = p_dot = npos;
|
|
|
|
p_type = FileType::unknown;
|
|
|
|
p_path.clear();
|
|
|
|
p_atime = p_mtime = p_ctime = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ConstCharRange r = p_path.iter();
|
|
|
|
|
2016-01-22 18:12:39 +00:00
|
|
|
ConstCharRange found = find_last(r, PathSeparator);
|
2015-09-04 17:25:17 +00:00
|
|
|
if (found.empty())
|
|
|
|
p_slash = npos;
|
|
|
|
else
|
|
|
|
p_slash = r.distance_front(found);
|
|
|
|
|
|
|
|
found = find(filename(), '.');
|
|
|
|
if (found.empty())
|
|
|
|
p_dot = npos;
|
|
|
|
else
|
|
|
|
p_dot = r.distance_front(found);
|
|
|
|
|
2015-09-06 15:07:14 +00:00
|
|
|
#ifdef OSTD_PLATFORM_WIN32
|
|
|
|
if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
|
|
p_type = FileType::directory;
|
|
|
|
else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
|
|
|
|
p_type = FileType::symlink;
|
|
|
|
else if (attr.dwFileAttributes & (FILE_ATTRIBUTE_ARCHIVE |
|
|
|
|
FILE_ATTRIBUTE_COMPRESSED |
|
|
|
|
FILE_ATTRIBUTE_COMPRESSED |
|
|
|
|
FILE_ATTRIBUTE_HIDDEN |
|
|
|
|
FILE_ATTRIBUTE_NORMAL |
|
|
|
|
FILE_ATTRIBUTE_SPARSE_FILE |
|
|
|
|
FILE_ATTRIBUTE_TEMPORARY))
|
|
|
|
p_type = FileType::regular;
|
|
|
|
else
|
|
|
|
p_type = FileType::unknown;
|
|
|
|
|
|
|
|
p_atime = detail::filetime_to_time_t(attr.ftLastAccessTime);
|
|
|
|
p_mtime = detail::filetime_to_time_t(attr.ftLastWriteTime);
|
|
|
|
p_ctime = detail::filetime_to_time_t(attr.ftCreationTime);
|
|
|
|
#else
|
2015-09-04 17:25:17 +00:00
|
|
|
if (S_ISREG(st.st_mode))
|
2015-09-04 17:55:48 +00:00
|
|
|
p_type = FileType::regular;
|
2015-09-04 17:25:17 +00:00
|
|
|
else if (S_ISDIR(st.st_mode))
|
2015-09-04 17:55:48 +00:00
|
|
|
p_type = FileType::directory;
|
2015-09-04 17:25:17 +00:00
|
|
|
else if (S_ISCHR(st.st_mode))
|
2015-09-04 17:55:48 +00:00
|
|
|
p_type = FileType::character;
|
2015-09-04 17:25:17 +00:00
|
|
|
else if (S_ISBLK(st.st_mode))
|
2015-09-04 17:55:48 +00:00
|
|
|
p_type = FileType::block;
|
2015-09-04 17:25:17 +00:00
|
|
|
else if (S_ISFIFO(st.st_mode))
|
|
|
|
p_type = FileType::fifo;
|
|
|
|
else if (S_ISLNK(st.st_mode))
|
2015-09-04 17:55:48 +00:00
|
|
|
p_type = FileType::symlink;
|
2015-09-04 17:25:17 +00:00
|
|
|
else if (S_ISSOCK(st.st_mode))
|
2015-09-04 17:55:48 +00:00
|
|
|
p_type = FileType::socket;
|
2015-09-04 17:25:17 +00:00
|
|
|
else
|
|
|
|
p_type = FileType::unknown;
|
|
|
|
|
|
|
|
p_atime = st.st_atime;
|
|
|
|
p_mtime = st.st_mtime;
|
|
|
|
p_ctime = st.st_ctime;
|
2015-09-06 15:07:14 +00:00
|
|
|
#endif
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Size p_slash = npos, p_dot = npos;
|
|
|
|
FileType p_type = FileType::unknown;
|
|
|
|
String p_path;
|
|
|
|
|
|
|
|
time_t p_atime = 0, p_mtime = 0, p_ctime = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DirectoryRange;
|
|
|
|
|
2015-09-08 00:03:56 +00:00
|
|
|
#ifndef OSTD_PLATFORM_WIN32
|
2015-09-04 17:25:17 +00:00
|
|
|
struct DirectoryStream {
|
2015-09-05 02:35:07 +00:00
|
|
|
friend struct DirectoryRange;
|
|
|
|
|
2015-09-06 15:36:11 +00:00
|
|
|
DirectoryStream(): p_d(), p_de(), p_dev(), p_path() {}
|
2016-06-23 18:18:35 +00:00
|
|
|
DirectoryStream(DirectoryStream const &) = delete;
|
2015-09-06 15:36:11 +00:00
|
|
|
DirectoryStream(DirectoryStream &&s): p_d(s.p_d), p_de(s.p_de),
|
|
|
|
p_dev(s.p_dev),
|
|
|
|
p_path(move(s.p_path)) {
|
2015-09-04 17:25:17 +00:00
|
|
|
s.p_d = nullptr;
|
2015-09-06 15:36:11 +00:00
|
|
|
s.p_de = nullptr;
|
|
|
|
memset(&s.p_dev, 0, sizeof(s.p_dev));
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
2016-07-06 17:31:21 +00:00
|
|
|
DirectoryStream(ConstCharRange path): DirectoryStream() {
|
2015-09-04 17:25:17 +00:00
|
|
|
open(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
~DirectoryStream() { close(); }
|
|
|
|
|
2016-06-23 18:18:35 +00:00
|
|
|
DirectoryStream &operator=(DirectoryStream const &) = delete;
|
2015-09-04 17:25:17 +00:00
|
|
|
DirectoryStream &operator=(DirectoryStream &&s) {
|
|
|
|
close();
|
|
|
|
swap(s);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool open(ConstCharRange path) {
|
|
|
|
if (p_d || (path.size() > FILENAME_MAX)) return false;
|
|
|
|
char buf[FILENAME_MAX + 1];
|
|
|
|
memcpy(buf, &path[0], path.size());
|
|
|
|
buf[path.size()] = '\0';
|
|
|
|
p_d = opendir(buf);
|
2016-06-28 18:52:37 +00:00
|
|
|
if (!pop_front()) {
|
2015-09-07 23:49:50 +00:00
|
|
|
close();
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-04 17:46:28 +00:00
|
|
|
p_path = path;
|
2015-09-07 23:49:50 +00:00
|
|
|
return true;
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool is_open() const { return p_d != nullptr; }
|
|
|
|
|
|
|
|
void close() {
|
2015-09-06 15:20:51 +00:00
|
|
|
if (p_d) closedir(p_d);
|
2015-09-04 17:25:17 +00:00
|
|
|
p_d = nullptr;
|
2015-09-06 15:36:11 +00:00
|
|
|
p_de = nullptr;
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
2015-09-05 02:35:07 +00:00
|
|
|
long size() const {
|
2015-09-04 17:25:17 +00:00
|
|
|
if (!p_d) return -1;
|
2015-09-06 15:36:11 +00:00
|
|
|
DIR *td = opendir(p_path.data());
|
|
|
|
if (!td) return -1;
|
2015-09-05 02:35:07 +00:00
|
|
|
long ret = 0;
|
2015-09-06 15:36:11 +00:00
|
|
|
struct dirent rdv;
|
|
|
|
struct dirent *rd;
|
2015-09-06 16:39:09 +00:00
|
|
|
while (pop_front(td, &rdv, &rd))
|
2015-09-07 23:49:50 +00:00
|
|
|
ret += strcmp(rd->d_name, ".") && strcmp(rd->d_name, "..");
|
2015-09-06 15:36:11 +00:00
|
|
|
closedir(td);
|
2015-09-05 02:35:07 +00:00
|
|
|
return ret;
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool rewind() {
|
|
|
|
if (!p_d) return false;
|
|
|
|
rewinddir(p_d);
|
2016-06-28 18:52:37 +00:00
|
|
|
if (!pop_front()) {
|
2015-09-08 00:17:28 +00:00
|
|
|
close();
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-04 17:25:17 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-09-07 18:34:55 +00:00
|
|
|
bool empty() const {
|
|
|
|
return !p_de;
|
|
|
|
}
|
|
|
|
|
2015-09-04 17:25:17 +00:00
|
|
|
FileInfo read() {
|
2015-09-06 15:36:11 +00:00
|
|
|
if (!pop_front())
|
|
|
|
return FileInfo();
|
|
|
|
return front();
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void swap(DirectoryStream &s) {
|
|
|
|
detail::swap_adl(p_d, s.p_d);
|
2015-09-06 15:36:11 +00:00
|
|
|
detail::swap_adl(p_de, s.p_de);
|
2015-09-04 17:46:28 +00:00
|
|
|
detail::swap_adl(p_path, s.p_path);
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DirectoryRange iter();
|
|
|
|
|
|
|
|
private:
|
2015-09-06 16:39:09 +00:00
|
|
|
static bool pop_front(DIR *d, struct dirent *dev, struct dirent **de) {
|
|
|
|
if (!d) return false;
|
|
|
|
if (readdir_r(d, dev, de))
|
2015-09-06 15:36:11 +00:00
|
|
|
return false;
|
2016-06-28 18:52:37 +00:00
|
|
|
/* order of . and .. in the stream is not guaranteed, apparently...
|
|
|
|
* gotta check every time because of that
|
|
|
|
*/
|
|
|
|
while (*de && (!strcmp((*de)->d_name, ".") ||
|
|
|
|
!strcmp((*de)->d_name, ".."))) {
|
|
|
|
if (readdir_r(d, dev, de))
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-06 16:39:09 +00:00
|
|
|
return !!*de;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool pop_front() {
|
|
|
|
return pop_front(p_d, &p_dev, &p_de);
|
2015-09-05 02:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-09-06 15:36:11 +00:00
|
|
|
FileInfo front() const {
|
|
|
|
if (!p_de)
|
|
|
|
return FileInfo();
|
|
|
|
String ap = p_path;
|
2016-01-22 18:12:39 +00:00
|
|
|
ap += PathSeparator;
|
2016-07-02 03:56:23 +00:00
|
|
|
ap += static_cast<char const *>(p_de->d_name);
|
2015-09-06 15:36:11 +00:00
|
|
|
return FileInfo(ap);
|
2015-09-05 02:35:07 +00:00
|
|
|
}
|
|
|
|
|
2015-09-04 17:25:17 +00:00
|
|
|
DIR *p_d;
|
2015-09-06 15:36:11 +00:00
|
|
|
struct dirent *p_de;
|
|
|
|
struct dirent p_dev;
|
2015-09-04 17:46:28 +00:00
|
|
|
String p_path;
|
2015-09-04 17:25:17 +00:00
|
|
|
};
|
|
|
|
|
2015-09-08 00:03:56 +00:00
|
|
|
#else /* OSTD_PLATFORM_WIN32 */
|
|
|
|
|
|
|
|
struct DirectoryStream {
|
|
|
|
friend struct DirectoryRange;
|
|
|
|
|
|
|
|
DirectoryStream(): p_handle(INVALID_HANDLE_VALUE), p_data(), p_path() {}
|
2016-06-23 18:18:35 +00:00
|
|
|
DirectoryStream(DirectoryStream const &) = delete;
|
2015-09-08 00:03:56 +00:00
|
|
|
DirectoryStream(DirectoryStream &&s): p_handle(s.p_handle),
|
|
|
|
p_data(s.p_data),
|
|
|
|
p_path(move(s.p_path)) {
|
|
|
|
s.p_handle = INVALID_HANDLE_VALUE;
|
|
|
|
memset(&s.p_data, 0, sizeof(s.p_data));
|
|
|
|
}
|
|
|
|
|
2016-07-06 17:31:21 +00:00
|
|
|
DirectoryStream(ConstCharRange path): DirectoryStream() {
|
2015-09-08 00:03:56 +00:00
|
|
|
open(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
~DirectoryStream() { close(); }
|
|
|
|
|
2016-06-23 18:18:35 +00:00
|
|
|
DirectoryStream &operator=(DirectoryStream const &) = delete;
|
2015-09-08 00:03:56 +00:00
|
|
|
DirectoryStream &operator=(DirectoryStream &&s) {
|
|
|
|
close();
|
|
|
|
swap(s);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool open(ConstCharRange path) {
|
2016-07-06 17:51:39 +00:00
|
|
|
if (p_handle != INVALID_HANDLE_VALUE)
|
|
|
|
return false;
|
|
|
|
if ((path.size() >= 1024) || !path.size())
|
|
|
|
return false;
|
|
|
|
char buf[1026];
|
2015-09-08 00:03:56 +00:00
|
|
|
memcpy(buf, &path[0], path.size());
|
2016-07-06 17:51:39 +00:00
|
|
|
char *bptr = &buf[path.size()];
|
|
|
|
/* if path ends with a slash, replace it */
|
|
|
|
bptr -= ((*(bptr - 1) == '\\') || (*(bptr - 1) == '/'));
|
|
|
|
/* include trailing zero */
|
|
|
|
memcpy(bptr, "\\*", 3);
|
2015-09-08 00:03:56 +00:00
|
|
|
p_handle = FindFirstFile(buf, &p_data);
|
|
|
|
if (p_handle == INVALID_HANDLE_VALUE)
|
|
|
|
return false;
|
|
|
|
while (!strcmp(p_data.cFileName, ".") ||
|
2016-07-06 17:51:39 +00:00
|
|
|
!strcmp(p_data.cFileName, "..")) {
|
2015-09-08 00:03:56 +00:00
|
|
|
if (!FindNextFile(p_handle, &p_data)) {
|
|
|
|
FindClose(p_handle);
|
|
|
|
p_handle = INVALID_HANDLE_VALUE;
|
|
|
|
p_data.cFileName[0] = '\0';
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-06 17:51:39 +00:00
|
|
|
}
|
2015-09-08 00:03:56 +00:00
|
|
|
p_path = path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_open() const { return p_handle != INVALID_HANDLE_VALUE; }
|
|
|
|
|
|
|
|
void close() {
|
|
|
|
if (p_handle != INVALID_HANDLE_VALUE)
|
|
|
|
FindClose(p_handle);
|
|
|
|
p_handle = INVALID_HANDLE_VALUE;
|
|
|
|
p_data.cFileName[0] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
long size() const {
|
|
|
|
if (p_handle == INVALID_HANDLE_VALUE)
|
|
|
|
return -1;
|
|
|
|
WIN32_FIND_DATA wfd;
|
2016-07-06 17:31:21 +00:00
|
|
|
HANDLE td = FindFirstFile(p_path.data(), &wfd);
|
2015-09-08 00:03:56 +00:00
|
|
|
if (td == INVALID_HANDLE_VALUE)
|
|
|
|
return -1;
|
|
|
|
while (!strcmp(wfd.cFileName, ".") && !strcmp(wfd.cFileName, ".."))
|
|
|
|
if (!FindNextFile(td, &wfd)) {
|
|
|
|
FindClose(td);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
long ret = 1;
|
|
|
|
while (FindNextFile(td, &wfd))
|
|
|
|
++ret;
|
|
|
|
FindClose(td);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool rewind() {
|
|
|
|
if (p_handle != INVALID_HANDLE_VALUE)
|
|
|
|
FindClose(p_handle);
|
|
|
|
p_handle = FindFirstFile(p_path.data(), &p_data);
|
|
|
|
if (p_handle == INVALID_HANDLE_VALUE) {
|
|
|
|
p_data.cFileName[0] = '\0';
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
while (!strcmp(p_data.cFileName, ".") ||
|
|
|
|
!strcmp(p_data.cFileName, ".."))
|
|
|
|
if (!FindNextFile(p_handle, &p_data)) {
|
|
|
|
FindClose(p_handle);
|
|
|
|
p_handle = INVALID_HANDLE_VALUE;
|
|
|
|
p_data.cFileName[0] = '\0';
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool empty() const {
|
|
|
|
return p_data.cFileName[0] == '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
FileInfo read() {
|
|
|
|
if (!pop_front())
|
|
|
|
return FileInfo();
|
|
|
|
return front();
|
|
|
|
}
|
|
|
|
|
|
|
|
void swap(DirectoryStream &s) {
|
2016-07-06 17:31:21 +00:00
|
|
|
detail::swap_adl(p_handle, s.p_handle);
|
|
|
|
detail::swap_adl(p_data, s.p_data);
|
2015-09-08 00:03:56 +00:00
|
|
|
detail::swap_adl(p_path, s.p_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
DirectoryRange iter();
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool pop_front() {
|
|
|
|
if (!is_open())
|
|
|
|
return false;
|
|
|
|
if (!FindNextFile(p_handle, &p_data)) {
|
|
|
|
p_data.cFileName[0] = '\0';
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
FileInfo front() const {
|
|
|
|
if (empty())
|
|
|
|
return FileInfo();
|
|
|
|
String ap = p_path;
|
2016-01-22 18:12:39 +00:00
|
|
|
ap += PathSeparator;
|
2016-07-06 17:31:21 +00:00
|
|
|
ap += static_cast<char const *>(p_data.cFileName);
|
2015-09-08 00:03:56 +00:00
|
|
|
return FileInfo(ap);
|
|
|
|
}
|
|
|
|
|
2016-07-06 17:31:21 +00:00
|
|
|
HANDLE p_handle;
|
2015-09-08 00:03:56 +00:00
|
|
|
WIN32_FIND_DATA p_data;
|
|
|
|
String p_path;
|
|
|
|
};
|
|
|
|
#endif /* OSTD_PLATFORM_WIN32 */
|
|
|
|
|
2015-09-04 17:25:17 +00:00
|
|
|
struct DirectoryRange: InputRange<
|
2015-09-05 02:35:07 +00:00
|
|
|
DirectoryRange, InputRangeTag, FileInfo, FileInfo, Size, long
|
2015-09-04 17:25:17 +00:00
|
|
|
> {
|
|
|
|
DirectoryRange() = delete;
|
2015-09-06 15:36:11 +00:00
|
|
|
DirectoryRange(DirectoryStream &s): p_stream(&s) {}
|
2016-06-23 18:18:35 +00:00
|
|
|
DirectoryRange(DirectoryRange const &r): p_stream(r.p_stream) {}
|
2015-09-04 17:25:17 +00:00
|
|
|
|
2016-06-23 18:18:35 +00:00
|
|
|
DirectoryRange &operator=(DirectoryRange const &r) {
|
2015-09-05 02:35:07 +00:00
|
|
|
p_stream = r.p_stream;
|
2015-09-04 17:25:17 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool empty() const {
|
2015-09-06 15:36:11 +00:00
|
|
|
return p_stream->empty();
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool pop_front() {
|
2015-09-05 02:35:07 +00:00
|
|
|
return p_stream->pop_front();
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
2015-09-05 02:35:07 +00:00
|
|
|
FileInfo front() const {
|
2015-09-06 15:36:11 +00:00
|
|
|
return p_stream->front();
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
2016-06-23 18:18:35 +00:00
|
|
|
bool equals_front(DirectoryRange const &s) const {
|
2015-09-06 15:36:11 +00:00
|
|
|
return p_stream == s.p_stream;
|
2015-09-04 17:25:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
DirectoryStream *p_stream;
|
|
|
|
};
|
|
|
|
|
2015-09-10 17:54:47 +00:00
|
|
|
inline DirectoryRange DirectoryStream::iter() {
|
2015-09-04 17:25:17 +00:00
|
|
|
return DirectoryRange(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
template<Size I> struct PathJoin {
|
|
|
|
template<typename T, typename ...A>
|
2016-06-23 18:18:35 +00:00
|
|
|
static void join(String &s, T const &a, A const &...b) {
|
2015-09-04 17:25:17 +00:00
|
|
|
s += a;
|
2016-01-22 18:12:39 +00:00
|
|
|
s += PathSeparator;
|
2015-09-04 17:25:17 +00:00
|
|
|
PathJoin<I - 1>::join(s, b...);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
template<> struct PathJoin<1> {
|
|
|
|
template<typename T>
|
2016-06-23 18:18:35 +00:00
|
|
|
static void join(String &s, T const &a) {
|
2015-09-04 17:25:17 +00:00
|
|
|
s += a;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename ...A>
|
2016-06-23 18:18:35 +00:00
|
|
|
inline FileInfo path_join(A const &...args) {
|
2015-09-04 17:25:17 +00:00
|
|
|
String path;
|
|
|
|
detail::PathJoin<sizeof...(A)>::join(path, args...);
|
|
|
|
path_normalize(path.iter());
|
|
|
|
return FileInfo(path);
|
|
|
|
}
|
|
|
|
|
2015-09-14 00:31:31 +00:00
|
|
|
inline bool directory_change(ConstCharRange path) {
|
|
|
|
char buf[1024];
|
|
|
|
if (path.size() >= 1024)
|
|
|
|
return false;
|
|
|
|
memcpy(buf, path.data(), path.size());
|
|
|
|
buf[path.size()] = '\0';
|
|
|
|
#ifndef OSTD_PLATFORM_WIN32
|
|
|
|
return !chdir(buf);
|
|
|
|
#else
|
|
|
|
return !_chdir(buf);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-09-04 17:25:17 +00:00
|
|
|
} /* namespace ostd */
|
|
|
|
|
2016-02-07 21:17:15 +00:00
|
|
|
#endif
|