Blender V4.3
fileops_c.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10#include <cstdlib> /* malloc */
11#include <cstring>
12
13#include <fcntl.h>
14#include <sys/stat.h>
15#include <sys/types.h>
16
17#include <cerrno>
18
19#include <zlib.h>
20#include <zstd.h>
21
22#ifdef WIN32
23# include "BLI_fileops_types.h"
24# include "BLI_winstuff.h"
25# include "utf_winfunc.hh"
26# include "utfconv.hh"
27# include <io.h>
28# include <shellapi.h>
29# include <shobjidl.h>
30# include <windows.h>
31#else
32# if defined(__APPLE__)
33# include <CoreFoundation/CoreFoundation.h>
34# include <objc/message.h>
35# include <objc/runtime.h>
36# endif
37# include <dirent.h>
38# include <sys/param.h>
39# include <sys/wait.h>
40# include <unistd.h>
41#endif
42
43#include "MEM_guardedalloc.h"
44
45#include "BLI_fileops.h"
46#include "BLI_path_utils.hh"
47#include "BLI_string.h"
48#include "BLI_string_utils.hh"
49#include "BLI_sys_types.h" /* For `intptr_t` support. */
50#include "BLI_utildefines.h"
51
53#define FILE_MAX_STATIC_BUF 256
54
55#ifdef WIN32
56/* Text string used as the "verb" for Windows shell operations. */
57static const char *windows_operation_string(FileExternalOperation operation)
58{
59 switch (operation) {
61 return "open";
63 return "open";
65 return "edit";
67 return "new";
69 return "find";
71 return "show";
73 return "play";
75 return "browse";
77 return "preview";
79 return "print";
81 return "install";
83 return "runas";
85 return "properties";
87 return "find";
89 return "cmd";
90 }
92 return "";
93}
94#endif
95
96int64_t BLI_read(int fd, void *buf, size_t nbytes)
97{
98 /* Define our own read as `read` is not guaranteed to read the number of bytes requested.
99 * This happens rarely but was observed with larger than 2GB files on Linux, see: #113473.
100 *
101 * Even though this is a loop, the most common code-path will exit with "Success" case.
102 * In the case where read more data than the file contains, it will loop twice,
103 * exiting on EOF with the second iteration. */
104 int64_t nbytes_read_total = 0;
105 while (true) {
106 int64_t nbytes_read = read(fd,
107 buf,
108#ifdef WIN32
109 /* Read must not exceed INT_MAX on WIN32, clamp. */
110 std::min<size_t>(nbytes, INT_MAX)
111#else
112 nbytes
113#endif
114 );
115 if (nbytes_read == nbytes) {
116 /* Success (common case). */
117 return nbytes_read_total + nbytes_read;
118 }
119 if (nbytes_read == 0) {
120 /* EOF (common case for the second iteration when reading more data than `fd` contains). */
121 return nbytes_read_total;
122 }
123 if (nbytes_read < 0) {
124 /* Error. */
125 return nbytes_read;
126 }
127
128 if (UNLIKELY(nbytes_read > nbytes)) {
129 /* Badly behaving LIBC, reading more bytes than requested should never happen.
130 * Possibly an invalid internal state/corruption, only check to prevent an eternal loop. */
132 /* Set the IO-error so there is some indication an error occurred. */
133 if (errno == 0) {
134 errno = EIO;
135 }
136 return -1;
137 }
138
139 /* If this is reached, fewer bytes were read than were requested. */
140 buf = (void *)(((char *)buf) + nbytes_read);
141 nbytes_read_total += nbytes_read;
142 nbytes -= nbytes_read;
143 }
144}
145
147{
148#ifdef WIN32
149 const char *opstring = windows_operation_string(operation);
150 return BLI_windows_external_operation_supported(filepath, opstring);
151#else
152 UNUSED_VARS(filepath, operation);
153 return false;
154#endif
155}
156
158{
159#ifdef WIN32
160 const char *opstring = windows_operation_string(operation);
161 if (BLI_windows_external_operation_supported(filepath, opstring) &&
162 BLI_windows_external_operation_execute(filepath, opstring))
163 {
164 return true;
165 }
166 return false;
167#else
168 UNUSED_VARS(filepath, operation);
169 return false;
170#endif
171}
172
174 void *buf, size_t len, FILE *file, size_t file_offset, int compression_level)
175{
176 fseek(file, file_offset, SEEK_SET);
177
178 ZSTD_CCtx *ctx = ZSTD_createCCtx();
179 ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level);
180
181 ZSTD_inBuffer input = {buf, len, 0};
182
183 size_t out_len = ZSTD_CStreamOutSize();
184 void *out_buf = MEM_mallocN(out_len, __func__);
185 size_t total_written = 0;
186
187 /* Compress block and write it out until the input has been consumed. */
188 while (input.pos < input.size) {
189 ZSTD_outBuffer output = {out_buf, out_len, 0};
190 size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue);
191 if (ZSTD_isError(ret)) {
192 break;
193 }
194 if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
195 break;
196 }
197 total_written += output.pos;
198 }
199
200 /* Finalize the `Zstd` frame. */
201 size_t ret = 1;
202 while (ret != 0) {
203 ZSTD_outBuffer output = {out_buf, out_len, 0};
204 ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end);
205 if (ZSTD_isError(ret)) {
206 break;
207 }
208 if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
209 break;
210 }
211 total_written += output.pos;
212 }
213
214 MEM_freeN(out_buf);
215 ZSTD_freeCCtx(ctx);
216
217 return ZSTD_isError(ret) ? 0 : total_written;
218}
219
220size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
221{
222 fseek(file, file_offset, SEEK_SET);
223
224 ZSTD_DCtx *ctx = ZSTD_createDCtx();
225
226 size_t in_len = ZSTD_DStreamInSize();
227 void *in_buf = MEM_mallocN(in_len, __func__);
228 ZSTD_inBuffer input = {in_buf, in_len, 0};
229
230 ZSTD_outBuffer output = {buf, len, 0};
231
232 size_t ret = 0;
233 /* Read and decompress chunks of input data until we have enough output. */
234 while (output.pos < output.size && !ZSTD_isError(ret)) {
235 input.size = fread(in_buf, 1, in_len, file);
236 if (input.size == 0) {
237 break;
238 }
239
240 /* Consume input data until we run out or have enough output. */
241 input.pos = 0;
242 while (input.pos < input.size && output.pos < output.size) {
243 ret = ZSTD_decompressStream(ctx, &output, &input);
244
245 if (ZSTD_isError(ret)) {
246 break;
247 }
248 }
249 }
250
251 MEM_freeN(in_buf);
252 ZSTD_freeDCtx(ctx);
253
254 return ZSTD_isError(ret) ? 0 : output.pos;
255}
256
257bool BLI_file_magic_is_gzip(const char header[4])
258{
259 /* GZIP itself starts with the magic bytes 0x1f 0x8b.
260 * The third byte indicates the compression method, which is 0x08 for DEFLATE. */
261 return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08;
262}
263
264bool BLI_file_magic_is_zstd(const char header[4])
265{
266 /* ZSTD files consist of concatenated frames, each either a ZSTD frame or a skippable frame.
267 * Both types of frames start with a magic number: `0xFD2FB528` for ZSTD frames and `0x184D2A5`
268 * for skippable frames, with the * being anything from 0 to F.
269 *
270 * To check whether a file is ZSTD-compressed, we just check whether the first frame matches
271 * either. Seeking through the file until a ZSTD frame is found would make things more
272 * complicated and the probability of a false positive is rather low anyways.
273 *
274 * Note that LZ4 uses a compatible format, so even though its compressed frames have a
275 * different magic number, a valid LZ4 file might also start with a skippable frame matching
276 * the second check here.
277 *
278 * For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
279 */
280
281 uint32_t magic = *((uint32_t *)header);
282 if (magic == 0xFD2FB528) {
283 return true;
284 }
285 if ((magic >> 4) == 0x184D2A5) {
286 return true;
287 }
288 return false;
289}
290
291bool BLI_file_is_writable(const char *filepath)
292{
293 bool writable;
294 if (BLI_access(filepath, W_OK) == 0) {
295 /* File exists and I can write to it. */
296 writable = true;
297 }
298 else if (errno != ENOENT) {
299 /* Most likely file or containing directory cannot be accessed. */
300 writable = false;
301 }
302 else {
303 /* File doesn't exist -- check I can create it in parent directory. */
304 char parent[FILE_MAX];
305 BLI_path_split_dir_part(filepath, parent, sizeof(parent));
306#ifdef WIN32
307 /* Windows does not have X_OK. */
308 writable = BLI_access(parent, W_OK) == 0;
309#else
310 writable = BLI_access(parent, X_OK | W_OK) == 0;
311#endif
312 }
313 return writable;
314}
315
316bool BLI_file_touch(const char *filepath)
317{
318 FILE *f = BLI_fopen(filepath, "r+b");
319
320 if (f != nullptr) {
321 int c = getc(f);
322
323 if (c == EOF) {
324 /* Empty file, reopen in truncate write mode. */
325 fclose(f);
326 f = BLI_fopen(filepath, "w+b");
327 }
328 else {
329 /* Otherwise, rewrite first byte. */
330 rewind(f);
331 putc(c, f);
332 }
333 }
334 else {
335 f = BLI_fopen(filepath, "wb");
336 }
337 if (f) {
338 fclose(f);
339 return true;
340 }
341 return false;
342}
343
344static bool dir_create_recursive(char *dirname, int len)
345{
346 BLI_assert(strlen(dirname) == len);
348 /* Caller must ensure the path doesn't have trailing slashes. */
350 "Paths must not end with a slash!");
352 STREQ(dirname + (len - 2), "..")),
353 "Paths containing \"..\" components must be normalized first!");
354
355 bool ret = true;
356 char *dirname_parent_end = (char *)BLI_path_parent_dir_end(dirname, len);
357 if (dirname_parent_end) {
358 const char dirname_parent_end_value = *dirname_parent_end;
359 *dirname_parent_end = '\0';
360#ifdef WIN32
361 /* Check special case `c:\foo`, don't try create `c:`, harmless but unnecessary. */
363#endif
364 {
365 const int mode = BLI_exists(dirname);
366 if (mode != 0) {
367 if (!S_ISDIR(mode)) {
368 ret = false;
369 }
370 }
371 else if (!dir_create_recursive(dirname, dirname_parent_end - dirname)) {
372 ret = false;
373 }
374 }
375 *dirname_parent_end = dirname_parent_end_value;
376 }
377 if (ret) {
378#ifdef WIN32
379 if (umkdir(dirname) == -1) {
380 ret = false;
381 }
382#else
383 if (mkdir(dirname, 0777) != 0) {
384 ret = false;
385 }
386#endif
387 }
388 return ret;
389}
390
392{
393 const int mode = BLI_exists(dirname);
394 if (mode != 0) {
395 /* The file exists, either it's a directory (ok), or not,
396 * in which case this function can't do anything useful
397 * (the caller could remove it and re-run this function). */
398 return S_ISDIR(mode) ? true : false;
399 }
400
401 char dirname_static_buf[FILE_MAX];
402 char *dirname_mut = dirname_static_buf;
403
404 size_t len = strlen(dirname);
405 if (len >= sizeof(dirname_static_buf)) {
406 dirname_mut = MEM_cnew_array<char>(len + 1, __func__);
407 }
408 memcpy(dirname_mut, dirname, len + 1);
409
410 /* Strip trailing chars, important for first entering #dir_create_recursive
411 * when then ensures this is the case for recursive calls. */
412 while ((len > 0) && BLI_path_slash_is_native_compat(dirname_mut[len - 1])) {
413 len--;
414 }
415 dirname_mut[len] = '\0';
416
417 const bool ret = (len > 0) && dir_create_recursive(dirname_mut, len);
418
419 /* Ensure the string was properly restored. */
420 BLI_assert(memcmp(dirname, dirname_mut, len) == 0);
421
422 if (dirname_mut != dirname_static_buf) {
423 MEM_freeN(dirname_mut);
424 }
425
426 return ret;
427}
428
429bool BLI_file_ensure_parent_dir_exists(const char *filepath)
430{
431 char di[FILE_MAX];
432 BLI_path_split_dir_part(filepath, di, sizeof(di));
433
434 /* Make if the dir doesn't exist. */
435 return BLI_dir_create_recursive(di);
436}
437
438int BLI_rename(const char *from, const char *to)
439{
440 if (!BLI_exists(from)) {
441 return 1;
442 }
443
444 /* NOTE(@ideasman42): there are no checks that `from` & `to` *aren't* the same file.
445 * It's up to the caller to ensure this. In practice these paths are often generated
446 * and known to be different rather than arbitrary user input.
447 * In the case of arbitrary paths (renaming a file in the file-selector for example),
448 * the caller must ensure file renaming doesn't cause user data loss.
449 *
450 * Support for checking the files aren't the same could be added, however path comparison
451 * alone is *not* a guarantee the files are different (given the possibility of accessing
452 * the same file through different paths via symbolic-links), we could instead support a
453 * version of Python's `os.path.samefile(..)` which compares the I-node & device.
454 * In this particular case we would not want to follow symbolic-links as well.
455 * Since this functionality isn't required at the moment, leave this as-is.
456 * Noting it as a potential improvement. */
457
458 /* NOTE: To avoid the concurrency 'time of check/time of use' (TOC/TOU) issue, this code attempts
459 * to use available solutions for an 'atomic' (file-system wise) rename operation, instead of
460 * first checking for an existing `to` target path, and then doing the rename operation if it
461 * does not exists at the time of check.
462 *
463 * Windows (through `MoveFileExW`) by default does not allow replacing an existing path. It is
464 * however not clear whether its API is exposed to the TOC/TOU issue or not.
465 *
466 * On Linux or OSX, to keep operations atomic, special non-standardized variants of `rename` must
467 * be used, depending on the OS. Note that there may also be failure due to file system not
468 * supporting this operation, although in practice this should not be a problem in modern
469 * systems.
470 * - https://man7.org/linux/man-pages/man2/rename.2.html
471 * - https://www.unix.com/man-page/mojave/2/renameatx_np/
472 *
473 * BSD systems do not have any such thing currently, and are therefore exposed to the TOC/TOU
474 * issue. */
475
476#ifdef WIN32
477 return urename(from, to, false);
478#else
479# if defined(__APPLE__)
480 int ret = renamex_np(from, to, RENAME_EXCL);
481 if (!(ret < 0 && errno == ENOTSUP)) {
482 return ret;
483 }
484# endif
485
486# if defined(__GLIBC_PREREQ)
487# if __GLIBC_PREREQ(2, 28)
488 /* Most common Linux case, use `RENAME_NOREPLACE` when available. */
489 int ret = renameat2(AT_FDCWD, from, AT_FDCWD, to, RENAME_NOREPLACE);
490 if (!(ret < 0 && errno == EINVAL)) {
491 return ret;
492 }
493# endif /* __GLIBC_PREREQ(2, 28) */
494# endif /* __GLIBC_PREREQ */
495 /* A naive non-atomic implementation, which is used for OS where atomic rename is not supported
496 * at all, or not implemented for specific file systems (for example NFS, Samba, exFAT, NTFS,
497 * etc). For those see #116049, #119966. */
498 if (BLI_exists(to)) {
499 return 1;
500 }
501 return rename(from, to);
502#endif /* !defined(WIN32) */
503}
504
505int BLI_rename_overwrite(const char *from, const char *to)
506{
507 if (!BLI_exists(from)) {
508 return 1;
509 }
510
511#ifdef WIN32
512 /* `urename` from `utfconv` intern utils uses `MoveFileExW`, which allows to replace an existing
513 * file, but not an existing directory, even if empty. This will only delete empty directories.
514 */
515 if (BLI_is_dir(to)) {
516 if (BLI_delete(to, true, false)) {
517 return 1;
518 }
519 }
520 return urename(from, to, true);
521#else
522 return rename(from, to);
523#endif
524}
525
526#ifdef WIN32
527
528static void callLocalErrorCallBack(const char *err)
529{
530 printf("%s\n", err);
531}
532
533FILE *BLI_fopen(const char *filepath, const char *mode)
534{
535 BLI_assert(!BLI_path_is_rel(filepath));
536
537 return ufopen(filepath, mode);
538}
539
540void BLI_get_short_name(char short_name[256], const char *filepath)
541{
542 wchar_t short_name_16[256];
543 int i = 0;
544
545 UTF16_ENCODE(filepath);
546
547 GetShortPathNameW(filepath_16, short_name_16, 256);
548
549 for (i = 0; i < 256; i++) {
550 short_name[i] = char(short_name_16[i]);
551 }
552
553 UTF16_UN_ENCODE(filepath);
554}
555
556void *BLI_gzopen(const char *filepath, const char *mode)
557{
558 gzFile gzfile;
559
560 BLI_assert(!BLI_path_is_rel(filepath));
561
562 /* XXX: Creates file before transcribing the path. */
563 if (mode[0] == 'w') {
564 FILE *file = ufopen(filepath, "a");
565 if (file == NULL) {
566 /* File couldn't be opened, e.g. due to permission error. */
567 return NULL;
568 }
569 fclose(file);
570 }
571
572 /* temporary #if until we update all libraries to 1.2.7
573 * for correct wide char path handling */
574# if ZLIB_VERNUM >= 0x1270
575 UTF16_ENCODE(filepath);
576
577 gzfile = gzopen_w(filepath_16, mode);
578
579 UTF16_UN_ENCODE(filepath);
580# else
581 {
582 char short_name[256];
583 BLI_get_short_name(short_name, filepath);
584 gzfile = gzopen(short_name, mode);
585 }
586# endif
587
588 return gzfile;
589}
590
591int BLI_open(const char *filepath, int oflag, int pmode)
592{
593 BLI_assert(!BLI_path_is_rel(filepath));
594
595 return uopen(filepath, oflag, pmode);
596}
597
598int BLI_access(const char *filepath, int mode)
599{
600 BLI_assert(!BLI_path_is_rel(filepath));
601
602 return uaccess(filepath, mode);
603}
604
605static bool delete_soft(const wchar_t *path_16, const char **r_error_message)
606{
607 /* Deletes file or directory to recycling bin. The latter moves all contained files and
608 * directories recursively to the recycling bin as well. */
609 IFileOperation *pfo;
610 IShellItem *psi;
611
612 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
613
614 if (SUCCEEDED(hr)) {
615 /* This is also the case when COM was previously initialized and CoInitializeEx returns
616 * S_FALSE, which is not an error. Both HRESULT values S_OK and S_FALSE indicate success. */
617
618 hr = CoCreateInstance(
619 CLSID_FileOperation, NULL, CLSCTX_ALL, IID_IFileOperation, (void **)&pfo);
620
621 if (SUCCEEDED(hr)) {
622 /* Flags for deletion:
623 * FOF_ALLOWUNDO: Enables moving file to recycling bin.
624 * FOF_SILENT: Don't show progress dialog box.
625 * FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */
626 hr = pfo->SetOperationFlags(FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING);
627
628 if (SUCCEEDED(hr)) {
629 hr = SHCreateItemFromParsingName(path_16, NULL, IID_IShellItem, (void **)&psi);
630
631 if (SUCCEEDED(hr)) {
632 hr = pfo->DeleteItem(psi, NULL);
633
634 if (SUCCEEDED(hr)) {
635 hr = pfo->PerformOperations();
636
637 if (FAILED(hr)) {
638 *r_error_message = "Failed to prepare delete operation";
639 }
640 }
641 else {
642 *r_error_message = "Failed to prepare delete operation";
643 }
644 psi->Release();
645 }
646 else {
647 *r_error_message = "Failed to parse path";
648 }
649 }
650 else {
651 *r_error_message = "Failed to set operation flags";
652 }
653 pfo->Release();
654 }
655 else {
656 *r_error_message = "Failed to create FileOperation instance";
657 }
658 CoUninitialize();
659 }
660 else {
661 *r_error_message = "Failed to initialize COM";
662 }
663
664 return FAILED(hr);
665}
666
667static bool delete_unique(const char *path, const bool dir)
668{
669 bool err;
670
671 UTF16_ENCODE(path);
672
673 if (dir) {
674 err = !RemoveDirectoryW(path_16);
675 if (err) {
676 printf("Unable to remove directory\n");
677 }
678 }
679 else {
680 err = !DeleteFileW(path_16);
681 if (err) {
682 callLocalErrorCallBack("Unable to delete file");
683 }
684 }
685
686 UTF16_UN_ENCODE(path);
687
688 return err;
689}
690
691static bool delete_recursive(const char *dir)
692{
693 struct direntry *filelist, *fl;
694 bool err = false;
695 uint filelist_num, i;
696
697 i = filelist_num = BLI_filelist_dir_contents(dir, &filelist);
698 fl = filelist;
699 while (i--) {
700 if (FILENAME_IS_CURRPAR(fl->relname)) {
701 /* Skip! */
702 }
703 else if (S_ISDIR(fl->type)) {
704 char path[FILE_MAXDIR];
705
706 /* dir listing produces dir path without trailing slash... */
707 STRNCPY(path, fl->path);
709
710 if (delete_recursive(path)) {
711 err = true;
712 }
713 }
714 else {
715 if (delete_unique(fl->path, false)) {
716 err = true;
717 }
718 }
719 fl++;
720 }
721
722 if (!err && delete_unique(dir, true)) {
723 err = true;
724 }
725
726 BLI_filelist_free(filelist, filelist_num);
727
728 return err;
729}
730
731int BLI_delete(const char *path, bool dir, bool recursive)
732{
733 int err;
734
736
737 /* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
738 BLI_assert(!(dir == false && recursive == true));
739
740 if (recursive) {
741 err = delete_recursive(path);
742 }
743 else {
744 err = delete_unique(path, dir);
745 }
746
747 return err;
748}
749
753int BLI_delete_soft(const char *file, const char **r_error_message)
754{
755 int err;
756
758
759 UTF16_ENCODE(file);
760
761 err = delete_soft(file_16, r_error_message);
762
763 UTF16_UN_ENCODE(file);
764
765 return err;
766}
767
775static const char *path_destination_ensure_filename(const char *path_src,
776 const char *path_dst,
777 char *buf,
778 size_t buf_size)
779{
780 const char *filename_src = BLI_path_basename(path_src);
781 /* Unlikely but possible this has no slashes. */
782 if (filename_src != path_src) {
783 const size_t path_dst_len = strlen(path_dst);
784 /* Check if `path_dst` points to a directory. */
785 if (path_dst_len && BLI_path_slash_is_native_compat(path_dst[path_dst_len - 1])) {
786 size_t buf_size_needed = path_dst_len + strlen(filename_src) + 1;
787 char *path_dst_with_filename = (buf_size_needed <= buf_size) ?
788 buf :
789 MEM_cnew_array<char>(buf_size_needed, __func__);
790 BLI_string_join(path_dst_with_filename, buf_size_needed, path_dst, filename_src);
791 return path_dst_with_filename;
792 }
793 }
794 return path_dst;
795}
796
797int BLI_path_move(const char *path_src, const char *path_dst)
798{
799 char path_dst_buf[FILE_MAX_STATIC_BUF];
800 const char *path_dst_with_filename = path_destination_ensure_filename(
801 path_src, path_dst, path_dst_buf, sizeof(path_dst_buf));
802
803 int err;
804
805 UTF16_ENCODE(path_src);
806 UTF16_ENCODE(path_dst_with_filename);
807 err = !MoveFileW(path_src_16, path_dst_with_filename_16);
808 UTF16_UN_ENCODE(path_dst_with_filename);
809 UTF16_UN_ENCODE(path_src);
810
811 if (err) {
812 callLocalErrorCallBack("Unable to move file");
813 printf(" Move from '%s' to '%s' failed\n", path_src, path_dst_with_filename);
814 }
815
816 if (!ELEM(path_dst_with_filename, path_dst_buf, path_dst)) {
817 MEM_freeN((void *)path_dst_with_filename);
818 }
819
820 return err;
821}
822
823int BLI_copy(const char *path_src, const char *path_dst)
824{
825 char path_dst_buf[FILE_MAX_STATIC_BUF];
826 const char *path_dst_with_filename = path_destination_ensure_filename(
827 path_src, path_dst, path_dst_buf, sizeof(path_dst_buf));
828 int err;
829
830 UTF16_ENCODE(path_src);
831 UTF16_ENCODE(path_dst_with_filename);
832 err = !CopyFileW(path_src_16, path_dst_with_filename_16, false);
833 UTF16_UN_ENCODE(path_dst_with_filename);
834 UTF16_UN_ENCODE(path_src);
835
836 if (err) {
837 callLocalErrorCallBack("Unable to copy file!");
838 printf(" Copy from '%s' to '%s' failed\n", path_src, path_dst_with_filename);
839 }
840
841 if (!ELEM(path_dst_with_filename, path_dst_buf, path_dst)) {
842 MEM_freeN((void *)path_dst_with_filename);
843 }
844
845 return err;
846}
847
848# if 0
849int BLI_create_symlink(const char *path_src, const char *path_dst)
850{
851 /* See patch from #30870, should this ever become needed. */
852 callLocalErrorCallBack("Linking files is unsupported on Windows");
853 (void)path_src;
854 (void)path_dst;
855 return 1;
856}
857# endif
858
859#else /* The UNIX world */
860
861/* results from recursive_operation and its callbacks */
862enum {
863 /* operation succeeded */
865
866 /* operation requested not to perform recursive digging for current path */
868
869 /* error occurred in callback and recursive walking should stop immediately */
871};
872
873typedef int (*RecursiveOp_Callback)(const char *from, const char *to);
874
875[[maybe_unused]] static bool path_has_trailing_slash(const char *path)
876{
877 const int path_len = strlen(path);
878 if (path_len == 0) {
879 return false;
880 }
881 return BLI_path_slash_is_native_compat(path[path_len - 1]);
882}
883
884static size_t path_len_no_trailing_slash(const char *path)
885{
886 int len = strlen(path);
887 int len_found = len;
888 while (len) {
889 len--;
891 break;
892 }
893 len_found = len;
894 }
895 return len_found;
896}
897
898/* -------------------------------------------------------------------- */
905struct StrBuf {
906 char *str;
907 size_t str_len;
909};
910
911static void strbuf_init(StrBuf *buf, const char *str, size_t str_len, size_t str_len_alloc)
912{
913 str_len_alloc = std::max(str_len + 1, str_len_alloc);
914 buf->str = static_cast<char *>(malloc(str_len_alloc));
915 memcpy(buf->str, str, str_len);
916 buf->str[str_len] = '\0';
917 buf->str_len = str_len;
918 buf->str_len_alloc = str_len_alloc;
919}
920
921static void strbuf_free(StrBuf *buf)
922{
923 free(buf->str);
924}
925
929static void strbuf_append_path(StrBuf *buf, const char *filename)
930{
931 BLI_assert(strlen(buf->str) == buf->str_len);
933 bool has_slash = (buf->str_len > 0 &&
935 const size_t filename_len = strlen(filename);
936 const size_t len = buf->str_len + (has_slash ? 0 : 1) + filename_len;
937
938 if (buf->str_len_alloc < len) {
939 buf->str = static_cast<char *>(realloc(static_cast<void *>(buf->str), len + 1));
940 buf->str_len_alloc = len;
941 }
942 if (has_slash == false) {
943 buf->str[buf->str_len++] = SEP;
944 }
945 memcpy(buf->str + buf->str_len, filename, filename_len + 1);
946 buf->str_len += filename_len;
947 BLI_assert(buf->str_len <= buf->str_len_alloc);
948}
949
950static void strbuf_trim(StrBuf *buf, size_t len)
951{
952 BLI_assert(len <= buf->str_len);
953 buf->str_len = len;
954 buf->str[len] = '\0';
955}
956
960 StrBuf *dst_buf,
961 RecursiveOp_Callback callback_dir_pre,
962 RecursiveOp_Callback callback_file,
963 RecursiveOp_Callback callback_dir_post)
964{
965 /* NOTE(@ideasman42): This function must *not* use any `MEM_*` functions
966 * as it's used to purge temporary files on when the processed is aborted,
967 * in this case the `MEM_*` state may have already been freed (memory usage tracking for e.g.)
968 * causing freed memory access, potentially crashing. This constraint doesn't apply to the
969 * callbacks themselves - unless they might also be called when aborting. */
970 struct stat st;
971 int ret = 0;
972
973 dirent **dirlist = nullptr;
974 int dirlist_num = 0;
975
976 /* Check there's no trailing slash in file paths. */
978 BLI_assert(!(dst_buf && path_has_trailing_slash(dst_buf->str)));
979
980 do { /* once */
981
982 ret = lstat(src_buf->str, &st);
983 if (ret < 0) {
984 /* source wasn't found, nothing to operate with */
985 break;
986 }
987
988 if (!S_ISDIR(st.st_mode)) {
989 /* source isn't a directory, can't do recursive walking for it,
990 * so just call file callback and leave */
991 if (callback_file != nullptr) {
992 ret = callback_file(src_buf->str, dst_buf ? dst_buf->str : nullptr);
994 ret = -1;
995 }
996 }
997 break;
998 }
999
1000 dirlist_num = scandir(src_buf->str, &dirlist, nullptr, alphasort);
1001 if (dirlist_num < 0) {
1002 /* error opening directory for listing */
1003 perror("scandir");
1004 ret = -1;
1005 break;
1006 }
1007
1008 if (callback_dir_pre != nullptr) {
1009 ret = callback_dir_pre(src_buf->str, dst_buf ? dst_buf->str : nullptr);
1012 /* callback requested not to perform recursive walking, not an error */
1013 ret = 0;
1014 }
1015 else {
1016 ret = -1;
1017 }
1018 break;
1019 }
1020 }
1021 const size_t src_len = src_buf->str_len;
1022 const size_t dst_len = dst_buf ? dst_buf->str_len : 0;
1023
1024 for (int i = 0; i < dirlist_num; i++) {
1025 const dirent *const dirent = dirlist[i];
1026
1028 continue;
1029 }
1030
1031 strbuf_append_path(src_buf, dirent->d_name);
1032 if (dst_buf) {
1033 strbuf_append_path(dst_buf, dirent->d_name);
1034 }
1035
1036 bool is_dir;
1037
1038# ifdef __HAIKU__
1039 {
1040 struct stat st_dir;
1041 lstat(src_buf->str, &st_dir);
1042 is_dir = S_ISDIR(st_dir.st_mode);
1043 }
1044# else
1045 is_dir = (dirent->d_type == DT_DIR);
1046# endif
1047
1048 if (is_dir) {
1049 /* Recurse into sub-directories. */
1051 src_buf, dst_buf, callback_dir_pre, callback_file, callback_dir_post);
1052 }
1053 else if (callback_file != nullptr) {
1054 ret = callback_file(src_buf->str, dst_buf ? dst_buf->str : nullptr);
1056 ret = -1;
1057 }
1058 }
1059 strbuf_trim(src_buf, src_len);
1060 if (dst_buf) {
1061 strbuf_trim(dst_buf, dst_len);
1062 }
1063
1064 if (ret != 0) {
1065 break;
1066 }
1067 }
1068 if (ret != 0) {
1069 break;
1070 }
1071
1072 if (callback_dir_post != nullptr) {
1073 ret = callback_dir_post(src_buf->str, dst_buf ? dst_buf->str : nullptr);
1075 ret = -1;
1076 }
1077 }
1078 } while (false);
1079
1080 if (dirlist != nullptr) {
1081 for (int i = 0; i < dirlist_num; i++) {
1082 free(dirlist[i]);
1083 }
1084 free(dirlist);
1085 }
1086
1087 return ret;
1088}
1089
1106static int recursive_operation(const char *path_src,
1107 const char *path_dst,
1108 RecursiveOp_Callback callback_dir_pre,
1109 RecursiveOp_Callback callback_file,
1110 RecursiveOp_Callback callback_dir_post)
1111
1112{
1113 StrBuf src_buf_stack = {};
1114 StrBuf dst_buf_stack = {};
1115 StrBuf *src_buf = &src_buf_stack;
1116 StrBuf *dst_buf = path_dst ? &dst_buf_stack : nullptr;
1117# ifndef NDEBUG
1118 /* Don't over allocate to ensure resizing works as expected. */
1119 const size_t str_len_over_alloc = 0;
1120# else
1121 const size_t str_len_over_alloc = FILE_MAX;
1122# endif
1123
1124 strbuf_init(src_buf, path_src, path_len_no_trailing_slash(path_src), str_len_over_alloc);
1125 if (dst_buf) {
1126 strbuf_init(dst_buf, path_dst, path_len_no_trailing_slash(path_dst), str_len_over_alloc);
1127 }
1128
1129 const int result = recursive_operation_impl(
1130 src_buf, dst_buf, callback_dir_pre, callback_file, callback_dir_post);
1131
1132 strbuf_free(src_buf);
1133 if (dst_buf) {
1134 strbuf_free(dst_buf);
1135 }
1136 return result;
1137}
1138
1139static int delete_callback_post(const char *from, const char * /*to*/)
1140{
1141 if (rmdir(from)) {
1142 perror("rmdir");
1143
1145 }
1146
1148}
1149
1150static int delete_single_file(const char *from, const char * /*to*/)
1151{
1152 if (unlink(from)) {
1153 perror("unlink");
1154
1156 }
1157
1159}
1160
1161FILE *BLI_fopen(const char *filepath, const char *mode)
1162{
1163 BLI_assert(!BLI_path_is_rel(filepath));
1164
1165 return fopen(filepath, mode);
1166}
1167
1168void *BLI_gzopen(const char *filepath, const char *mode)
1169{
1170 BLI_assert(!BLI_path_is_rel(filepath));
1171
1172 return gzopen(filepath, mode);
1173}
1174
1175int BLI_open(const char *filepath, int oflag, int pmode)
1176{
1177 BLI_assert(!BLI_path_is_rel(filepath));
1178
1179 return open(filepath, oflag, pmode);
1180}
1181
1182int BLI_access(const char *filepath, int mode)
1183{
1184 BLI_assert(!BLI_path_is_rel(filepath));
1185
1186 return access(filepath, mode);
1187}
1188
1189int BLI_delete(const char *path, bool dir, bool recursive)
1190{
1192 /* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
1193 BLI_assert(!(dir == false && recursive == true));
1194
1195 if (recursive) {
1196 return recursive_operation(path, nullptr, nullptr, delete_single_file, delete_callback_post);
1197 }
1198 if (dir) {
1199 return rmdir(path);
1200 }
1201 return remove(path);
1202}
1203
1204/* Apple version is defined in fileops_apple.mm */
1205# ifndef __APPLE__
1206int BLI_delete_soft(const char *filepath, const char **r_error_message)
1207{
1208 BLI_assert(!BLI_path_is_rel(filepath));
1209
1210 const char *args[5];
1211 const char *process_failed;
1212
1213 /* May contain `:` delimiter characters according to version 1.5 of the spec:
1214 * https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html */
1215 const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
1216 const char *xdg_session_desktop = getenv("XDG_SESSION_DESKTOP");
1217
1218 if ((xdg_current_desktop && BLI_string_elem_split_by_delim(xdg_current_desktop, ':', "KDE")) ||
1219 (xdg_session_desktop && STREQ(xdg_session_desktop, "KDE")))
1220 {
1221 args[0] = "kioclient5";
1222 args[1] = "move";
1223 args[2] = filepath;
1224 args[3] = "trash:/";
1225 args[4] = nullptr;
1226 process_failed = "kioclient5 reported failure";
1227 }
1228 else {
1229 args[0] = "gio";
1230 args[1] = "trash";
1231 args[2] = filepath;
1232 args[3] = nullptr;
1233 process_failed = "gio reported failure";
1234 }
1235
1236 /* Restore when there are no errors. */
1237 const int errno_prev = errno;
1238 errno = 0;
1239
1240 int pid = fork();
1241 if (UNLIKELY(pid == -1)) {
1242 *r_error_message = errno ? strerror(errno) : "unable to fork process";
1243 return -1;
1244 }
1245
1246 if (pid == 0) {
1247 /* Child process. */
1248 execvp(args[0], (char **)args);
1249 /* This should only be reached if `execvp` fails and stack isn't replaced. */
1250
1251 /* Ensure outputs are flushed as `_exit` doesn't flush. */
1252 fflush(stdout);
1253 fflush(stderr);
1254
1255 /* Use `_exit` instead of `exit` so Blender's `atexit` cleanup functions don't run. */
1256 _exit(errno);
1258 return -1;
1259 }
1260
1261 /* Parent process. */
1262 int wstatus = 0;
1263 waitpid(pid, &wstatus, 0);
1264
1265 int result = 0; /* Success. */
1266 if (WIFEXITED(wstatus)) {
1267 const int errno_child = WEXITSTATUS(wstatus);
1268 if (errno_child) {
1269 *r_error_message = process_failed;
1270 result = -1;
1271
1272 /* Forward to the error so the caller may set the message. */
1273 errno = errno_child;
1274 }
1275 }
1276 else {
1277 *r_error_message =
1278 "Blender may not support moving files or directories to trash on your system.";
1279 result = -1;
1280 }
1281
1282 if (result == 0) {
1283 /* Only overwrite the value if there was an error. */
1284 errno = errno_prev;
1285 }
1286
1287 return result;
1288}
1289# endif
1290
1294static bool check_the_same(const char *path_a, const char *path_b)
1295{
1296 struct stat st_a, st_b;
1297
1298 if (lstat(path_a, &st_a)) {
1299 return false;
1300 }
1301
1302 if (lstat(path_b, &st_b)) {
1303 return false;
1304 }
1305
1306 return st_a.st_dev == st_b.st_dev && st_a.st_ino == st_b.st_ino;
1307}
1308
1312static int set_permissions(const char *filepath, const struct stat *st)
1313{
1314 if (chown(filepath, st->st_uid, st->st_gid)) {
1315 perror("chown");
1316 return -1;
1317 }
1318
1319 if (chmod(filepath, st->st_mode)) {
1320 perror("chmod");
1321 return -1;
1322 }
1323
1324 return 0;
1325}
1326
1327/* pre-recursive callback for copying operation
1328 * creates a destination directory where all source content fill be copied to */
1329static int copy_callback_pre(const char *from, const char *to)
1330{
1331 struct stat st;
1332
1333 if (check_the_same(from, to)) {
1334 fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
1336 }
1337
1338 if (lstat(from, &st)) {
1339 perror("stat");
1341 }
1342
1343 /* Create a directory. */
1344 if (mkdir(to, st.st_mode)) {
1345 perror("mkdir");
1347 }
1348
1349 /* Set proper owner and group on new directory. */
1350 if (chown(to, st.st_uid, st.st_gid)) {
1351 perror("chown");
1353 }
1354
1356}
1357
1358static int copy_single_file(const char *from, const char *to)
1359{
1360 FILE *from_stream, *to_stream;
1361 struct stat st;
1362 char buf[4096];
1363 size_t len;
1364
1365 if (check_the_same(from, to)) {
1366 fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
1368 }
1369
1370 if (lstat(from, &st)) {
1371 perror("lstat");
1373 }
1374
1375 if (S_ISLNK(st.st_mode)) {
1376 /* Symbolic links should be copied in special way. */
1377 char *link_buffer;
1378 int need_free;
1379 int64_t link_len;
1380
1381 /* Get large enough buffer to read link content. */
1382 if ((st.st_size + 1) < sizeof(buf)) {
1383 link_buffer = buf;
1384 need_free = 0;
1385 }
1386 else {
1387 link_buffer = MEM_cnew_array<char>(st.st_size + 2, "copy_single_file link_buffer");
1388 need_free = 1;
1389 }
1390
1391 link_len = readlink(from, link_buffer, st.st_size + 1);
1392 if (link_len < 0) {
1393 perror("readlink");
1394
1395 if (need_free) {
1396 MEM_freeN(link_buffer);
1397 }
1398
1400 }
1401
1402 link_buffer[link_len] = '\0';
1403
1404 if (symlink(link_buffer, to)) {
1405 perror("symlink");
1406 if (need_free) {
1407 MEM_freeN(link_buffer);
1408 }
1410 }
1411
1412 if (need_free) {
1413 MEM_freeN(link_buffer);
1414 }
1415
1417 }
1418 if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
1419 /* Copy special type of file. */
1420 if (mknod(to, st.st_mode, st.st_rdev)) {
1421 perror("mknod");
1423 }
1424
1425 if (set_permissions(to, &st)) {
1427 }
1428
1430 }
1431 if (!S_ISREG(st.st_mode)) {
1432 fprintf(stderr, "Copying of this kind of files isn't supported yet\n");
1434 }
1435
1436 from_stream = fopen(from, "rb");
1437 if (!from_stream) {
1438 perror("fopen");
1440 }
1441
1442 to_stream = fopen(to, "wb");
1443 if (!to_stream) {
1444 perror("fopen");
1445 fclose(from_stream);
1447 }
1448
1449 while ((len = fread(buf, 1, sizeof(buf), from_stream)) > 0) {
1450 fwrite(buf, 1, len, to_stream);
1451 }
1452
1453 fclose(to_stream);
1454 fclose(from_stream);
1455
1456 if (set_permissions(to, &st)) {
1458 }
1459
1461}
1462
1463static int move_callback_pre(const char *from, const char *to)
1464{
1465 int ret = rename(from, to);
1466
1467 if (ret) {
1468 return copy_callback_pre(from, to);
1469 }
1470
1472}
1473
1474static int move_single_file(const char *from, const char *to)
1475{
1476 int ret = rename(from, to);
1477
1478 if (ret) {
1479 return copy_single_file(from, to);
1480 }
1481
1483}
1484
1485int BLI_path_move(const char *path_src, const char *path_dst)
1486{
1487 int ret = recursive_operation(path_src, path_dst, move_callback_pre, move_single_file, nullptr);
1488
1489 if (ret && ret != -1) {
1490 return recursive_operation(
1491 path_src, nullptr, nullptr, delete_single_file, delete_callback_post);
1492 }
1493
1494 return ret;
1495}
1496
1497static const char *path_destination_ensure_filename(const char *path_src,
1498 const char *path_dst,
1499 char *buf,
1500 size_t buf_size)
1501{
1502 if (BLI_is_dir(path_dst)) {
1503 char *path_src_no_slash = BLI_strdup(path_src);
1504 BLI_path_slash_rstrip(path_src_no_slash);
1505 const char *filename_src = BLI_path_basename(path_src_no_slash);
1506 if (filename_src != path_src_no_slash) {
1507 const size_t buf_size_needed = strlen(path_dst) + 1 + strlen(filename_src) + 1;
1508 char *path_dst_with_filename = (buf_size_needed <= buf_size) ?
1509 buf :
1510 MEM_cnew_array<char>(buf_size_needed, __func__);
1511 BLI_path_join(path_dst_with_filename, buf_size_needed, path_dst, filename_src);
1512 path_dst = path_dst_with_filename;
1513 }
1514 MEM_freeN(path_src_no_slash);
1515 }
1516 return path_dst;
1517}
1518
1519int BLI_copy(const char *path_src, const char *path_dst)
1520{
1521 char path_dst_buf[FILE_MAX_STATIC_BUF];
1522 const char *path_dst_with_filename = path_destination_ensure_filename(
1523 path_src, path_dst, path_dst_buf, sizeof(path_dst_buf));
1524 int ret;
1525
1527 path_src, path_dst_with_filename, copy_callback_pre, copy_single_file, nullptr);
1528
1529 if (!ELEM(path_dst_with_filename, path_dst_buf, path_dst)) {
1530 MEM_freeN((void *)path_dst_with_filename);
1531 }
1532
1533 return ret;
1534}
1535
1536# if 0
1537int BLI_create_symlink(const char *path_src, const char *path_dst)
1538{
1539 return symlink(path_dst, path_src);
1540}
1541# endif
1542
1543#endif
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
FileExternalOperation
@ FILE_EXTERNAL_OPERATION_FOLDER_OPEN
@ FILE_EXTERNAL_OPERATION_FOLDER_CMD
@ FILE_EXTERNAL_OPERATION_OPEN
@ FILE_EXTERNAL_OPERATION_PRINT
@ FILE_EXTERNAL_OPERATION_INSTALL
@ FILE_EXTERNAL_OPERATION_PLAY
@ FILE_EXTERNAL_OPERATION_BROWSE
@ FILE_EXTERNAL_OPERATION_PREVIEW
@ FILE_EXTERNAL_OPERATION_RUNAS
@ FILE_EXTERNAL_OPERATION_FOLDER_FIND
@ FILE_EXTERNAL_OPERATION_NEW
@ FILE_EXTERNAL_OPERATION_EDIT
@ FILE_EXTERNAL_OPERATION_PROPERTIES
@ FILE_EXTERNAL_OPERATION_FIND
@ FILE_EXTERNAL_OPERATION_SHOW
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:433
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
Some types for dealing with directories.
void BLI_kdtree_nd_ free(KDTree *tree)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAX
#define BLI_path_join(...)
#define FILENAME_IS_CURRPAR(_n)
bool BLI_path_is_rel(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define SEP
BLI_INLINE bool BLI_path_slash_is_native_compat(const char ch)
const char * BLI_path_parent_dir_end(const char *path, size_t path_len) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
bool BLI_path_is_win32_drive_only(const char *path)
void BLI_path_slash_rstrip(char *path) ATTR_NONNULL(1)
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
#define FILE_MAXDIR
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
bool bool int bool BLI_string_elem_split_by_delim(const char *haystack, const char delim, const char *needle) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
#define BLI_string_join(...)
unsigned int uint
#define UNUSED_VARS(...)
#define UNLIKELY(x)
#define ELEM(...)
#define STREQ(a, b)
Compatibility-like things for windows.
bool BLI_windows_external_operation_supported(const char *filepath, const char *operation)
#define S_ISDIR(x)
const char * dirname(char *path)
bool BLI_windows_external_operation_execute(const char *filepath, const char *operation)
#define S_ISREG(x)
Read Guarded memory(de)allocation.
ATTR_WARN_UNUSED_RESULT const BMFlagLayer const short oflag
#define printf
StackEntry * from
#define NULL
int len
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define str(s)
#define FILE_MAX_STATIC_BUF
Definition fileops_c.cc:53
static int recursive_operation_impl(StrBuf *src_buf, StrBuf *dst_buf, RecursiveOp_Callback callback_dir_pre, RecursiveOp_Callback callback_file, RecursiveOp_Callback callback_dir_post)
Definition fileops_c.cc:959
@ RecursiveOp_Callback_StopRecurs
Definition fileops_c.cc:867
@ RecursiveOp_Callback_OK
Definition fileops_c.cc:864
@ RecursiveOp_Callback_Error
Definition fileops_c.cc:870
static size_t path_len_no_trailing_slash(const char *path)
Definition fileops_c.cc:884
static void strbuf_append_path(StrBuf *buf, const char *filename)
Definition fileops_c.cc:929
int BLI_access(const char *filepath, int mode)
static bool check_the_same(const char *path_a, const char *path_b)
static int move_callback_pre(const char *from, const char *to)
static bool path_has_trailing_slash(const char *path)
Definition fileops_c.cc:875
size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
Definition fileops_c.cc:220
int(* RecursiveOp_Callback)(const char *from, const char *to)
Definition fileops_c.cc:873
bool BLI_file_magic_is_gzip(const char header[4])
Definition fileops_c.cc:257
static int delete_callback_post(const char *from, const char *)
FILE * BLI_fopen(const char *filepath, const char *mode)
bool BLI_file_external_operation_supported(const char *filepath, FileExternalOperation operation)
Definition fileops_c.cc:146
static void strbuf_free(StrBuf *buf)
Definition fileops_c.cc:921
bool BLI_dir_create_recursive(const char *dirname)
Definition fileops_c.cc:391
int BLI_open(const char *filepath, int oflag, int pmode)
static const char * path_destination_ensure_filename(const char *path_src, const char *path_dst, char *buf, size_t buf_size)
bool BLI_file_is_writable(const char *filepath)
Definition fileops_c.cc:291
bool BLI_file_magic_is_zstd(const char header[4])
Definition fileops_c.cc:264
static int copy_single_file(const char *from, const char *to)
bool BLI_file_ensure_parent_dir_exists(const char *filepath)
Definition fileops_c.cc:429
static int recursive_operation(const char *path_src, const char *path_dst, RecursiveOp_Callback callback_dir_pre, RecursiveOp_Callback callback_file, RecursiveOp_Callback callback_dir_post)
int BLI_copy(const char *path_src, const char *path_dst)
bool BLI_file_touch(const char *filepath)
Definition fileops_c.cc:316
static int move_single_file(const char *from, const char *to)
int BLI_delete_soft(const char *filepath, const char **r_error_message)
int BLI_delete(const char *path, bool dir, bool recursive)
static int delete_single_file(const char *from, const char *)
size_t BLI_file_zstd_from_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset, int compression_level)
Definition fileops_c.cc:173
static void strbuf_init(StrBuf *buf, const char *str, size_t str_len, size_t str_len_alloc)
Definition fileops_c.cc:911
static int set_permissions(const char *filepath, const struct stat *st)
void * BLI_gzopen(const char *filepath, const char *mode)
int BLI_path_move(const char *path_src, const char *path_dst)
int BLI_rename_overwrite(const char *from, const char *to)
Definition fileops_c.cc:505
static bool dir_create_recursive(char *dirname, int len)
Definition fileops_c.cc:344
bool BLI_file_external_operation_execute(const char *filepath, FileExternalOperation operation)
Definition fileops_c.cc:157
static void strbuf_trim(StrBuf *buf, size_t len)
Definition fileops_c.cc:950
int64_t BLI_read(int fd, void *buf, size_t nbytes)
Definition fileops_c.cc:96
int BLI_rename(const char *from, const char *to)
Definition fileops_c.cc:438
static int copy_callback_pre(const char *from, const char *to)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
return ret
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
size_t str_len_alloc
Definition fileops_c.cc:908
char * str
Definition fileops_c.cc:906
size_t str_len
Definition fileops_c.cc:907
char * d_name
const char * relname
const char * path
static int magic(const Tex *tex, const float texvec[3], TexResult *texres)
int urename(const char *oldname, const char *newname, const bool do_replace)
FILE * ufopen(const char *filename, const char *mode)
int umkdir(const char *pathname)
int uopen(const char *filename, int oflag, int pmode)
int uaccess(const char *filename, int mode)
#define UTF16_ENCODE(in8str)
Definition utfconv.hh:80
#define UTF16_UN_ENCODE(in8str)
Definition utfconv.hh:84