2 * Copyright 2017 Jacob Lifshay
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 #ifndef _LARGEFILE_SOURCE
25 #define _LARGEFILE_SOURCE
27 #ifndef _LARGEFILE64_SOURCE
28 #define _LARGEFILE64_SOURCE
31 #include "filesystem.h"
37 #elif defined(__linux__)
40 #include <sys/types.h>
45 #error filesystem is not implemented for your operating system
56 constexpr bool Filesystem_clock::is_steady
;
61 Filesystem_clock::time_point
timespec_to_time_point(const timespec
&ts
) noexcept
63 return Filesystem_clock::time_point(
64 Filesystem_clock::duration(static_cast<std::int64_t>(ts
.tv_sec
) * 1'000'000'000L
65 + static_cast<std::int64_t>(ts
.tv_nsec
)));
71 Filesystem_clock::time_point
filetime_to_time_point(const FILETIME
&ft
) noexcept
74 li
.u
.LowPart
= ft
.dwLowDateTime
;
75 li
.u
.HighPart
= ft
.dwHighDateTime
;
76 return Filesystem_clock::time_point(Filesystem_clock::duration(li
.QuadPart
));
81 Filesystem_clock::time_point
Filesystem_clock::now() noexcept
85 ::GetSystemTimeAsFileTime(&ft
);
86 return filetime_to_time_point(ft
);
87 #elif defined(__linux__)
89 ::clock_gettime(CLOCK_REALTIME
, &ts
);
90 return timespec_to_time_point(ts
);
92 #error Filesystem_clock::now is not implemented for your operating system
100 #error Stat_results is not implemented on windows
101 #elif defined(__linux__)
102 struct ::stat64 stat_results
;
103 constexpr Stat_results() noexcept
: type(file_type::none
), stat_results
{}
106 Stat_results(const path
&p
, bool follow_symlink
, std::error_code
&ec
)
107 : type(file_type::none
), stat_results
{}
110 int old_errno
= errno
;
112 follow_symlink
? stat64(p
.c_str(), &stat_results
) : lstat64(p
.c_str(), &stat_results
);
120 ec
= make_error_code(std::errc::no_such_file_or_directory
);
121 type
= file_type::not_found
;
124 ec
= std::error_code(errno
, std::generic_category());
125 type
= file_type::none
;
130 if(S_ISBLK(stat_results
.st_mode
))
131 type
= file_type::block
;
132 else if(S_ISCHR(stat_results
.st_mode
))
133 type
= file_type::character
;
134 else if(S_ISDIR(stat_results
.st_mode
))
135 type
= file_type::directory
;
136 else if(S_ISFIFO(stat_results
.st_mode
))
137 type
= file_type::fifo
;
138 else if(S_ISREG(stat_results
.st_mode
))
139 type
= file_type::regular
;
140 else if(S_ISLNK(stat_results
.st_mode
))
141 type
= file_type::symlink
;
142 else if(S_ISSOCK(stat_results
.st_mode
))
143 type
= file_type::symlink
;
145 type
= file_type::unknown
;
148 #error Stat_results is not implemented for your operating system
152 std::uintmax_t file_size(const path
&p
, std::error_code
*ec
)
155 #error file_size is not implemented on windows
156 #elif defined(__linux__)
159 std::error_code stat_error
;
160 Stat_results
stat_results(p
, true, stat_error
);
163 set_or_throw_error(ec
, "stat failed", p
, stat_error
);
166 return stat_results
.stat_results
.st_size
;
168 #error file_size is not implemented for your operating system
172 std::uintmax_t hard_link_count(const path
&p
, std::error_code
*ec
)
175 #error hard_link_count is not implemented on windows
176 #elif defined(__linux__)
179 std::error_code stat_error
;
180 Stat_results
stat_results(p
, true, stat_error
);
183 set_or_throw_error(ec
, "stat failed", p
, stat_error
);
186 return stat_results
.stat_results
.st_nlink
;
188 #error hard_link_count is not implemented for your operating system
192 file_time_type
last_write_time(const path
&p
, std::error_code
*ec
)
195 #error hard_link_count is not implemented on windows
196 #elif defined(__linux__)
199 std::error_code stat_error
;
200 Stat_results
stat_results(p
, true, stat_error
);
203 set_or_throw_error(ec
, "stat failed", p
, stat_error
);
204 return file_time_type::min();
206 return timespec_to_time_point(stat_results
.stat_results
.st_mtim
);
208 #error hard_link_count is not implemented for your operating system
212 file_status
status(const path
&p
, bool follow_symlink
, std::error_code
*ec
)
215 #error status is not implemented on windows
216 #elif defined(__linux__)
219 std::error_code stat_error
;
220 Stat_results
stat_results(p
, follow_symlink
, stat_error
);
223 if(stat_results
.type
== file_type::none
|| ec
)
224 set_or_throw_error(ec
, "stat failed", p
, stat_error
);
225 return file_status(stat_results
.type
);
227 return file_status(stat_results
.type
,
228 static_cast<perms
>(static_cast<std::uint32_t>(perms::mask
)
229 & stat_results
.stat_results
.st_mode
));
231 #error status is not implemented for your operating system
236 void directory_entry::refresh(std::error_code
*ec
)
239 #error status is not implemented on windows
240 #elif defined(__linux__)
244 std::error_code stat_error
;
245 detail::Stat_results
stat_results(path_value
, false, stat_error
);
248 if(stat_results
.type
== file_type::none
)
250 detail::set_or_throw_error(ec
, "stat failed", path_value
, stat_error
);
253 flags
.has_symlink_status_full_value
= true;
254 symlink_status_value
= file_status(stat_results
.type
);
257 flags
.has_symlink_status_full_value
= true;
258 symlink_status_value
= file_status(stat_results
.type
,
259 static_cast<perms
>(static_cast<std::uint32_t>(perms::mask
)
260 & stat_results
.stat_results
.st_mode
));
261 flags
.has_file_size_value
= true;
262 file_size_value
= stat_results
.stat_results
.st_size
;
263 flags
.has_hard_link_count_value
= true;
264 hard_link_count_value
= stat_results
.stat_results
.st_nlink
;
265 flags
.has_last_write_time_value
= true;
266 last_write_time_value
= detail::timespec_to_time_point(stat_results
.stat_results
.st_mtim
)
270 #error status is not implemented for your operating system
275 #error directory_iterator is not implemented on windows
276 #elif defined(__linux__)
277 struct directory_iterator::Implementation
279 ::DIR *dir
= nullptr;
280 const directory_options options
;
281 Implementation(const Implementation
&) = delete;
282 Implementation
&operator=(const Implementation
&) = delete;
283 Implementation(directory_entry
¤t_entry
,
285 directory_options options_in
,
288 : options(options_in
)
293 auto old_errno
= errno
;
294 dir
= ::opendir(p
.c_str());
300 || (options
& directory_options::skip_permission_denied
) == directory_options::none
)
301 detail::set_or_throw_error(
302 ec
, "opendir failed", p
, std::error_code(error
, std::generic_category()));
308 current_entry
.path_value
= p
/ path(); // add trailing slash
309 if(!read(current_entry
, ec
))
318 bool read(directory_entry
¤t_entry
, std::error_code
*ec
)
325 auto old_errno
= errno
;
327 // using readdir64 instead of readdir64_r: see
328 // https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html
329 entry
= ::readdir64(dir
);
335 detail::set_or_throw_error(
336 ec
, "readdir failed", std::error_code(error
, std::generic_category()));
339 if(entry
->d_name
== string_view(".") || entry
->d_name
== string_view(".."))
343 current_entry
.flags
= {};
344 current_entry
.path_value
.replace_filename(entry
->d_name
);
345 current_entry
.flags
.has_symlink_status_type_value
= true;
346 switch(entry
->d_type
)
349 current_entry
.symlink_status_value
.type(file_type::fifo
);
352 current_entry
.symlink_status_value
.type(file_type::character
);
355 current_entry
.symlink_status_value
.type(file_type::directory
);
358 current_entry
.symlink_status_value
.type(file_type::block
);
361 current_entry
.symlink_status_value
.type(file_type::symlink
);
364 current_entry
.symlink_status_value
.type(file_type::regular
);
367 current_entry
.symlink_status_value
.type(file_type::socket
);
371 current_entry
.flags
.has_symlink_status_type_value
= false;
376 void close() noexcept
380 auto old_errno
= errno
;
392 #error directory_iterator is not implemented for your operating system
395 std::shared_ptr
<directory_iterator::Implementation
> directory_iterator::create(
396 directory_entry
¤t_entry
, const path
&p
, directory_options options
, std::error_code
*ec
)
401 auto retval
= std::make_shared
<Implementation
>(current_entry
, p
, options
, ec
, failed
);
406 catch(std::bad_alloc
&)
410 *ec
= std::make_error_code(std::errc::not_enough_memory
);
415 void directory_iterator::increment(std::shared_ptr
<Implementation
> &implementation
,
416 directory_entry
¤t_entry
,
421 if(!implementation
->read(current_entry
, ec
))
422 implementation
= nullptr;
426 implementation
= nullptr;
434 #if 0 // change to 1 to test filesystem::path
443 #warning testing util::filesystem::path
446 template <typename Path
, bool Show_parts
= true>
447 static void write_path(const Path
&path
)
449 std::cout
<< path
<< ": kind=";
452 case Path_part_kind::file_name
:
453 std::cout
<< "file_name";
455 case Path_part_kind::multiple_parts
:
456 std::cout
<< "multiple_parts";
458 case Path_part_kind::root_dir
:
459 std::cout
<< "root_dir";
461 case Path_part_kind::relative_root_name
:
462 std::cout
<< "relative_root_name";
464 case Path_part_kind::absolute_root_name
:
465 std::cout
<< "absolute_root_name";
467 case Path_part_kind::path_separator
:
468 std::cout
<< "path_separator";
473 std::cout
<< " parts=[";
475 for(auto &part
: path
.parts
)
477 std::cout
<< separator
;
479 write_path
<Path
, false>(part
);
484 template <Path_traits_kind Traits_kind
>
485 static void test_path(const char *traits_kind_name
)
488 typedef basic_path
<> Path
;
490 typedef basic_path
<Traits_kind
> Path
;
492 std::cout
<< "testing basic_path<" << traits_kind_name
<< ">" << std::endl
;
493 for(auto *test_path_string
: {
518 "a/b/c/d/../.././e/../../f",
523 Path
p(test_path_string
);
524 std::cout
<< "'" << test_path_string
<< "' -> ";
526 std::cout
<< std::endl
;
527 std::cout
<< "make_preferred -> " << Path(p
).make_preferred() << std::endl
;
528 std::cout
<< "remove_filename -> " << Path(p
).remove_filename() << std::endl
;
529 std::cout
<< "lexically_normal -> " << p
.lexically_normal() << std::endl
;
530 std::cout
<< "root_name -> " << p
.root_name() << std::endl
;
531 std::cout
<< "root_directory -> " << p
.root_directory() << std::endl
;
532 std::cout
<< "root_path -> " << p
.root_path() << std::endl
;
533 std::cout
<< "relative_path -> " << p
.relative_path() << std::endl
;
534 std::cout
<< "parent_path -> " << p
.parent_path() << std::endl
;
535 std::cout
<< "filename -> " << p
.filename() << std::endl
;
536 std::cout
<< "stem -> " << p
.stem() << std::endl
;
537 std::cout
<< "extension -> " << p
.extension() << std::endl
;
538 std::cout
<< "operator/:";
539 for(auto *appended_path
: {
540 "", "/abc", "C:abc", "//a/abc", "C:/abc", "abc",
543 std::cout
<< " \"" << appended_path
<< "\"->" << p
/ appended_path
;
545 std::cout
<< std::endl
;
546 std::cout
<< "lexically_proximate:";
547 for(auto *base_path
: {
548 "", "/abc", "C:abc", "//a/abc", "C:/abc", "abc",
551 std::cout
<< " \"" << base_path
<< "\"->" << p
.lexically_proximate(base_path
);
553 std::cout
<< std::endl
;
558 test_path
<Path_traits_kind::posix
>("posix");
559 test_path
<Path_traits_kind::windows
>("windows");
560 std::string().assign("", 0);
571 Path_tester path_tester
;