Report every frame (with index) that has a JPEG2000 codestream error (DoM #2698).
[libdcp.git] / src / filesystem.cc
1 /*
2     Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
3
4     This file is part of libdcp.
5
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.
10
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.
15
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/>.
18
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
23     including the two.
24
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.
32 */
33
34
35 #include "filesystem.h"
36 #include <boost/algorithm/string.hpp>
37
38
39 bool
40 dcp::filesystem::exists(boost::filesystem::path const& path)
41 {
42         return boost::filesystem::exists(dcp::filesystem::fix_long_path(path));
43 }
44
45
46 bool
47 dcp::filesystem::exists(boost::filesystem::path const& path, boost::system::error_code& ec)
48 {
49         return boost::filesystem::exists(dcp::filesystem::fix_long_path(path), ec);
50 }
51
52
53 bool
54 dcp::filesystem::is_directory(boost::filesystem::path const& path)
55 {
56         return boost::filesystem::is_directory(dcp::filesystem::fix_long_path(path));
57 }
58
59
60 bool
61 dcp::filesystem::is_empty(boost::filesystem::path const& path)
62 {
63         return boost::filesystem::is_empty(dcp::filesystem::fix_long_path(path));
64 }
65
66
67 bool
68 dcp::filesystem::is_regular_file(boost::filesystem::path const& path)
69 {
70         return boost::filesystem::is_regular_file(dcp::filesystem::fix_long_path(path));
71 }
72
73
74 bool
75 dcp::filesystem::create_directory(boost::filesystem::path const& path)
76 {
77         return boost::filesystem::create_directory(dcp::filesystem::fix_long_path(path));
78 }
79
80
81 bool
82 dcp::filesystem::create_directory(boost::filesystem::path const& path, boost::system::error_code& ec)
83 {
84         return boost::filesystem::create_directory(dcp::filesystem::fix_long_path(path), ec);
85 }
86
87
88 void
89 dcp::filesystem::copy(boost::filesystem::path const& from, boost::filesystem::path const& to)
90 {
91         boost::filesystem::copy(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to));
92 }
93
94
95 void
96 dcp::filesystem::copy_file(boost::filesystem::path const& from, boost::filesystem::path const& to)
97 {
98         boost::filesystem::copy_file(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to));
99 }
100
101
102 void
103 dcp::filesystem::copy_file(boost::filesystem::path const& from, boost::filesystem::path const& to, boost::system::error_code& ec)
104 {
105         boost::filesystem::copy_file(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to), ec);
106 }
107
108
109 void
110 dcp::filesystem::copy_file(boost::filesystem::path const& from, boost::filesystem::path const& to, boost::filesystem::copy_option option)
111 {
112         boost::filesystem::copy_file(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to), option);
113 }
114
115
116 bool
117 dcp::filesystem::create_directories(boost::filesystem::path const& path)
118 {
119         return boost::filesystem::create_directories(dcp::filesystem::fix_long_path(path));
120 }
121
122
123 bool
124 dcp::filesystem::create_directories(boost::filesystem::path const& path, boost::system::error_code& ec)
125 {
126         return boost::filesystem::create_directories(dcp::filesystem::fix_long_path(path), ec);
127 }
128
129
130 boost::filesystem::path
131 dcp::filesystem::absolute(boost::filesystem::path const& path)
132 {
133         return dcp::filesystem::unfix_long_path(boost::filesystem::absolute(dcp::filesystem::fix_long_path(path)));
134 }
135
136
137 boost::filesystem::path
138 dcp::filesystem::canonical(boost::filesystem::path const& path)
139 {
140         return dcp::filesystem::unfix_long_path(boost::filesystem::canonical(dcp::filesystem::fix_long_path(path)));
141 }
142
143
144 boost::filesystem::path
145 dcp::filesystem::weakly_canonical(boost::filesystem::path const& path)
146 {
147 #ifdef DCPOMATIC_HAVE_WEAKLY_CANONICAL
148         return dcp::filesystem::unfix_long_path(boost::filesystem::weakly_canonical(dcp::filesystem::fix_long_path(path)));
149 #else
150         boost::filesystem::path complete(boost::filesystem::system_complete(dcp::filesystem::fix_long_path(path)));
151         boost::filesystem::path result;
152         for (auto part: complete) {
153                 if (part == "..") {
154                         boost::system::error_code ec;
155                         if (boost::filesystem::is_symlink(result, ec) || result.filename() == "..") {
156                                 result /= part;
157                         } else {
158                                 result = result.parent_path();
159                         }
160                 } else if (part != ".") {
161                         result /= part;
162                 }
163         }
164
165         return dcp::filesystem::unfix_long_path(result.make_preferred());
166 #endif
167 }
168
169
170 bool
171 dcp::filesystem::remove(boost::filesystem::path const& path)
172 {
173         return boost::filesystem::remove(dcp::filesystem::fix_long_path(path));
174 }
175
176
177 bool
178 dcp::filesystem::remove(boost::filesystem::path const& path, boost::system::error_code& ec)
179 {
180         return boost::filesystem::remove(dcp::filesystem::fix_long_path(path), ec);
181 }
182
183
184 uintmax_t
185 dcp::filesystem::remove_all(boost::filesystem::path const& path)
186 {
187         return boost::filesystem::remove_all(dcp::filesystem::fix_long_path(path));
188 }
189
190
191 uintmax_t
192 dcp::filesystem::remove_all(boost::filesystem::path const& path, boost::system::error_code& ec)
193 {
194         return boost::filesystem::remove_all(dcp::filesystem::fix_long_path(path), ec);
195 }
196
197
198 uintmax_t
199 dcp::filesystem::file_size(boost::filesystem::path const& path)
200 {
201         return boost::filesystem::file_size(dcp::filesystem::fix_long_path(path));
202 }
203
204
205 uintmax_t
206 dcp::filesystem::file_size(boost::filesystem::path const& path, boost::system::error_code& ec)
207 {
208         return boost::filesystem::file_size(dcp::filesystem::fix_long_path(path), ec);
209 }
210
211
212 boost::filesystem::path
213 dcp::filesystem::current_path()
214 {
215         return dcp::filesystem::unfix_long_path(boost::filesystem::current_path());
216 }
217
218
219 void
220 dcp::filesystem::current_path(boost::filesystem::path const& path)
221 {
222         boost::filesystem::current_path(dcp::filesystem::fix_long_path(path));
223 }
224
225
226 void
227 dcp::filesystem::create_hard_link(boost::filesystem::path const& from, boost::filesystem::path const& to)
228 {
229         boost::filesystem::create_hard_link(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to));
230 }
231
232
233 void
234 dcp::filesystem::create_hard_link(boost::filesystem::path const& from, boost::filesystem::path const& to, boost::system::error_code& ec)
235 {
236         boost::filesystem::create_hard_link(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to), ec);
237 }
238
239
240 void
241 dcp::filesystem::create_symlink(boost::filesystem::path const& from, boost::filesystem::path const& to, boost::system::error_code& ec)
242 {
243         boost::filesystem::create_symlink(dcp::filesystem::fix_long_path(from), dcp::filesystem::fix_long_path(to), ec);
244 }
245
246
247 std::string
248 dcp::filesystem::extension(boost::filesystem::path const& path)
249 {
250         return boost::filesystem::extension(dcp::filesystem::fix_long_path(path));
251 }
252
253
254 boost::filesystem::space_info
255 dcp::filesystem::space(boost::filesystem::path const& path)
256 {
257         return boost::filesystem::space(dcp::filesystem::fix_long_path(path));
258 }
259
260
261 std::time_t
262 dcp::filesystem::last_write_time(boost::filesystem::path const& path)
263 {
264         return boost::filesystem::last_write_time(dcp::filesystem::fix_long_path(path));
265 }
266
267
268 std::time_t
269 dcp::filesystem::last_write_time(boost::filesystem::path const& path, boost::system::error_code& ec)
270 {
271         return boost::filesystem::last_write_time(dcp::filesystem::fix_long_path(path), ec);
272 }
273
274
275 uintmax_t
276 dcp::filesystem::hard_link_count(boost::filesystem::path const& path)
277 {
278         return boost::filesystem::hard_link_count(dcp::filesystem::fix_long_path(path));
279 }
280
281
282 void
283 dcp::filesystem::rename(boost::filesystem::path const& old_path, boost::filesystem::path const& new_path)
284 {
285         boost::filesystem::rename(dcp::filesystem::fix_long_path(old_path), dcp::filesystem::fix_long_path(new_path));
286 }
287
288
289 void
290 dcp::filesystem::rename(boost::filesystem::path const& old_path, boost::filesystem::path const& new_path, boost::system::error_code& ec)
291 {
292         boost::filesystem::rename(dcp::filesystem::fix_long_path(old_path), dcp::filesystem::fix_long_path(new_path), ec);
293 }
294
295
296 /* We don't really need this but let's add it for completeness */
297 boost::filesystem::path
298 dcp::filesystem::change_extension(boost::filesystem::path const& path, std::string const& new_extension)
299 {
300         return boost::filesystem::change_extension(path, new_extension);
301 }
302
303
304 #ifdef DCPOMATIC_WINDOWS
305
306 dcp::filesystem::directory_iterator::directory_iterator(boost::filesystem::path const& path)
307         : _wrapped(dcp::filesystem::fix_long_path(path))
308 {
309
310 }
311
312
313 dcp::filesystem::directory_iterator::directory_iterator(boost::filesystem::path const& path, boost::system::error_code& ec)
314         : _wrapped(dcp::filesystem::fix_long_path(path), ec)
315 {
316
317 }
318
319
320 boost::filesystem::path
321 dcp::filesystem::directory_entry::path() const
322 {
323         return dcp::filesystem::unfix_long_path(_path);
324 }
325
326
327 dcp::filesystem::directory_entry::operator boost::filesystem::path const &() const
328 {
329         return dcp::filesystem::unfix_long_path(_path);
330 }
331
332
333 dcp::filesystem::recursive_directory_iterator::recursive_directory_iterator(boost::filesystem::path const& path)
334         : _wrapped(dcp::filesystem::fix_long_path(path))
335 {
336
337 }
338
339 #else
340
341 dcp::filesystem::directory_iterator::directory_iterator(boost::filesystem::path const& path)
342         : _wrapped(path)
343 {
344
345 }
346
347
348 dcp::filesystem::directory_iterator::directory_iterator(boost::filesystem::path const& path, boost::system::error_code& ec)
349         : _wrapped(path, ec)
350 {
351
352 }
353
354
355 boost::filesystem::path
356 dcp::filesystem::directory_entry::path() const
357 {
358         return _path;
359 }
360
361
362 dcp::filesystem::directory_entry::operator boost::filesystem::path const &() const
363 {
364         return _path;
365 }
366
367
368 dcp::filesystem::recursive_directory_iterator::recursive_directory_iterator(boost::filesystem::path const& path)
369         : _wrapped(path)
370 {
371
372 }
373
374 #endif
375
376
377 dcp::filesystem::directory_entry::directory_entry(boost::filesystem::path const& path)
378         : _path(path)
379 {
380
381 }
382
383
384 dcp::filesystem::directory_iterator&
385 dcp::filesystem::directory_iterator::operator++()
386 {
387         ++_wrapped;
388         return *this;
389 }
390
391
392 dcp::filesystem::directory_entry
393 dcp::filesystem::directory_iterator::operator*() const
394 {
395         _entry = dcp::filesystem::directory_entry(*_wrapped);
396         return _entry;
397 }
398
399
400 dcp::filesystem::directory_entry*
401 dcp::filesystem::directory_iterator::operator->() const
402 {
403         _entry = dcp::filesystem::directory_entry(_wrapped->path());
404         return &_entry;
405 }
406
407 bool
408 dcp::filesystem::directory_iterator::operator!=(dcp::filesystem::directory_iterator const& other) const
409 {
410         return _wrapped != other._wrapped;
411 }
412
413
414 dcp::filesystem::directory_iterator const&
415 dcp::filesystem::begin(dcp::filesystem::directory_iterator const& iter)
416 {
417         return iter;
418 }
419
420
421 dcp::filesystem::directory_iterator
422 dcp::filesystem::end(dcp::filesystem::directory_iterator const&)
423 {
424         return dcp::filesystem::directory_iterator();
425 }
426
427
428 dcp::filesystem::recursive_directory_iterator&
429 dcp::filesystem::recursive_directory_iterator::operator++()
430 {
431         ++_wrapped;
432         return *this;
433 }
434
435
436 bool
437 dcp::filesystem::recursive_directory_iterator::operator!=(dcp::filesystem::recursive_directory_iterator const& other) const
438 {
439         return _wrapped != other._wrapped;
440 }
441
442
443 dcp::filesystem::directory_entry
444 dcp::filesystem::recursive_directory_iterator::operator*() const
445 {
446         _entry = dcp::filesystem::directory_entry(_wrapped->path());
447         return _entry;
448 }
449
450
451 dcp::filesystem::directory_entry*
452 dcp::filesystem::recursive_directory_iterator::operator->() const
453 {
454         _entry = dcp::filesystem::directory_entry(_wrapped->path());
455         return &_entry;
456 }
457
458
459 dcp::filesystem::recursive_directory_iterator const&
460 dcp::filesystem::begin(dcp::filesystem::recursive_directory_iterator const& iter)
461 {
462         return iter;
463 }
464
465
466 dcp::filesystem::recursive_directory_iterator
467 dcp::filesystem::end(dcp::filesystem::recursive_directory_iterator const&)
468 {
469         return dcp::filesystem::recursive_directory_iterator();
470 }
471
472
473 /** Windows can't "by default" cope with paths longer than 260 characters, so if you pass such a path to
474  *  any boost::filesystem method it will fail.  There is a "fix" for this, which is to prepend
475  *  the string \\?\ to the path.  This will make it work, so long as:
476  *  - the path is absolute.
477  *  - the path contains no .. parts.
478  *  - the path only uses backslashes.
479  *  - individual path components are "short enough" (probably less than 255 characters)
480  *
481  *  See https://www.boost.org/doc/libs/1_57_0/libs/filesystem/doc/reference.html under
482  *  "Warning: Long paths on Windows" for some details.
483  *
484  *  Our fopen_boost uses this method to get this fix, but any other calls to boost::filesystem
485  *  will not unless this method is explicitly called to pre-process the pathname.
486  */
487 boost::filesystem::path
488 dcp::filesystem::fix_long_path(boost::filesystem::path long_path)
489 {
490 #ifdef LIBDCP_WINDOWS
491         using namespace boost::filesystem;
492
493         if (boost::algorithm::starts_with(long_path.string(), "\\\\")) {
494                 /* This could mean it starts with \\ (i.e. a SMB path) or \\?\ (a long path)
495                  * or a variety of other things... anyway, we'll leave it alone.
496                  */
497                 return long_path;
498         }
499
500         /* We have to make the path canonical but we can't call canonical() on the long path
501          * as it will fail.  So we'll sort of do it ourselves (possibly badly).
502          */
503         path fixed = "\\\\?\\";
504         if (long_path.is_absolute()) {
505                 fixed += long_path.make_preferred();
506         } else {
507                 fixed += filesystem::current_path() / long_path.make_preferred();
508         }
509         return fixed;
510 #else
511         return long_path;
512 #endif
513 }
514
515
516 boost::filesystem::path
517 dcp::filesystem::unfix_long_path(boost::filesystem::path long_path)
518 {
519 #ifdef LIBDCP_WINDOWS
520         if (boost::algorithm::starts_with(long_path.string(), "\\\\?\\")) {
521                 return long_path.string().substr(4);
522         }
523 #endif
524         return long_path;
525 }