├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── filesystem ├── fwd.h ├── path.h └── resolver.h └── path_demo.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | Makefile 4 | cmake_install.cmake 5 | path_demo 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(path) 3 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") 4 | # Enable C++11 mode on GCC / Clang 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 6 | endif() 7 | add_executable(path_demo path_demo.cpp filesystem/path.h filesystem/resolver.h) 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Wenzel Jakob , All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | You are under no obligation whatsoever to provide any bug fixes, patches, or 29 | upgrades to the features, functionality or performance of the source code 30 | ("Enhancements") to anyone; however, if you choose to make your Enhancements 31 | available either publicly, or directly to the author of this software, without 32 | imposing a separate written license agreement for such Enhancements, then you 33 | hereby grant the following license: a non-exclusive, royalty-free perpetual 34 | license to install, use, modify, prepare derivative works, incorporate into 35 | other computer software, distribute, and sublicense such enhancements or 36 | derivative works thereof, in binary and source code form. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### filesystem/path.h: A simple class for manipulating paths on Linux/Windows/Mac OS 2 | 3 | This class is just a temporary workaround to avoid the heavy boost dependency 4 | until `boost::filesystem` is integrated into the standard template library at 5 | some point in the future. 6 | -------------------------------------------------------------------------------- /filesystem/fwd.h: -------------------------------------------------------------------------------- 1 | /* 2 | fwd.h -- Forward declarations for path.h and resolver.h 3 | 4 | Copyright (c) 2015 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #if !defined(NAMESPACE_BEGIN) 13 | #define NAMESPACE_BEGIN(name) namespace name { 14 | #endif 15 | #if !defined(NAMESPACE_END) 16 | #define NAMESPACE_END(name) } 17 | #endif 18 | 19 | NAMESPACE_BEGIN(filesystem) 20 | 21 | class path; 22 | class resolver; 23 | 24 | NAMESPACE_END(filesystem) 25 | -------------------------------------------------------------------------------- /filesystem/path.h: -------------------------------------------------------------------------------- 1 | /* 2 | path.h -- A simple class for manipulating paths on Linux/Windows/Mac OS 3 | 4 | Copyright (c) 2015 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "fwd.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #if defined(_WIN32) 24 | # include 25 | # include 26 | #else 27 | # include 28 | #endif 29 | #include 30 | 31 | #if defined(__linux) 32 | # include 33 | #endif 34 | 35 | NAMESPACE_BEGIN(filesystem) 36 | 37 | /** 38 | * \brief Simple class for manipulating paths on Linux/Windows/Mac OS 39 | * 40 | * This class is just a temporary workaround to avoid the heavy boost 41 | * dependency until boost::filesystem is integrated into the standard template 42 | * library at some point in the future. 43 | */ 44 | class path { 45 | public: 46 | enum path_type { 47 | windows_path = 0, 48 | posix_path = 1, 49 | #if defined(_WIN32) 50 | native_path = windows_path 51 | #else 52 | native_path = posix_path 53 | #endif 54 | }; 55 | 56 | path() : m_type(native_path), m_absolute(false), m_smb(false) { } 57 | 58 | path(const path &path) 59 | : m_type(path.m_type), m_path(path.m_path), m_absolute(path.m_absolute), m_smb(path.m_smb) {} 60 | 61 | path(path &&path) 62 | : m_type(path.m_type), m_path(std::move(path.m_path)), 63 | m_absolute(path.m_absolute), m_smb(path.m_smb) {} 64 | 65 | path(const char *string) : m_smb(false) { set(string); } 66 | 67 | path(const std::string &string) : m_smb(false) { set(string); } 68 | 69 | #if defined(_WIN32) 70 | path(const std::wstring &wstring) { set(wstring); } 71 | path(const wchar_t *wstring) { set(wstring); } 72 | #endif 73 | 74 | size_t length() const { return m_path.size(); } 75 | 76 | bool empty() const { return m_path.empty(); } 77 | 78 | bool is_absolute() const { return m_absolute; } 79 | 80 | path make_absolute() const { 81 | #if !defined(_WIN32) 82 | char temp[PATH_MAX]; 83 | if (realpath(str().c_str(), temp) == NULL) 84 | throw std::runtime_error("Internal error in realpath(): " + std::string(strerror(errno))); 85 | return path(temp); 86 | #else 87 | std::wstring value = wstr(), out(MAX_PATH_WINDOWS, '\0'); 88 | DWORD length = GetFullPathNameW(value.c_str(), MAX_PATH_WINDOWS, &out[0], NULL); 89 | if (length == 0) 90 | throw std::runtime_error("Internal error in realpath(): " + std::to_string(GetLastError())); 91 | return path(out.substr(0, length)); 92 | #endif 93 | } 94 | 95 | bool exists() const { 96 | #if defined(_WIN32) 97 | return GetFileAttributesW(wstr().c_str()) != INVALID_FILE_ATTRIBUTES; 98 | #else 99 | struct stat sb; 100 | return stat(str().c_str(), &sb) == 0; 101 | #endif 102 | } 103 | 104 | size_t file_size() const { 105 | #if defined(_WIN32) 106 | struct _stati64 sb; 107 | if (_wstati64(wstr().c_str(), &sb) != 0) 108 | throw std::runtime_error("path::file_size(): cannot stat file \"" + str() + "\"!"); 109 | #else 110 | struct stat sb; 111 | if (stat(str().c_str(), &sb) != 0) 112 | throw std::runtime_error("path::file_size(): cannot stat file \"" + str() + "\"!"); 113 | #endif 114 | return (size_t) sb.st_size; 115 | } 116 | 117 | bool is_directory() const { 118 | #if defined(_WIN32) 119 | DWORD result = GetFileAttributesW(wstr().c_str()); 120 | if (result == INVALID_FILE_ATTRIBUTES) 121 | return false; 122 | return (result & FILE_ATTRIBUTE_DIRECTORY) != 0; 123 | #else 124 | struct stat sb; 125 | if (stat(str().c_str(), &sb)) 126 | return false; 127 | return S_ISDIR(sb.st_mode); 128 | #endif 129 | } 130 | 131 | bool is_file() const { 132 | #if defined(_WIN32) 133 | DWORD attr = GetFileAttributesW(wstr().c_str()); 134 | return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0); 135 | #else 136 | struct stat sb; 137 | if (stat(str().c_str(), &sb)) 138 | return false; 139 | return S_ISREG(sb.st_mode); 140 | #endif 141 | } 142 | 143 | std::string extension() const { 144 | const std::string &name = filename(); 145 | size_t pos = name.find_last_of("."); 146 | if (pos == std::string::npos) 147 | return ""; 148 | return name.substr(pos+1); 149 | } 150 | 151 | std::string filename() const { 152 | if (empty()) 153 | return ""; 154 | const std::string &last = m_path[m_path.size()-1]; 155 | return last; 156 | } 157 | 158 | path parent_path() const { 159 | path result; 160 | result.m_absolute = m_absolute; 161 | result.m_smb = m_smb; 162 | 163 | if (m_path.empty()) { 164 | if (!m_absolute) 165 | result.m_path.push_back(".."); 166 | } else { 167 | size_t until = m_path.size() - 1; 168 | for (size_t i = 0; i < until; ++i) 169 | result.m_path.push_back(m_path[i]); 170 | } 171 | return result; 172 | } 173 | 174 | path operator/(const path &other) const { 175 | if (other.m_absolute) 176 | throw std::runtime_error("path::operator/(): expected a relative path!"); 177 | if (m_type != other.m_type) 178 | throw std::runtime_error("path::operator/(): expected a path of the same type!"); 179 | 180 | path result(*this); 181 | 182 | for (size_t i=0; iMAX_PATH are 204 | // not supported at all in Windows. 205 | if (length > MAX_PATH_WINDOWS_LEGACY) { 206 | if (m_smb) 207 | oss << "\\\\?\\UNC\\"; 208 | else 209 | oss << "\\\\?\\"; 210 | } else if (m_smb) 211 | oss << "\\\\"; 212 | } 213 | } 214 | 215 | for (size_t i=0; iMAX_PATH characters long, so we remove it 235 | // for convenience and add it back (if necessary) in str()/wstr(). 236 | static const std::string LONG_PATH_PREFIX = "\\\\?\\"; 237 | if (tmp.length() >= LONG_PATH_PREFIX.length() 238 | && std::mismatch(std::begin(LONG_PATH_PREFIX), std::end(LONG_PATH_PREFIX), std::begin(tmp)).first == std::end(LONG_PATH_PREFIX)) { 239 | tmp.erase(0, LONG_PATH_PREFIX.length()); 240 | } 241 | 242 | // Special-case handling of absolute SMB paths, which start with the prefix "\\". 243 | if (tmp.length() >= 2 && tmp[0] == '\\' && tmp[1] == '\\') { 244 | m_path = {}; 245 | tmp.erase(0, 2); 246 | 247 | // Interestingly, there is a special-special case where relative paths may be specified as beginning with a "\\" 248 | // when a non-SMB file with a more-than-260-characters-long absolute _local_ path is double-clicked. This seems to 249 | // only happen with single-segment relative paths, so we can check for this condition by making sure no further 250 | // path separators are present. 251 | if (tmp.find_first_of("/\\") != std::string::npos) 252 | m_absolute = m_smb = true; 253 | else 254 | m_absolute = m_smb = false; 255 | 256 | // Special-case handling of absolute SMB paths, which start with the prefix "UNC\" 257 | } else if (tmp.length() >= 4 && tmp[0] == 'U' && tmp[1] == 'N' && tmp[2] == 'C' && tmp[3] == '\\') { 258 | m_path = {}; 259 | tmp.erase(0, 4); 260 | m_absolute = true; 261 | m_smb = true; 262 | // Special-case handling of absolute local paths, which start with the drive letter and a colon "X:\" 263 | // So that UTF-8 works, do not call std::isalpha if the high bit is set, as that causes an assert on Windows. 264 | } else if (tmp.length() >= 3 && ((unsigned char)tmp[0] < 0x80) && std::isalpha(tmp[0]) && 265 | tmp[1] == ':' && (tmp[2] == '\\' || tmp[2] == '/')) { 266 | m_path = {tmp.substr(0, 2)}; 267 | tmp.erase(0, 3); 268 | m_absolute = true; 269 | m_smb = false; 270 | // Relative path 271 | } else { 272 | m_path = {}; 273 | m_absolute = false; 274 | m_smb = false; 275 | } 276 | 277 | std::vector tokenized = tokenize(tmp, "/\\"); 278 | m_path.insert(std::end(m_path), std::begin(tokenized), std::end(tokenized)); 279 | } else { 280 | m_path = tokenize(str, "/"); 281 | m_absolute = !str.empty() && str[0] == '/'; 282 | } 283 | } 284 | 285 | path &operator=(const path &path) { 286 | m_type = path.m_type; 287 | m_path = path.m_path; 288 | m_absolute = path.m_absolute; 289 | m_smb = path.m_smb; 290 | return *this; 291 | } 292 | 293 | path &operator=(path &&path) { 294 | if (this != &path) { 295 | m_type = path.m_type; 296 | m_path = std::move(path.m_path); 297 | m_absolute = path.m_absolute; 298 | m_smb = path.m_smb; 299 | } 300 | return *this; 301 | } 302 | 303 | friend std::ostream &operator<<(std::ostream &os, const path &path) { 304 | os << path.str(); 305 | return os; 306 | } 307 | 308 | bool remove_file() { 309 | #if !defined(_WIN32) 310 | return std::remove(str().c_str()) == 0; 311 | #else 312 | return DeleteFileW(wstr().c_str()) != 0; 313 | #endif 314 | } 315 | 316 | bool resize_file(size_t target_length) { 317 | #if !defined(_WIN32) 318 | return ::truncate(str().c_str(), (off_t) target_length) == 0; 319 | #else 320 | HANDLE handle = CreateFileW(wstr().c_str(), GENERIC_WRITE, 0, nullptr, 0, FILE_ATTRIBUTE_NORMAL, nullptr); 321 | if (handle == INVALID_HANDLE_VALUE) 322 | return false; 323 | LARGE_INTEGER size; 324 | size.QuadPart = (LONGLONG) target_length; 325 | if (SetFilePointerEx(handle, size, NULL, FILE_BEGIN) == 0) { 326 | CloseHandle(handle); 327 | return false; 328 | } 329 | if (SetEndOfFile(handle) == 0) { 330 | CloseHandle(handle); 331 | return false; 332 | } 333 | CloseHandle(handle); 334 | return true; 335 | #endif 336 | } 337 | 338 | static path getcwd() { 339 | #if !defined(_WIN32) 340 | char temp[PATH_MAX]; 341 | if (::getcwd(temp, PATH_MAX) == NULL) 342 | throw std::runtime_error("Internal error in getcwd(): " + std::string(strerror(errno))); 343 | return path(temp); 344 | #else 345 | std::wstring temp(MAX_PATH_WINDOWS, '\0'); 346 | if (!_wgetcwd(&temp[0], MAX_PATH_WINDOWS)) 347 | throw std::runtime_error("Internal error in getcwd(): " + std::to_string(GetLastError())); 348 | return path(temp.c_str()); 349 | #endif 350 | } 351 | 352 | #if defined(_WIN32) 353 | std::wstring wstr(path_type type = native_path) const { 354 | std::string temp = str(type); 355 | int size = MultiByteToWideChar(CP_UTF8, 0, &temp[0], (int)temp.size(), NULL, 0); 356 | std::wstring result(size, 0); 357 | MultiByteToWideChar(CP_UTF8, 0, &temp[0], (int)temp.size(), &result[0], size); 358 | return result; 359 | } 360 | 361 | 362 | void set(const std::wstring &wstring, path_type type = native_path) { 363 | std::string string; 364 | if (!wstring.empty()) { 365 | int size = WideCharToMultiByte(CP_UTF8, 0, &wstring[0], (int)wstring.size(), 366 | NULL, 0, NULL, NULL); 367 | string.resize(size, 0); 368 | WideCharToMultiByte(CP_UTF8, 0, &wstring[0], (int)wstring.size(), 369 | &string[0], size, NULL, NULL); 370 | } 371 | set(string, type); 372 | } 373 | 374 | path &operator=(const std::wstring &str) { set(str); return *this; } 375 | #endif 376 | 377 | bool operator==(const path &p) const { return p.m_path == m_path; } 378 | bool operator!=(const path &p) const { return p.m_path != m_path; } 379 | 380 | protected: 381 | static std::vector tokenize(const std::string &string, const std::string &delim) { 382 | std::string::size_type lastPos = 0, pos = string.find_first_of(delim, lastPos); 383 | std::vector tokens; 384 | 385 | while (lastPos != std::string::npos) { 386 | if (pos != lastPos) 387 | tokens.push_back(string.substr(lastPos, pos - lastPos)); 388 | lastPos = pos; 389 | if (lastPos == std::string::npos || lastPos + 1 == string.length()) 390 | break; 391 | pos = string.find_first_of(delim, ++lastPos); 392 | } 393 | 394 | return tokens; 395 | } 396 | 397 | protected: 398 | #if defined(_WIN32) 399 | static const size_t MAX_PATH_WINDOWS = 32767; 400 | #endif 401 | static const size_t MAX_PATH_WINDOWS_LEGACY = 260; 402 | path_type m_type; 403 | std::vector m_path; 404 | bool m_absolute; 405 | bool m_smb; // Unused, except for on Windows 406 | }; 407 | 408 | inline bool create_directory(const path& p) { 409 | #if defined(_WIN32) 410 | return CreateDirectoryW(p.wstr().c_str(), NULL) != 0; 411 | #else 412 | return mkdir(p.str().c_str(), S_IRWXU) == 0; 413 | #endif 414 | } 415 | 416 | inline bool create_directories(const path& p) { 417 | #if defined(_WIN32) 418 | return SHCreateDirectory(nullptr, p.make_absolute().wstr().c_str()) == ERROR_SUCCESS; 419 | #else 420 | if (create_directory(p.str().c_str())) 421 | return true; 422 | 423 | if (p.empty()) 424 | return false; 425 | 426 | if (errno == ENOENT) { 427 | if (create_directory(p.parent_path())) 428 | return mkdir(p.str().c_str(), S_IRWXU) == 0; 429 | else 430 | return false; 431 | } 432 | return false; 433 | #endif 434 | } 435 | 436 | NAMESPACE_END(filesystem) 437 | -------------------------------------------------------------------------------- /filesystem/resolver.h: -------------------------------------------------------------------------------- 1 | /* 2 | resolver.h -- A simple class for cross-platform path resolution 3 | 4 | Copyright (c) 2015 Wenzel Jakob 5 | 6 | All rights reserved. Use of this source code is governed by a 7 | BSD-style license that can be found in the LICENSE file. 8 | */ 9 | 10 | #pragma once 11 | 12 | #include "path.h" 13 | 14 | NAMESPACE_BEGIN(filesystem) 15 | 16 | /** 17 | * \brief Simple class for resolving paths on Linux/Windows/Mac OS 18 | * 19 | * This convenience class looks for a file or directory given its name 20 | * and a set of search paths. The implementation walks through the 21 | * search paths in order and stops once the file is found. 22 | */ 23 | class resolver { 24 | public: 25 | typedef std::vector::iterator iterator; 26 | typedef std::vector::const_iterator const_iterator; 27 | 28 | resolver() { 29 | m_paths.push_back(path::getcwd()); 30 | } 31 | 32 | size_t size() const { return m_paths.size(); } 33 | 34 | iterator begin() { return m_paths.begin(); } 35 | iterator end() { return m_paths.end(); } 36 | 37 | const_iterator begin() const { return m_paths.begin(); } 38 | const_iterator end() const { return m_paths.end(); } 39 | 40 | void erase(iterator it) { m_paths.erase(it); } 41 | 42 | void prepend(const path &path) { m_paths.insert(m_paths.begin(), path); } 43 | void append(const path &path) { m_paths.push_back(path); } 44 | const path &operator[](size_t index) const { return m_paths[index]; } 45 | path &operator[](size_t index) { return m_paths[index]; } 46 | 47 | path resolve(const path &value) const { 48 | for (const_iterator it = m_paths.begin(); it != m_paths.end(); ++it) { 49 | path combined = *it / value; 50 | if (combined.exists()) 51 | return combined; 52 | } 53 | return value; 54 | } 55 | 56 | friend std::ostream &operator<<(std::ostream &os, const resolver &r) { 57 | os << "resolver[" << std::endl; 58 | for (size_t i = 0; i < r.m_paths.size(); ++i) { 59 | os << " \"" << r.m_paths[i] << "\""; 60 | if (i + 1 < r.m_paths.size()) 61 | os << ","; 62 | os << std::endl; 63 | } 64 | os << "]"; 65 | return os; 66 | } 67 | 68 | private: 69 | std::vector m_paths; 70 | }; 71 | 72 | NAMESPACE_END(filesystem) 73 | -------------------------------------------------------------------------------- /path_demo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "filesystem/path.h" 3 | #include "filesystem/resolver.h" 4 | 5 | using namespace std; 6 | using namespace filesystem; 7 | 8 | int main(int argc, char **argv) { 9 | #if !defined(WIN32) 10 | path path1("/dir 1/dir 2/"); 11 | #else 12 | path path1("C:\\dir 1\\dir 2\\"); 13 | #endif 14 | path path2("dir 3"); 15 | 16 | cout << path1.exists() << endl; 17 | cout << path1 << endl; 18 | cout << (path1/path2) << endl; 19 | cout << (path1/path2).parent_path() << endl; 20 | cout << (path1/path2).parent_path().parent_path() << endl; 21 | cout << (path1/path2).parent_path().parent_path().parent_path() << endl; 22 | cout << (path1/path2).parent_path().parent_path().parent_path().parent_path() << endl; 23 | cout << path().parent_path() << endl; 24 | cout << "some/path.ext:operator==() = " << (path("some/path.ext") == path("some/path.ext")) << endl; 25 | cout << "some/path.ext:operator==() (unequal) = " << (path("some/path.ext") == path("another/path.ext")) << endl; 26 | 27 | cout << "nonexistant:exists = " << path("nonexistant").exists() << endl; 28 | cout << "nonexistant:is_file = " << path("nonexistant").is_file() << endl; 29 | cout << "nonexistant:is_directory = " << path("nonexistant").is_directory() << endl; 30 | cout << "nonexistant:filename = " << path("nonexistant").filename() << endl; 31 | cout << "nonexistant:extension = " << path("nonexistant").extension() << endl; 32 | cout << "filesystem/path.h:exists = " << path("filesystem/path.h").exists() << endl; 33 | cout << "filesystem/path.h:is_file = " << path("filesystem/path.h").is_file() << endl; 34 | cout << "filesystem/path.h:is_directory = " << path("filesystem/path.h").is_directory() << endl; 35 | cout << "filesystem/path.h:filename = " << path("filesystem/path.h").filename() << endl; 36 | cout << "filesystem/path.h:extension = " << path("filesystem/path.h").extension() << endl; 37 | cout << "filesystem/path.h:make_absolute = " << path("filesystem/path.h").make_absolute() << endl; 38 | cout << "../filesystem:exists = " << path("../filesystem").exists() << endl; 39 | cout << "../filesystem:is_file = " << path("../filesystem").is_file() << endl; 40 | cout << "../filesystem:is_directory = " << path("../filesystem").is_directory() << endl; 41 | cout << "../filesystem:extension = " << path("../filesystem").extension() << endl; 42 | cout << "../filesystem:filename = " << path("../filesystem").filename() << endl; 43 | cout << "../filesystem:make_absolute = " << path("../filesystem").make_absolute() << endl; 44 | 45 | cout << "resolve(filesystem/path.h) = " << resolver().resolve("filesystem/path.h") << endl; 46 | cout << "resolve(nonexistant) = " << resolver().resolve("nonexistant") << endl; 47 | return 0; 48 | } 49 | --------------------------------------------------------------------------------