Blender V5.0
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
8
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_string_utils.hh"
25# include "BLI_winstuff.h"
26# include "utf_winfunc.hh"
27# include "utfconv.hh"
28# include <io.h>
29# include <shellapi.h>
30# include <shobjidl.h>
31# include <windows.h>
32#else
33# if defined(__APPLE__)
34# include <CoreFoundation/CoreFoundation.h>
35# include <objc/message.h>
36# include <objc/runtime.h>
37# endif
38# include <dirent.h>
39# include <sys/param.h>
40# include <sys/wait.h>
41# include <unistd.h>
42#endif
43
44#include "MEM_guardedalloc.h"
45
46#include "BLI_fileops.h"
47#include "BLI_path_utils.hh"
48#include "BLI_string.h"
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
354static bool dir_create_recursive(const char *dirname, const int len)
355{
356 BLI_assert(strlen(dirname) == len);
357 /* Caller must ensure the path doesn't have trailing slashes. */
359 "Paths must not end with a slash!");
361 STREQ(dirname + (len - 2), "..")),
362 "Paths containing \"..\" components must be normalized first!");
363
364 bool ret = true;
365 char *dirname_parent_end = (char *)BLI_path_parent_dir_end(dirname, len);
366 if (dirname_parent_end) {
367 const char dirname_parent_end_value = *dirname_parent_end;
368 *dirname_parent_end = '\0';
369#ifdef WIN32
370 /* Check special case `c:\foo`, don't try create `c:`, harmless but unnecessary. */
372#endif
373 {
374 const int mode = BLI_exists(dirname);
375 if (mode != 0) {
376 if (!S_ISDIR(mode)) {
377 ret = false;
378 }
379 }
380 else if (!dir_create_recursive(dirname, dirname_parent_end - dirname)) {
381 ret = false;
382 }
383 }
384 *dirname_parent_end = dirname_parent_end_value;
385 }
386 if (ret) {
387 /* Ignore errors when the directory was created (probably by another process) in between the
388 * earlier call to BLI_exists() and this call to mkdir. Since this function only creates a
389 * directory if it doesn't exist yet, this is actually not seen as an error, even though
390 * mkdir() failed. */
391#ifdef WIN32
392 if (umkdir(dirname) == -1) {
393 if (GetLastError() == ERROR_ALREADY_EXISTS && BLI_is_dir(dirname)) {
394 return true;
395 }
396
397 /* Any other error should bubble up as an actual error. */
398 ret = false;
399 }
400#else
401 if (mkdir(dirname, 0777) != 0) {
402 if (errno == EEXIST && BLI_is_dir(dirname)) {
403 return true;
404 }
405
406 /* Any other error should bubble up as an actual error. */
407 ret = false;
408 }
409#endif
410 }
411 return ret;
412}
413
415{
416 const int mode = BLI_exists(dirname);
417 if (mode != 0) {
418 /* The file exists, either it's a directory (ok), or not,
419 * in which case this function can't do anything useful
420 * (the caller could remove it and re-run this function). */
421 return S_ISDIR(mode) ? true : false;
422 }
423
424 char dirname_static_buf[FILE_MAX];
425 char *dirname_mut = dirname_static_buf;
426
427 size_t len = strlen(dirname);
428 if (len >= sizeof(dirname_static_buf)) {
429 dirname_mut = MEM_calloc_arrayN<char>(len + 1, __func__);
430 }
431 memcpy(dirname_mut, dirname, len + 1);
432
433 /* Strip trailing chars, important for first entering #dir_create_recursive
434 * when then ensures this is the case for recursive calls. */
435 while ((len > 0) && BLI_path_slash_is_native_compat(dirname_mut[len - 1])) {
436 len--;
437 }
438 dirname_mut[len] = '\0';
439
440 const bool ret = (len > 0) && dir_create_recursive(dirname_mut, len);
441
442 /* Ensure the string was properly restored. */
443 BLI_assert(memcmp(dirname, dirname_mut, len) == 0);
444
445 if (dirname_mut != dirname_static_buf) {
446 MEM_freeN(dirname_mut);
447 }
448
449 return ret;
450}
451
452bool BLI_file_ensure_parent_dir_exists(const char *filepath)
453{
454 char di[FILE_MAX];
455 BLI_path_split_dir_part(filepath, di, sizeof(di));
456
457 /* Make if the dir doesn't exist. */
458 return BLI_dir_create_recursive(di);
459}
460
461int BLI_rename(const char *from, const char *to)
462{
463 if (!BLI_exists(from)) {
464 return 1;
465 }
466
467 /* NOTE(@ideasman42): there are no checks that `from` & `to` *aren't* the same file.
468 * It's up to the caller to ensure this. In practice these paths are often generated
469 * and known to be different rather than arbitrary user input.
470 * In the case of arbitrary paths (renaming a file in the file-selector for example),
471 * the caller must ensure file renaming doesn't cause user data loss.
472 *
473 * Support for checking the files aren't the same could be added, however path comparison
474 * alone is *not* a guarantee the files are different (given the possibility of accessing
475 * the same file through different paths via symbolic-links), we could instead support a
476 * version of Python's `os.path.samefile(..)` which compares the I-node & device.
477 * In this particular case we would not want to follow symbolic-links as well.
478 * Since this functionality isn't required at the moment, leave this as-is.
479 * Noting it as a potential improvement. */
480
481 /* NOTE: To avoid the concurrency 'time of check/time of use' (TOC/TOU) issue, this code attempts
482 * to use available solutions for an 'atomic' (file-system wise) rename operation, instead of
483 * first checking for an existing `to` target path, and then doing the rename operation if it
484 * does not exists at the time of check.
485 *
486 * Windows (through `MoveFileExW`) by default does not allow replacing an existing path. It is
487 * however not clear whether its API is exposed to the TOC/TOU issue or not.
488 *
489 * On Linux or OSX, to keep operations atomic, special non-standardized variants of `rename` must
490 * be used, depending on the OS. Note that there may also be failure due to file system not
491 * supporting this operation, although in practice this should not be a problem in modern
492 * systems.
493 * - https://man7.org/linux/man-pages/man2/rename.2.html
494 * - https://www.unix.com/man-page/mojave/2/renameatx_np/
495 *
496 * BSD systems do not have any such thing currently, and are therefore exposed to the TOC/TOU
497 * issue. */
498
499#ifdef WIN32
500 return urename(from, to, false);
501#else
502# if defined(__APPLE__)
503 int ret = renamex_np(from, to, RENAME_EXCL);
504 if (!(ret < 0 && errno == ENOTSUP)) {
505 return ret;
506 }
507# endif
508
509# if defined(__GLIBC_PREREQ)
510# if __GLIBC_PREREQ(2, 28)
511 /* Most common Linux case, use `RENAME_NOREPLACE` when available. */
512 int ret = renameat2(AT_FDCWD, from, AT_FDCWD, to, RENAME_NOREPLACE);
513 if (!(ret < 0 && errno == EINVAL)) {
514 return ret;
515 }
516# endif /* __GLIBC_PREREQ(2, 28) */
517# endif /* __GLIBC_PREREQ */
518 /* A naive non-atomic implementation, which is used for OS where atomic rename is not supported
519 * at all, or not implemented for specific file systems (for example NFS, Samba, exFAT, NTFS,
520 * etc). For those see #116049, #119966. */
521 if (BLI_exists(to)) {
522 return 1;
523 }
524 return rename(from, to);
525#endif /* !defined(WIN32) */
526}
527
528int BLI_rename_overwrite(const char *from, const char *to)
529{
530 if (!BLI_exists(from)) {
531 return 1;
532 }
533
534#ifdef WIN32
535 /* `urename` from `utfconv` intern utils uses `MoveFileExW`, which allows to replace an existing
536 * file, but not an existing directory, even if empty. This will only delete empty directories.
537 */
538 if (BLI_is_dir(to)) {
539 if (BLI_delete(to, true, false)) {
540 return 1;
541 }
542 }
543 return urename(from, to, true);
544#else
545 return rename(from, to);
546#endif
547}
548
549#ifdef WIN32
550
551static void callLocalErrorCallBack(const char *err)
552{
553 printf("%s\n", err);
554}
555
556FILE *BLI_fopen(const char *filepath, const char *mode)
557{
558 BLI_assert(!BLI_path_is_rel(filepath));
559
560 return ufopen(filepath, mode);
561}
562
563void BLI_get_short_name(char short_name[256], const char *filepath)
564{
565 wchar_t short_name_16[256];
566 int i = 0;
567
568 UTF16_ENCODE(filepath);
569
570 GetShortPathNameW(filepath_16, short_name_16, 256);
571
572 for (i = 0; i < 256; i++) {
573 short_name[i] = char(short_name_16[i]);
574 }
575
576 UTF16_UN_ENCODE(filepath);
577}
578
579void *BLI_gzopen(const char *filepath, const char *mode)
580{
581 gzFile gzfile;
582
583 BLI_assert(!BLI_path_is_rel(filepath));
584
585 /* XXX: Creates file before transcribing the path. */
586 if (mode[0] == 'w') {
587 FILE *file = ufopen(filepath, "a");
588 if (file == nullptr) {
589 /* File couldn't be opened, e.g. due to permission error. */
590 return nullptr;
591 }
592 fclose(file);
593 }
594
595 /* Temporary `#if` until we update all libraries to 1.2.7 for correct wide char path handling. */
596# if ZLIB_VERNUM >= 0x1270
597 UTF16_ENCODE(filepath);
598
599 gzfile = gzopen_w(filepath_16, mode);
600
601 UTF16_UN_ENCODE(filepath);
602# else
603 {
604 char short_name[256];
605 BLI_get_short_name(short_name, filepath);
606 gzfile = gzopen(short_name, mode);
607 }
608# endif
609
610 return gzfile;
611}
612
613int BLI_open(const char *filepath, int oflag, int pmode)
614{
615 BLI_assert(!BLI_path_is_rel(filepath));
616
617 return uopen(filepath, oflag, pmode);
618}
619
620int BLI_access(const char *filepath, int mode)
621{
622 BLI_assert(!BLI_path_is_rel(filepath));
623
624 return uaccess(filepath, mode);
625}
626
627static bool delete_soft(const wchar_t *path_16, const char **r_error_message)
628{
629 /* Deletes file or directory to recycling bin. The latter moves all contained files and
630 * directories recursively to the recycling bin as well. */
631 IFileOperation *pfo;
632 IShellItem *psi;
633
634 HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
635
636 if (SUCCEEDED(hr)) {
637 /* This is also the case when COM was previously initialized and CoInitializeEx returns
638 * S_FALSE, which is not an error. Both HRESULT values S_OK and S_FALSE indicate success. */
639
640 hr = CoCreateInstance(
641 CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_IFileOperation, (void **)&pfo);
642
643 if (SUCCEEDED(hr)) {
644 /* Flags for deletion:
645 * FOF_ALLOWUNDO: Enables moving file to recycling bin.
646 * FOF_SILENT: Don't show progress dialog box.
647 * FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */
648 hr = pfo->SetOperationFlags(FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING);
649
650 if (SUCCEEDED(hr)) {
651 hr = SHCreateItemFromParsingName(path_16, nullptr, IID_IShellItem, (void **)&psi);
652
653 if (SUCCEEDED(hr)) {
654 hr = pfo->DeleteItem(psi, nullptr);
655
656 if (SUCCEEDED(hr)) {
657 hr = pfo->PerformOperations();
658
659 if (FAILED(hr)) {
660 *r_error_message = "Failed to prepare delete operation";
661 }
662 }
663 else {
664 *r_error_message = "Failed to prepare delete operation";
665 }
666 psi->Release();
667 }
668 else {
669 *r_error_message = "Failed to parse path";
670 }
671 }
672 else {
673 *r_error_message = "Failed to set operation flags";
674 }
675 pfo->Release();
676 }
677 else {
678 *r_error_message = "Failed to create FileOperation instance";
679 }
680 CoUninitialize();
681 }
682 else {
683 *r_error_message = "Failed to initialize COM";
684 }
685
686 return FAILED(hr);
687}
688
689static bool delete_unique(const char *path, const bool dir)
690{
691 bool err;
692
693 UTF16_ENCODE(path);
694
695 if (dir) {
696 err = !RemoveDirectoryW(path_16);
697 if (err) {
698 printf("Unable to remove directory\n");
699 }
700 }
701 else {
702 err = !DeleteFileW(path_16);
703 if (err) {
704 callLocalErrorCallBack("Unable to delete file");
705 }
706 }
707
708 UTF16_UN_ENCODE(path);
709
710 return err;
711}
712
713static bool delete_recursive(const char *dir)
714{
715 struct direntry *filelist, *fl;
716 bool err = false;
717 uint filelist_num, i;
718
719 i = filelist_num = BLI_filelist_dir_contents(dir, &filelist);
720 fl = filelist;
721 while (i--) {
722 if (FILENAME_IS_CURRPAR(fl->relname)) {
723 /* Skip! */
724 }
725 else if (S_ISDIR(fl->type)) {
726 char path[FILE_MAXDIR];
727
728 /* dir listing produces dir path without trailing slash... */
729 STRNCPY(path, fl->path);
731
732 if (delete_recursive(path)) {
733 err = true;
734 }
735 }
736 else {
737 if (delete_unique(fl->path, false)) {
738 err = true;
739 }
740 }
741 fl++;
742 }
743
744 if (!err && delete_unique(dir, true)) {
745 err = true;
746 }
747
748 BLI_filelist_free(filelist, filelist_num);
749
750 return err;
751}
752
753int BLI_delete(const char *path, bool dir, bool recursive)
754{
755 int err;
756
758
759 /* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
760 BLI_assert(!(dir == false && recursive == true));
761
762 if (recursive) {
763 err = delete_recursive(path);
764 }
765 else {
766 err = delete_unique(path, dir);
767 }
768
769 return err;
770}
771
775int BLI_delete_soft(const char *file, const char **r_error_message)
776{
777 int err;
778
780
781 UTF16_ENCODE(file);
782
783 err = delete_soft(file_16, r_error_message);
784
785 UTF16_UN_ENCODE(file);
786
787 return err;
788}
789
797static const char *path_destination_ensure_filename(const char *path_src,
798 const char *path_dst,
799 char *buf,
800 size_t buf_size)
801{
802 const char *filename_src = BLI_path_basename(path_src);
803 /* Unlikely but possible this has no slashes. */
804 if (filename_src != path_src) {
805 const size_t path_dst_len = strlen(path_dst);
806 /* Check if `path_dst` points to a directory. */
807 if (path_dst_len && BLI_path_slash_is_native_compat(path_dst[path_dst_len - 1])) {
808 size_t buf_size_needed = path_dst_len + strlen(filename_src) + 1;
809 char *path_dst_with_filename = (buf_size_needed <= buf_size) ?
810 buf :
811 MEM_calloc_arrayN<char>(buf_size_needed, __func__);
812 BLI_string_join(path_dst_with_filename, buf_size_needed, path_dst, filename_src);
813 return path_dst_with_filename;
814 }
815 }
816 return path_dst;
817}
818
819int BLI_path_move(const char *path_src, const char *path_dst)
820{
821 char path_dst_buf[FILE_MAX_STATIC_BUF];
822 const char *path_dst_with_filename = path_destination_ensure_filename(
823 path_src, path_dst, path_dst_buf, sizeof(path_dst_buf));
824
825 int err;
826
827 UTF16_ENCODE(path_src);
828 UTF16_ENCODE(path_dst_with_filename);
829 err = !MoveFileW(path_src_16, path_dst_with_filename_16);
830 UTF16_UN_ENCODE(path_dst_with_filename);
831 UTF16_UN_ENCODE(path_src);
832
833 if (err) {
834 callLocalErrorCallBack("Unable to move file");
835 printf(" Move from '%s' to '%s' failed\n", path_src, path_dst_with_filename);
836 }
837
838 if (!ELEM(path_dst_with_filename, path_dst_buf, path_dst)) {
839 MEM_freeN(path_dst_with_filename);
840 }
841
842 return err;
843}
844
845int BLI_copy(const char *path_src, const char *path_dst)
846{
847 char path_dst_buf[FILE_MAX_STATIC_BUF];
848 const char *path_dst_with_filename = path_destination_ensure_filename(
849 path_src, path_dst, path_dst_buf, sizeof(path_dst_buf));
850 int err;
851
852 UTF16_ENCODE(path_src);
853 UTF16_ENCODE(path_dst_with_filename);
854 err = !CopyFileW(path_src_16, path_dst_with_filename_16, false);
855 UTF16_UN_ENCODE(path_dst_with_filename);
856 UTF16_UN_ENCODE(path_src);
857
858 if (err) {
859 callLocalErrorCallBack("Unable to copy file!");
860 printf(" Copy from '%s' to '%s' failed\n", path_src, path_dst_with_filename);
861 }
862
863 if (!ELEM(path_dst_with_filename, path_dst_buf, path_dst)) {
864 MEM_freeN(path_dst_with_filename);
865 }
866
867 return err;
868}
869
870# if 0
871int BLI_create_symlink(const char *path_src, const char *path_dst)
872{
873 /* See patch from #30870, should this ever become needed. */
874 callLocalErrorCallBack("Linking files is unsupported on Windows");
875 (void)path_src;
876 (void)path_dst;
877 return 1;
878}
879# endif
880
881#else /* The UNIX world */
882
883/* results from recursive_operation and its callbacks */
884enum {
885 /* operation succeeded */
887
888 /* operation requested not to perform recursive digging for current path */
890
891 /* error occurred in callback and recursive walking should stop immediately */
893};
894
895using RecursiveOp_Callback = int (*)(const char *from, const char *to);
896
897[[maybe_unused]] static bool path_has_trailing_slash(const char *path)
898{
899 const int path_len = strlen(path);
900 if (path_len == 0) {
901 return false;
902 }
903 return BLI_path_slash_is_native_compat(path[path_len - 1]);
904}
905
906static size_t path_len_no_trailing_slash(const char *path)
907{
908 int len = strlen(path);
909 int len_found = len;
910 while (len) {
911 len--;
913 break;
914 }
915 len_found = len;
916 }
917 return len_found;
918}
919
920/* -------------------------------------------------------------------- */
923
927struct StrBuf {
928 char *str;
929 size_t str_len;
931};
932
933static void strbuf_init(StrBuf *buf, const char *str, size_t str_len, size_t str_len_alloc)
934{
935 str_len_alloc = std::max(str_len + 1, str_len_alloc);
936 buf->str = static_cast<char *>(malloc(str_len_alloc));
937 memcpy(buf->str, str, str_len);
938 buf->str[str_len] = '\0';
939 buf->str_len = str_len;
940 buf->str_len_alloc = str_len_alloc;
941}
942
943static void strbuf_free(StrBuf *buf)
944{
945 free(buf->str);
946}
947
951static void strbuf_append_path(StrBuf *buf, const char *filename)
952{
953 BLI_assert(strlen(buf->str) == buf->str_len);
955 bool has_slash = (buf->str_len > 0 &&
957 const size_t filename_len = strlen(filename);
958 const size_t len = buf->str_len + (has_slash ? 0 : 1) + filename_len;
959
960 if (buf->str_len_alloc < len) {
961 buf->str = static_cast<char *>(realloc(static_cast<void *>(buf->str), len + 1));
962 buf->str_len_alloc = len;
963 }
964 if (has_slash == false) {
965 buf->str[buf->str_len++] = SEP;
966 }
967 memcpy(buf->str + buf->str_len, filename, filename_len + 1);
968 buf->str_len += filename_len;
969 BLI_assert(buf->str_len <= buf->str_len_alloc);
970}
971
972static void strbuf_trim(StrBuf *buf, size_t len)
973{
974 BLI_assert(len <= buf->str_len);
975 buf->str_len = len;
976 buf->str[len] = '\0';
977}
978
980
982 StrBuf *dst_buf,
983 RecursiveOp_Callback callback_dir_pre,
984 RecursiveOp_Callback callback_file,
985 RecursiveOp_Callback callback_dir_post)
986{
987 /* NOTE(@ideasman42): This function must *not* use any `MEM_*` functions
988 * as it's used to purge temporary files on when the processed is aborted,
989 * in this case the `MEM_*` state may have already been freed (e.g. memory usage tracking)
990 * causing freed memory access, potentially crashing. This constraint doesn't apply to the
991 * callbacks themselves - unless they might also be called when aborting. */
992 struct stat st;
993 int ret = 0;
994
995 dirent **dirlist = nullptr;
996 int dirlist_num = 0;
997
998 /* Check there's no trailing slash in file paths. */
1000 BLI_assert(!(dst_buf && path_has_trailing_slash(dst_buf->str)));
1001
1002 do { /* once */
1003
1004 ret = lstat(src_buf->str, &st);
1005 if (ret < 0) {
1006 /* source wasn't found, nothing to operate with */
1007 break;
1008 }
1009
1010 if (!S_ISDIR(st.st_mode)) {
1011 /* source isn't a directory, can't do recursive walking for it,
1012 * so just call file callback and leave */
1013 if (callback_file != nullptr) {
1014 ret = callback_file(src_buf->str, dst_buf ? dst_buf->str : nullptr);
1016 ret = -1;
1017 }
1018 }
1019 break;
1020 }
1021
1022 dirlist_num = scandir(src_buf->str, &dirlist, nullptr, alphasort);
1023 if (dirlist_num < 0) {
1024 /* error opening directory for listing */
1025 perror("scandir");
1026 ret = -1;
1027 break;
1028 }
1029
1030 if (callback_dir_pre != nullptr) {
1031 ret = callback_dir_pre(src_buf->str, dst_buf ? dst_buf->str : nullptr);
1034 /* callback requested not to perform recursive walking, not an error */
1035 ret = 0;
1036 }
1037 else {
1038 ret = -1;
1039 }
1040 break;
1041 }
1042 }
1043 const size_t src_len = src_buf->str_len;
1044 const size_t dst_len = dst_buf ? dst_buf->str_len : 0;
1045
1046 for (int i = 0; i < dirlist_num; i++) {
1047 const dirent *const dirent = dirlist[i];
1048
1050 continue;
1051 }
1052
1053 strbuf_append_path(src_buf, dirent->d_name);
1054 if (dst_buf) {
1055 strbuf_append_path(dst_buf, dirent->d_name);
1056 }
1057
1058 bool is_dir;
1059
1060# ifdef __HAIKU__
1061 {
1062 struct stat st_dir;
1063 lstat(src_buf->str, &st_dir);
1064 is_dir = S_ISDIR(st_dir.st_mode);
1065 }
1066# else
1067 is_dir = (dirent->d_type == DT_DIR);
1068# endif
1069
1070 if (is_dir) {
1071 /* Recurse into sub-directories. */
1073 src_buf, dst_buf, callback_dir_pre, callback_file, callback_dir_post);
1074 }
1075 else if (callback_file != nullptr) {
1076 ret = callback_file(src_buf->str, dst_buf ? dst_buf->str : nullptr);
1078 ret = -1;
1079 }
1080 }
1081 strbuf_trim(src_buf, src_len);
1082 if (dst_buf) {
1083 strbuf_trim(dst_buf, dst_len);
1084 }
1085
1086 if (ret != 0) {
1087 break;
1088 }
1089 }
1090 if (ret != 0) {
1091 break;
1092 }
1093
1094 if (callback_dir_post != nullptr) {
1095 ret = callback_dir_post(src_buf->str, dst_buf ? dst_buf->str : nullptr);
1097 ret = -1;
1098 }
1099 }
1100 } while (false);
1101
1102 if (dirlist != nullptr) {
1103 for (int i = 0; i < dirlist_num; i++) {
1104 free(dirlist[i]);
1105 }
1106 free(dirlist);
1107 }
1108
1109 return ret;
1110}
1111
1128static int recursive_operation(const char *path_src,
1129 const char *path_dst,
1130 RecursiveOp_Callback callback_dir_pre,
1131 RecursiveOp_Callback callback_file,
1132 RecursiveOp_Callback callback_dir_post)
1133
1134{
1135 StrBuf src_buf_stack = {};
1136 StrBuf dst_buf_stack = {};
1137 StrBuf *src_buf = &src_buf_stack;
1138 StrBuf *dst_buf = path_dst ? &dst_buf_stack : nullptr;
1139# ifndef NDEBUG
1140 /* Don't over allocate to ensure resizing works as expected. */
1141 const size_t str_len_over_alloc = 0;
1142# else
1143 const size_t str_len_over_alloc = FILE_MAX;
1144# endif
1145
1146 strbuf_init(src_buf, path_src, path_len_no_trailing_slash(path_src), str_len_over_alloc);
1147 if (dst_buf) {
1148 strbuf_init(dst_buf, path_dst, path_len_no_trailing_slash(path_dst), str_len_over_alloc);
1149 }
1150
1151 const int result = recursive_operation_impl(
1152 src_buf, dst_buf, callback_dir_pre, callback_file, callback_dir_post);
1153
1154 strbuf_free(src_buf);
1155 if (dst_buf) {
1156 strbuf_free(dst_buf);
1157 }
1158 return result;
1159}
1160
1161static int delete_callback_post(const char *from, const char * /*to*/)
1162{
1163 if (rmdir(from)) {
1164 perror("rmdir");
1165
1167 }
1168
1170}
1171
1172static int delete_single_file(const char *from, const char * /*to*/)
1173{
1174 if (unlink(from)) {
1175 perror("unlink");
1176
1178 }
1179
1181}
1182
1183FILE *BLI_fopen(const char *filepath, const char *mode)
1184{
1185 BLI_assert(!BLI_path_is_rel(filepath));
1186
1187 return fopen(filepath, mode);
1188}
1189
1190void *BLI_gzopen(const char *filepath, const char *mode)
1191{
1192 BLI_assert(!BLI_path_is_rel(filepath));
1193
1194 return gzopen(filepath, mode);
1195}
1196
1197int BLI_open(const char *filepath, int oflag, int pmode)
1198{
1199 BLI_assert(!BLI_path_is_rel(filepath));
1200
1201 return open(filepath, oflag, pmode);
1202}
1203
1204int BLI_access(const char *filepath, int mode)
1205{
1206 BLI_assert(!BLI_path_is_rel(filepath));
1207
1208 return access(filepath, mode);
1209}
1210
1211int BLI_delete(const char *path, bool dir, bool recursive)
1212{
1214 /* Not an error but avoid ambiguous arguments (recursive file deletion isn't meaningful). */
1215 BLI_assert(!(dir == false && recursive == true));
1216
1217 if (recursive) {
1218 return recursive_operation(path, nullptr, nullptr, delete_single_file, delete_callback_post);
1219 }
1220 if (dir) {
1221 return rmdir(path);
1222 }
1223 return remove(path);
1224}
1225
1226/* Apple version is defined in fileops_apple.mm */
1227# ifndef __APPLE__
1228int BLI_delete_soft(const char *filepath, const char **r_error_message)
1229{
1230 BLI_assert(!BLI_path_is_rel(filepath));
1231
1232 const char *args[5];
1233 const char *process_failed;
1234
1235 /* May contain `:` delimiter characters according to version 1.5 of the spec:
1236 * https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html */
1237 const char *xdg_current_desktop = [] {
1238 /* Account for VSCode overriding this value (TSK!), see: #133921. */
1239 const char *key = "ORIGINAL_XDG_CURRENT_DESKTOP";
1240 const char *value = getenv(key);
1241 return value ? value : getenv(key + 9);
1242 }();
1243 const char *xdg_session_desktop = getenv("XDG_SESSION_DESKTOP");
1244
1245 if ((xdg_current_desktop && BLI_string_elem_split_by_delim(xdg_current_desktop, ':', "KDE")) ||
1246 (xdg_session_desktop && STREQ(xdg_session_desktop, "KDE")))
1247 {
1248 args[0] = "kioclient5";
1249 args[1] = "move";
1250 args[2] = filepath;
1251 args[3] = "trash:/";
1252 args[4] = nullptr;
1253 process_failed = "kioclient5 reported failure";
1254 }
1255 else {
1256 args[0] = "gio";
1257 args[1] = "trash";
1258 args[2] = filepath;
1259 args[3] = nullptr;
1260 process_failed = "gio reported failure";
1261 }
1262
1263 /* Restore when there are no errors. */
1264 const int errno_prev = errno;
1265 errno = 0;
1266
1267 int pid = fork();
1268 if (UNLIKELY(pid == -1)) {
1269 *r_error_message = errno ? strerror(errno) : "unable to fork process";
1270 return -1;
1271 }
1272
1273 if (pid == 0) {
1274 /* Child process. */
1275 execvp(args[0], (char **)args);
1276 /* This should only be reached if `execvp` fails and stack isn't replaced. */
1277
1278 /* Ensure outputs are flushed as `_exit` doesn't flush. */
1279 fflush(stdout);
1280 fflush(stderr);
1281
1282 /* Use `_exit` instead of `exit` so Blender's `atexit` cleanup functions don't run. */
1283 _exit(errno);
1285 return -1;
1286 }
1287
1288 /* Parent process. */
1289 int wstatus = 0;
1290 waitpid(pid, &wstatus, 0);
1291
1292 int result = 0; /* Success. */
1293 if (WIFEXITED(wstatus)) {
1294 const int errno_child = WEXITSTATUS(wstatus);
1295 if (errno_child) {
1296 *r_error_message = process_failed;
1297 result = -1;
1298
1299 /* Forward to the error so the caller may set the message. */
1300 errno = errno_child;
1301 }
1302 }
1303 else {
1304 *r_error_message =
1305 "Blender may not support moving files or directories to trash on your system.";
1306 result = -1;
1307 }
1308
1309 if (result == 0) {
1310 /* Only overwrite the value if there was an error. */
1311 errno = errno_prev;
1312 }
1313
1314 return result;
1315}
1316# endif
1317
1321static bool check_the_same(const char *path_a, const char *path_b)
1322{
1323 struct stat st_a, st_b;
1324
1325 if (lstat(path_a, &st_a)) {
1326 return false;
1327 }
1328
1329 if (lstat(path_b, &st_b)) {
1330 return false;
1331 }
1332
1333 return st_a.st_dev == st_b.st_dev && st_a.st_ino == st_b.st_ino;
1334}
1335
1339static int set_permissions(const char *filepath, const struct stat *st)
1340{
1341 if (chown(filepath, st->st_uid, st->st_gid)) {
1342 perror("chown");
1343 return -1;
1344 }
1345
1346 if (chmod(filepath, st->st_mode)) {
1347 perror("chmod");
1348 return -1;
1349 }
1350
1351 return 0;
1352}
1353
1354/* pre-recursive callback for copying operation
1355 * creates a destination directory where all source content fill be copied to */
1356static int copy_callback_pre(const char *from, const char *to)
1357{
1358 struct stat st;
1359
1360 if (check_the_same(from, to)) {
1361 fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
1363 }
1364
1365 if (lstat(from, &st)) {
1366 perror("stat");
1368 }
1369
1370 /* Create a directory. */
1371 if (mkdir(to, st.st_mode)) {
1372 perror("mkdir");
1374 }
1375
1376 /* Set proper owner and group on new directory. */
1377 if (chown(to, st.st_uid, st.st_gid)) {
1378 perror("chown");
1380 }
1381
1383}
1384
1385static int copy_single_file(const char *from, const char *to)
1386{
1387 FILE *from_stream, *to_stream;
1388 struct stat st;
1389 char buf[4096];
1390 size_t len;
1391
1392 if (check_the_same(from, to)) {
1393 fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
1395 }
1396
1397 if (lstat(from, &st)) {
1398 perror("lstat");
1400 }
1401
1402 if (S_ISLNK(st.st_mode)) {
1403 /* Symbolic links should be copied in special way. */
1404 char *link_buffer;
1405 int need_free;
1406 int64_t link_len;
1407
1408 /* Get large enough buffer to read link content. */
1409 if ((st.st_size + 1) < sizeof(buf)) {
1410 link_buffer = buf;
1411 need_free = 0;
1412 }
1413 else {
1414 link_buffer = MEM_calloc_arrayN<char>(st.st_size + 2, "copy_single_file link_buffer");
1415 need_free = 1;
1416 }
1417
1418 link_len = readlink(from, link_buffer, st.st_size + 1);
1419 if (link_len < 0) {
1420 perror("readlink");
1421
1422 if (need_free) {
1423 MEM_freeN(link_buffer);
1424 }
1425
1427 }
1428
1429 link_buffer[link_len] = '\0';
1430
1431 if (symlink(link_buffer, to)) {
1432 perror("symlink");
1433 if (need_free) {
1434 MEM_freeN(link_buffer);
1435 }
1437 }
1438
1439 if (need_free) {
1440 MEM_freeN(link_buffer);
1441 }
1442
1444 }
1445 if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
1446 /* Copy special type of file. */
1447 if (mknod(to, st.st_mode, st.st_rdev)) {
1448 perror("mknod");
1450 }
1451
1452 if (set_permissions(to, &st)) {
1454 }
1455
1457 }
1458 if (!S_ISREG(st.st_mode)) {
1459 fprintf(stderr, "Copying of this kind of files isn't supported yet\n");
1461 }
1462
1463 from_stream = fopen(from, "rb");
1464 if (!from_stream) {
1465 perror("fopen");
1467 }
1468
1469 to_stream = fopen(to, "wb");
1470 if (!to_stream) {
1471 perror("fopen");
1472 fclose(from_stream);
1474 }
1475
1476 while ((len = fread(buf, 1, sizeof(buf), from_stream)) > 0) {
1477 fwrite(buf, 1, len, to_stream);
1478 }
1479
1480 fclose(to_stream);
1481 fclose(from_stream);
1482
1483 if (set_permissions(to, &st)) {
1485 }
1486
1488}
1489
1490static int move_callback_pre(const char *from, const char *to)
1491{
1492 int ret = rename(from, to);
1493
1494 if (ret) {
1495 return copy_callback_pre(from, to);
1496 }
1497
1499}
1500
1501static int move_single_file(const char *from, const char *to)
1502{
1503 int ret = rename(from, to);
1504
1505 if (ret) {
1506 return copy_single_file(from, to);
1507 }
1508
1510}
1511
1512int BLI_path_move(const char *path_src, const char *path_dst)
1513{
1514 int ret = recursive_operation(path_src, path_dst, move_callback_pre, move_single_file, nullptr);
1515
1516 if (ret && ret != -1) {
1517 return recursive_operation(
1518 path_src, nullptr, nullptr, delete_single_file, delete_callback_post);
1519 }
1520
1521 return ret;
1522}
1523
1524static const char *path_destination_ensure_filename(const char *path_src,
1525 const char *path_dst,
1526 char *buf,
1527 size_t buf_size)
1528{
1529 if (BLI_is_dir(path_dst)) {
1530 char *path_src_no_slash = BLI_strdup(path_src);
1531 BLI_path_slash_rstrip(path_src_no_slash);
1532 const char *filename_src = BLI_path_basename(path_src_no_slash);
1533 if (filename_src != path_src_no_slash) {
1534 const size_t buf_size_needed = strlen(path_dst) + 1 + strlen(filename_src) + 1;
1535 char *path_dst_with_filename = (buf_size_needed <= buf_size) ?
1536 buf :
1537 MEM_calloc_arrayN<char>(buf_size_needed, __func__);
1538 BLI_path_join(path_dst_with_filename, buf_size_needed, path_dst, filename_src);
1539 path_dst = path_dst_with_filename;
1540 }
1541 MEM_freeN(path_src_no_slash);
1542 }
1543 return path_dst;
1544}
1545
1546int BLI_copy(const char *path_src, const char *path_dst)
1547{
1548 char path_dst_buf[FILE_MAX_STATIC_BUF];
1549 const char *path_dst_with_filename = path_destination_ensure_filename(
1550 path_src, path_dst, path_dst_buf, sizeof(path_dst_buf));
1551 int ret;
1552
1554 path_src, path_dst_with_filename, copy_callback_pre, copy_single_file, nullptr);
1555
1556 if (!ELEM(path_dst_with_filename, path_dst_buf, path_dst)) {
1557 MEM_freeN(path_dst_with_filename);
1558 }
1559
1560 return ret;
1561}
1562
1563# if 0
1564int BLI_create_symlink(const char *path_src, const char *path_dst)
1565{
1566 return symlink(path_dst, path_src);
1567}
1568# endif
1569
1570#endif
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:360
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:443
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.cc:41
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
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.
return true
ATTR_WARN_UNUSED_RESULT const BMFlagLayer const short oflag
long long int int64_t
#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:981
static size_t path_len_no_trailing_slash(const char *path)
Definition fileops_c.cc:906
static void strbuf_append_path(StrBuf *buf, const char *filename)
Definition fileops_c.cc:951
int BLI_access(const char *filepath, int mode)
static bool check_the_same(const char *path_a, const char *path_b)
static bool dir_create_recursive(const char *dirname, const int len)
Definition fileops_c.cc:354
static int move_callback_pre(const char *from, const char *to)
static bool path_has_trailing_slash(const char *path)
Definition fileops_c.cc:897
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
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:943
bool BLI_dir_create_recursive(const char *dirname)
Definition fileops_c.cc:414
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:452
int(*)(const char *from, const char *to) RecursiveOp_Callback
Definition fileops_c.cc:895
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:933
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:528
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:972
@ RecursiveOp_Callback_StopRecurs
Definition fileops_c.cc:889
@ RecursiveOp_Callback_OK
Definition fileops_c.cc:886
@ RecursiveOp_Callback_Error
Definition fileops_c.cc:892
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:461
static int copy_callback_pre(const char *from, const char *to)
#define input
#define printf(...)
#define output
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
return ret
size_t str_len_alloc
Definition fileops_c.cc:930
char * str
Definition fileops_c.cc:928
size_t str_len
Definition fileops_c.cc:929
char * d_name
const char * relname
const char * path
i
Definition text_draw.cc:230
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
uint len