2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of libdcp.
6 libdcp is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 libdcp is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with libdcp. If not, see <http://www.gnu.org/licenses/>.
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of portions of this program with the
21 OpenSSL library under certain conditions as described in each
22 individual source file, and distribute linked combinations
25 You must obey the GNU General Public License in all respects
26 for all of the code used other than OpenSSL. If you modify
27 file(s) with this exception, you may extend this exception to your
28 version of the file(s), but you are not obligated to do so. If you
29 do not wish to do so, delete this exception statement from your
30 version. If you delete this exception statement from all source
31 files in the program, then also delete it here.
35 #include "filesystem.h"
36 #include <boost/algorithm/string.hpp>
40 dcp::filesystem::exists(boost::filesystem::path const& path)
42 return boost::filesystem::exists(dcp::filesystem::fix_long_path(path));
47 dcp::filesystem::exists(boost::filesystem::path const& path, boost::system::error_code& ec)
49 return boost::filesystem::exists(dcp::filesystem::fix_long_path(path), ec);
54 dcp::filesystem::is_directory(boost::filesystem::path const& path)
56 return boost::filesystem::is_directory(dcp::filesystem::fix_long_path(path));
61 dcp::filesystem::is_empty(boost::filesystem::path const& path)
63 return boost::filesystem::is_empty(dcp::filesystem::fix_long_path(path));
68 dcp::filesystem::is_regular_file(boost::filesystem::path const& path)
70 return boost::filesystem::is_regular_file(dcp::filesystem::fix_long_path(path));
75 dcp::filesystem::create_directory(boost::filesystem::path const& path)
77 return boost::filesystem::create_directory(dcp::filesystem::fix_long_path(path));
82 dcp::filesystem::create_directory(boost::filesystem::path const& path, boost::system::error_code& ec)
84 return boost::filesystem::create_directory(dcp::filesystem::fix_long_path(path), ec);
89 dcp::filesystem::copy(boost::filesystem::path const& from, boost::filesystem::path const& to)
91 boost::filesystem::copy(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to));
96 dcp::filesystem::copy_file(boost::filesystem::path const& from, boost::filesystem::path const& to)
98 boost::filesystem::copy_file(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to));
103 dcp::filesystem::copy_file(boost::filesystem::path const& from, boost::filesystem::path const& to, boost::system::error_code& ec)
105 boost::filesystem::copy_file(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to), ec);
110 dcp::filesystem::copy_file(boost::filesystem::path const& from, boost::filesystem::path const& to, boost::filesystem::copy_option option)
112 boost::filesystem::copy_file(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to), option);
117 dcp::filesystem::create_directories(boost::filesystem::path const& path)
119 return boost::filesystem::create_directories(dcp::filesystem::fix_long_path(path));
124 dcp::filesystem::create_directories(boost::filesystem::path const& path, boost::system::error_code& ec)
126 return boost::filesystem::create_directories(dcp::filesystem::fix_long_path(path), ec);
130 boost::filesystem::path
131 dcp::filesystem::absolute(boost::filesystem::path const& path)
133 return dcp::filesystem::unfix_long_path(boost::filesystem::absolute(dcp::filesystem::fix_long_path(path)));
137 boost::filesystem::path
138 dcp::filesystem::canonical(boost::filesystem::path const& path)
140 return dcp::filesystem::unfix_long_path(boost::filesystem::canonical(dcp::filesystem::fix_long_path(path)));
144 boost::filesystem::path
145 dcp::filesystem::weakly_canonical(boost::filesystem::path const& path)
147 return dcp::filesystem::unfix_long_path(boost::filesystem::weakly_canonical(dcp::filesystem::fix_long_path(path)));
152 dcp::filesystem::remove(boost::filesystem::path const& path)
154 return boost::filesystem::remove(dcp::filesystem::fix_long_path(path));
159 dcp::filesystem::remove(boost::filesystem::path const& path, boost::system::error_code& ec)
161 return boost::filesystem::remove(dcp::filesystem::fix_long_path(path), ec);
166 dcp::filesystem::remove_all(boost::filesystem::path const& path)
168 return boost::filesystem::remove_all(dcp::filesystem::fix_long_path(path));
173 dcp::filesystem::remove_all(boost::filesystem::path const& path, boost::system::error_code& ec)
175 return boost::filesystem::remove_all(dcp::filesystem::fix_long_path(path), ec);
180 dcp::filesystem::file_size(boost::filesystem::path const& path)
182 return boost::filesystem::file_size(dcp::filesystem::fix_long_path(path));
187 dcp::filesystem::file_size(boost::filesystem::path const& path, boost::system::error_code& ec)
189 return boost::filesystem::file_size(dcp::filesystem::fix_long_path(path), ec);
193 boost::filesystem::path
194 dcp::filesystem::current_path()
196 return dcp::filesystem::unfix_long_path(boost::filesystem::current_path());
201 dcp::filesystem::current_path(boost::filesystem::path const& path)
203 boost::filesystem::current_path(dcp::filesystem::fix_long_path(path));
208 dcp::filesystem::create_hard_link(boost::filesystem::path const& from, boost::filesystem::path const& to)
210 boost::filesystem::create_hard_link(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to));
215 dcp::filesystem::create_hard_link(boost::filesystem::path const& from, boost::filesystem::path const& to, boost::system::error_code& ec)
217 boost::filesystem::create_hard_link(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to), ec);
222 dcp::filesystem::create_symlink(boost::filesystem::path const& from, boost::filesystem::path const& to, boost::system::error_code& ec)
224 boost::filesystem::create_symlink(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to), ec);
229 dcp::filesystem::extension(boost::filesystem::path const& path)
231 return boost::filesystem::extension(dcp::filesystem::fix_long_path(path));
235 boost::filesystem::space_info
236 dcp::filesystem::space(boost::filesystem::path const& path)
238 return boost::filesystem::space(dcp::filesystem::fix_long_path(path));
243 dcp::filesystem::last_write_time(boost::filesystem::path const& path)
245 return boost::filesystem::last_write_time(dcp::filesystem::fix_long_path(path));
250 dcp::filesystem::last_write_time(boost::filesystem::path const& path, boost::system::error_code& ec)
252 return boost::filesystem::last_write_time(dcp::filesystem::fix_long_path(path), ec);
257 dcp::filesystem::hard_link_count(boost::filesystem::path const& path)
259 return boost::filesystem::hard_link_count(dcp::filesystem::fix_long_path(path));
264 dcp::filesystem::rename(boost::filesystem::path const& old_path, boost::filesystem::path const& new_path)
266 boost::filesystem::rename(dcp::filesystem::fix_long_path(old_path), dcp::filesystem::fix_long_path(new_path));
271 dcp::filesystem::rename(boost::filesystem::path const& old_path, boost::filesystem::path const& new_path, boost::system::error_code& ec)
273 boost::filesystem::rename(dcp::filesystem::fix_long_path(old_path), dcp::filesystem::fix_long_path(new_path), ec);
277 /* We don't really need this but let's add it for completeness */
278 boost::filesystem::path
279 dcp::filesystem::change_extension(boost::filesystem::path const& path, std::string const& new_extension)
281 return boost::filesystem::change_extension(path, new_extension);
285 #ifdef DCPOMATIC_WINDOWS
287 dcp::filesystem::directory_iterator::directory_iterator(boost::filesystem::path const& path)
288 : _wrapped(dcp::filesystem::fix_long_path(path))
294 dcp::filesystem::directory_iterator::directory_iterator(boost::filesystem::path const& path, boost::system::error_code& ec)
295 : _wrapped(dcp::filesystem::fix_long_path(path), ec)
301 boost::filesystem::path
302 dcp::filesystem::directory_entry::path() const
304 return dcp::filesystem::unfix_long_path(_path);
308 dcp::filesystem::directory_entry::operator boost::filesystem::path const &() const
310 return dcp::filesystem::unfix_long_path(_path);
314 dcp::filesystem::recursive_directory_iterator::recursive_directory_iterator(boost::filesystem::path const& path)
315 : _wrapped(dcp::filesystem::fix_long_path(path))
322 dcp::filesystem::directory_iterator::directory_iterator(boost::filesystem::path const& path)
329 dcp::filesystem::directory_iterator::directory_iterator(boost::filesystem::path const& path, boost::system::error_code& ec)
336 boost::filesystem::path
337 dcp::filesystem::directory_entry::path() const
343 dcp::filesystem::directory_entry::operator boost::filesystem::path const &() const
349 dcp::filesystem::recursive_directory_iterator::recursive_directory_iterator(boost::filesystem::path const& path)
358 dcp::filesystem::directory_entry::directory_entry(boost::filesystem::path const& path)
365 dcp::filesystem::directory_iterator&
366 dcp::filesystem::directory_iterator::operator++()
373 dcp::filesystem::directory_entry
374 dcp::filesystem::directory_iterator::operator*() const
376 _entry = dcp::filesystem::directory_entry(*_wrapped);
381 dcp::filesystem::directory_entry*
382 dcp::filesystem::directory_iterator::operator->() const
384 _entry = dcp::filesystem::directory_entry(_wrapped->path());
389 dcp::filesystem::directory_iterator::operator!=(dcp::filesystem::directory_iterator const& other) const
391 return _wrapped != other._wrapped;
395 dcp::filesystem::directory_iterator const&
396 dcp::filesystem::begin(dcp::filesystem::directory_iterator const& iter)
402 dcp::filesystem::directory_iterator
403 dcp::filesystem::end(dcp::filesystem::directory_iterator const&)
405 return dcp::filesystem::directory_iterator();
409 dcp::filesystem::recursive_directory_iterator&
410 dcp::filesystem::recursive_directory_iterator::operator++()
418 dcp::filesystem::recursive_directory_iterator::operator!=(dcp::filesystem::recursive_directory_iterator const& other) const
420 return _wrapped != other._wrapped;
424 dcp::filesystem::directory_entry
425 dcp::filesystem::recursive_directory_iterator::operator*() const
427 _entry = dcp::filesystem::directory_entry(_wrapped->path());
432 dcp::filesystem::directory_entry*
433 dcp::filesystem::recursive_directory_iterator::operator->() const
435 _entry = dcp::filesystem::directory_entry(_wrapped->path());
440 dcp::filesystem::recursive_directory_iterator const&
441 dcp::filesystem::begin(dcp::filesystem::recursive_directory_iterator const& iter)
447 dcp::filesystem::recursive_directory_iterator
448 dcp::filesystem::end(dcp::filesystem::recursive_directory_iterator const&)
450 return dcp::filesystem::recursive_directory_iterator();
454 /** Windows can't "by default" cope with paths longer than 260 characters, so if you pass such a path to
455 * any boost::filesystem method it will fail. There is a "fix" for this, which is to prepend
456 * the string \\?\ to the path. This will make it work, so long as:
457 * - the path is absolute.
458 * - the path contains no .. parts.
459 * - the path only uses backslashes.
460 * - individual path components are "short enough" (probably less than 255 characters)
462 * See https://www.boost.org/doc/libs/1_57_0/libs/filesystem/doc/reference.html under
463 * "Warning: Long paths on Windows" for some details.
465 * Our fopen_boost uses this method to get this fix, but any other calls to boost::filesystem
466 * will not unless this method is explicitly called to pre-process the pathname.
468 boost::filesystem::path
469 dcp::filesystem::fix_long_path(boost::filesystem::path long_path)
471 #ifdef LIBDCP_WINDOWS
472 using namespace boost::filesystem;
474 if (boost::algorithm::starts_with(long_path.string(), "\\\\")) {
475 /* This could mean it starts with \\ (i.e. a SMB path) or \\?\ (a long path)
476 * or a variety of other things... anyway, we'll leave it alone.
481 /* We have to make the path canonical but we can't call canonical() on the long path
482 * as it will fail. So we'll sort of do it ourselves (possibly badly).
484 path fixed = "\\\\?\\";
485 if (long_path.is_absolute()) {
486 fixed += long_path.make_preferred();
488 fixed += filesystem::current_path() / long_path.make_preferred();
497 boost::filesystem::path
498 dcp::filesystem::unfix_long_path(boost::filesystem::path long_path)
500 #ifdef LIBDCP_WINDOWS
501 if (boost::algorithm::starts_with(long_path.string(), "\\\\?\\")) {
502 return long_path.string().substr(4);