00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #pragma once
00011
00012 #include "fwd.h"
00013 #include <string>
00014 #include <vector>
00015 #include <stdexcept>
00016 #include <sstream>
00017 #include <cctype>
00018 #include <cstdlib>
00019 #include <cerrno>
00020 #include <cstring>
00021
00022 #if defined(_WIN32)
00023 # include <windows.h>
00024 #else
00025 # include <unistd.h>
00026 #endif
00027 #include <sys/stat.h>
00028
00029 #if defined(__linux)
00030 # include <linux/limits.h>
00031 #endif
00032
00033 NAMESPACE_BEGIN(filesystem)
00034
00035
00042 class path {
00043 public:
00044 enum path_type {
00045 windows_path = 0,
00046 posix_path = 1,
00047 #if defined(_WIN32)
00048 native_path = windows_path
00049 #else
00050 native_path = posix_path
00051 #endif
00052 };
00053
00054 path() : m_type(native_path), m_absolute(false) { }
00055
00056 path(const path &path)
00057 : m_type(path.m_type), m_path(path.m_path), m_absolute(path.m_absolute) {}
00058
00059 path(path &&path)
00060 : m_type(path.m_type), m_path(std::move(path.m_path)),
00061 m_absolute(path.m_absolute) {}
00062
00063 path(const char *string) { set(string); }
00064
00065 path(const std::string &string) { set(string); }
00066
00067 #if defined(_WIN32)
00068 path(const std::wstring &wstring) { set(wstring); }
00069 path(const wchar_t *wstring) { set(wstring); }
00070 #endif
00071
00072 size_t length() const { return m_path.size(); }
00073
00074 bool empty() const { return m_path.empty(); }
00075
00076 bool is_absolute() const { return m_absolute; }
00077
00078 path make_absolute() const {
00079 #if !defined(_WIN32)
00080 char temp[PATH_MAX];
00081 if (realpath(str().c_str(), temp) == NULL)
00082 throw std::runtime_error("Internal error in realpath(): " + std::string(strerror(errno)));
00083 return path(temp);
00084 #else
00085 std::wstring value = wstr(), out(MAX_PATH, '\0');
00086 DWORD length = GetFullPathNameW(value.c_str(), MAX_PATH, &out[0], NULL);
00087 if (length == 0)
00088 throw std::runtime_error("Internal error in realpath(): " + std::to_string(GetLastError()));
00089 return path(out.substr(0, length));
00090 #endif
00091 }
00092
00093 bool exists() const {
00094 #if defined(_WIN32)
00095 return GetFileAttributesW(wstr().c_str()) != INVALID_FILE_ATTRIBUTES;
00096 #else
00097 struct stat sb;
00098 return stat(str().c_str(), &sb) == 0;
00099 #endif
00100 }
00101
00102 size_t file_size() const {
00103 #if defined(_WIN32)
00104 struct _stati64 sb;
00105 if (_wstati64(wstr().c_str(), &sb) != 0)
00106 throw std::runtime_error("path::file_size(): cannot stat file \"" + str() + "\"!");
00107 #else
00108 struct stat sb;
00109 if (stat(str().c_str(), &sb) != 0)
00110 throw std::runtime_error("path::file_size(): cannot stat file \"" + str() + "\"!");
00111 #endif
00112 return (size_t) sb.st_size;
00113 }
00114
00115 bool is_directory() const {
00116 #if defined(_WIN32)
00117 DWORD result = GetFileAttributesW(wstr().c_str());
00118 if (result == INVALID_FILE_ATTRIBUTES)
00119 return false;
00120 return (result & FILE_ATTRIBUTE_DIRECTORY) != 0;
00121 #else
00122 struct stat sb;
00123 if (stat(str().c_str(), &sb))
00124 return false;
00125 return S_ISDIR(sb.st_mode);
00126 #endif
00127 }
00128
00129 bool is_file() const {
00130 #if defined(_WIN32)
00131 DWORD attr = GetFileAttributesW(wstr().c_str());
00132 return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0);
00133 #else
00134 struct stat sb;
00135 if (stat(str().c_str(), &sb))
00136 return false;
00137 return S_ISREG(sb.st_mode);
00138 #endif
00139 }
00140
00141 std::string extension() const {
00142 const std::string &name = filename();
00143 size_t pos = name.find_last_of(".");
00144 if (pos == std::string::npos)
00145 return "";
00146 return name.substr(pos+1);
00147 }
00148
00149 std::string filename() const {
00150 if (empty())
00151 return "";
00152 const std::string &last = m_path[m_path.size()-1];
00153 return last;
00154 }
00155
00156 path parent_path() const {
00157 path result;
00158 result.m_absolute = m_absolute;
00159
00160 if (m_path.empty()) {
00161 if (!m_absolute)
00162 result.m_path.push_back("..");
00163 } else {
00164 size_t until = m_path.size() - 1;
00165 for (size_t i = 0; i < until; ++i)
00166 result.m_path.push_back(m_path[i]);
00167 }
00168 return result;
00169 }
00170
00171 path operator/(const path &other) const {
00172 if (other.m_absolute)
00173 throw std::runtime_error("path::operator/(): expected a relative path!");
00174 if (m_type != other.m_type)
00175 throw std::runtime_error("path::operator/(): expected a path of the same type!");
00176
00177 path result(*this);
00178
00179 for (size_t i=0; i<other.m_path.size(); ++i)
00180 result.m_path.push_back(other.m_path[i]);
00181
00182 return result;
00183 }
00184
00185 std::string str(path_type type = native_path) const {
00186 std::ostringstream oss;
00187
00188 if (m_type == posix_path && m_absolute)
00189 oss << "/";
00190
00191 for (size_t i=0; i<m_path.size(); ++i) {
00192 oss << m_path[i];
00193 if (i+1 < m_path.size()) {
00194 if (type == posix_path)
00195 oss << '/';
00196 else
00197 oss << '\\';
00198 }
00199 }
00200
00201 return oss.str();
00202 }
00203
00204 void set(const std::string &str, path_type type = native_path) {
00205 m_type = type;
00206 if (type == windows_path) {
00207 m_path = tokenize(str, "/\\");
00208 m_absolute = str.size() >= 2 && std::isalpha(str[0]) && str[1] == ':';
00209 } else {
00210 m_path = tokenize(str, "/");
00211 m_absolute = !str.empty() && str[0] == '/';
00212 }
00213 }
00214
00215 path &operator=(const path &path) {
00216 m_type = path.m_type;
00217 m_path = path.m_path;
00218 m_absolute = path.m_absolute;
00219 return *this;
00220 }
00221
00222 path &operator=(path &&path) {
00223 if (this != &path) {
00224 m_type = path.m_type;
00225 m_path = std::move(path.m_path);
00226 m_absolute = path.m_absolute;
00227 }
00228 return *this;
00229 }
00230
00231 friend std::ostream &operator<<(std::ostream &os, const path &path) {
00232 os << path.str();
00233 return os;
00234 }
00235
00236 bool remove_file() {
00237 #if !defined(_WIN32)
00238 return std::remove(str().c_str()) == 0;
00239 #else
00240 return DeleteFileW(wstr().c_str()) != 0;
00241 #endif
00242 }
00243
00244 bool resize_file(size_t target_length) {
00245 #if !defined(_WIN32)
00246 return ::truncate(str().c_str(), (off_t) target_length) == 0;
00247 #else
00248 HANDLE handle = CreateFileW(wstr().c_str(), GENERIC_WRITE, 0, nullptr, 0, FILE_ATTRIBUTE_NORMAL, nullptr);
00249 if (handle == INVALID_HANDLE_VALUE)
00250 return false;
00251 LARGE_INTEGER size;
00252 size.QuadPart = (LONGLONG) target_length;
00253 if (SetFilePointerEx(handle, size, NULL, FILE_BEGIN) == 0) {
00254 CloseHandle(handle);
00255 return false;
00256 }
00257 if (SetEndOfFile(handle) == 0) {
00258 CloseHandle(handle);
00259 return false;
00260 }
00261 CloseHandle(handle);
00262 return true;
00263 #endif
00264 }
00265
00266 static path getcwd() {
00267 #if !defined(_WIN32)
00268 char temp[PATH_MAX];
00269 if (::getcwd(temp, PATH_MAX) == NULL)
00270 throw std::runtime_error("Internal error in getcwd(): " + std::string(strerror(errno)));
00271 return path(temp);
00272 #else
00273 std::wstring temp(MAX_PATH, '\0');
00274 if (!_wgetcwd(&temp[0], MAX_PATH))
00275 throw std::runtime_error("Internal error in getcwd(): " + std::to_string(GetLastError()));
00276 return path(temp.c_str());
00277 #endif
00278 }
00279
00280 #if defined(_WIN32)
00281 std::wstring wstr(path_type type = native_path) const {
00282 std::string temp = str(type);
00283 int size = MultiByteToWideChar(CP_UTF8, 0, &temp[0], (int)temp.size(), NULL, 0);
00284 std::wstring result(size, 0);
00285 MultiByteToWideChar(CP_UTF8, 0, &temp[0], (int)temp.size(), &result[0], size);
00286 return result;
00287 }
00288
00289
00290 void set(const std::wstring &wstring, path_type type = native_path) {
00291 std::string string;
00292 if (!wstring.empty()) {
00293 int size = WideCharToMultiByte(CP_UTF8, 0, &wstring[0], (int)wstring.size(),
00294 NULL, 0, NULL, NULL);
00295 string.resize(size, 0);
00296 WideCharToMultiByte(CP_UTF8, 0, &wstring[0], (int)wstring.size(),
00297 &string[0], size, NULL, NULL);
00298 }
00299 set(string, type);
00300 }
00301
00302 path &operator=(const std::wstring &str) { set(str); return *this; }
00303 #endif
00304
00305 bool operator==(const path &p) const { return p.m_path == m_path; }
00306 bool operator!=(const path &p) const { return p.m_path != m_path; }
00307
00308 protected:
00309 static std::vector<std::string> tokenize(const std::string &string, const std::string &delim) {
00310 std::string::size_type lastPos = 0, pos = string.find_first_of(delim, lastPos);
00311 std::vector<std::string> tokens;
00312
00313 while (lastPos != std::string::npos) {
00314 if (pos != lastPos)
00315 tokens.push_back(string.substr(lastPos, pos - lastPos));
00316 lastPos = pos;
00317 if (lastPos == std::string::npos || lastPos + 1 == string.length())
00318 break;
00319 pos = string.find_first_of(delim, ++lastPos);
00320 }
00321
00322 return tokens;
00323 }
00324
00325 protected:
00326 path_type m_type;
00327 std::vector<std::string> m_path;
00328 bool m_absolute;
00329 };
00330
00331 inline bool create_directory(const path& p) {
00332 #if defined(_WIN32)
00333 return CreateDirectoryW(p.wstr().c_str(), NULL) != 0;
00334 #else
00335 return mkdir(p.str().c_str(), S_IRUSR | S_IWUSR | S_IXUSR) == 0;
00336 #endif
00337 }
00338
00339 NAMESPACE_END(filesystem)