Blender  V3.3
fileops.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 #include <stdlib.h> /* malloc */
9 #include <string.h>
10 
11 #include <fcntl.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 
15 #include <errno.h>
16 
17 #include <zlib.h>
18 #include <zstd.h>
19 
20 #ifdef WIN32
21 # include "BLI_fileops_types.h"
22 # include "BLI_winstuff.h"
23 # include "utf_winfunc.h"
24 # include "utfconv.h"
25 # include <io.h>
26 # include <shellapi.h>
27 # include <shobjidl.h>
28 # include <windows.h>
29 #else
30 # if defined(__APPLE__)
31 # include <CoreFoundation/CoreFoundation.h>
32 # include <objc/message.h>
33 # include <objc/runtime.h>
34 # endif
35 # include <dirent.h>
36 # include <sys/param.h>
37 # include <sys/wait.h>
38 # include <unistd.h>
39 #endif
40 
41 #include "MEM_guardedalloc.h"
42 
43 #include "BLI_fileops.h"
44 #include "BLI_path_util.h"
45 #include "BLI_string.h"
46 #include "BLI_sys_types.h" /* for intptr_t support */
47 #include "BLI_utildefines.h"
48 
50  void *buf, size_t len, FILE *file, size_t file_offset, int compression_level)
51 {
52  fseek(file, file_offset, SEEK_SET);
53 
54  ZSTD_CCtx *ctx = ZSTD_createCCtx();
55  ZSTD_CCtx_setParameter(ctx, ZSTD_c_compressionLevel, compression_level);
56 
57  ZSTD_inBuffer input = {buf, len, 0};
58 
59  size_t out_len = ZSTD_CStreamOutSize();
60  void *out_buf = MEM_mallocN(out_len, __func__);
61  size_t total_written = 0;
62 
63  /* Compress block and write it out until the input has been consumed. */
64  while (input.pos < input.size) {
65  ZSTD_outBuffer output = {out_buf, out_len, 0};
66  size_t ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_continue);
67  if (ZSTD_isError(ret)) {
68  break;
69  }
70  if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
71  break;
72  }
73  total_written += output.pos;
74  }
75 
76  /* Finalize the `Zstd` frame. */
77  size_t ret = 1;
78  while (ret != 0) {
79  ZSTD_outBuffer output = {out_buf, out_len, 0};
80  ret = ZSTD_compressStream2(ctx, &output, &input, ZSTD_e_end);
81  if (ZSTD_isError(ret)) {
82  break;
83  }
84  if (fwrite(out_buf, 1, output.pos, file) != output.pos) {
85  break;
86  }
87  total_written += output.pos;
88  }
89 
90  MEM_freeN(out_buf);
91  ZSTD_freeCCtx(ctx);
92 
93  return ZSTD_isError(ret) ? 0 : total_written;
94 }
95 
96 size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
97 {
98  fseek(file, file_offset, SEEK_SET);
99 
100  ZSTD_DCtx *ctx = ZSTD_createDCtx();
101 
102  size_t in_len = ZSTD_DStreamInSize();
103  void *in_buf = MEM_mallocN(in_len, __func__);
104  ZSTD_inBuffer input = {in_buf, in_len, 0};
105 
106  ZSTD_outBuffer output = {buf, len, 0};
107 
108  size_t ret = 0;
109  /* Read and decompress chunks of input data until we have enough output. */
110  while (output.pos < output.size && !ZSTD_isError(ret)) {
111  input.size = fread(in_buf, 1, in_len, file);
112  if (input.size == 0) {
113  break;
114  }
115 
116  /* Consume input data until we run out or have enough output. */
117  input.pos = 0;
118  while (input.pos < input.size && output.pos < output.size) {
119  ret = ZSTD_decompressStream(ctx, &output, &input);
120 
121  if (ZSTD_isError(ret)) {
122  break;
123  }
124  }
125  }
126 
127  MEM_freeN(in_buf);
128  ZSTD_freeDCtx(ctx);
129 
130  return ZSTD_isError(ret) ? 0 : output.pos;
131 }
132 
133 bool BLI_file_magic_is_gzip(const char header[4])
134 {
135  /* GZIP itself starts with the magic bytes 0x1f 0x8b.
136  * The third byte indicates the compression method, which is 0x08 for DEFLATE. */
137  return header[0] == 0x1f && header[1] == 0x8b && header[2] == 0x08;
138 }
139 
140 bool BLI_file_magic_is_zstd(const char header[4])
141 {
142  /* ZSTD files consist of concatenated frames, each either a Zstd frame or a skippable frame.
143  * Both types of frames start with a magic number: 0xFD2FB528 for Zstd frames and 0x184D2A5*
144  * for skippable frames, with the * being anything from 0 to F.
145  *
146  * To check whether a file is Zstd-compressed, we just check whether the first frame matches
147  * either. Seeking through the file until a Zstd frame is found would make things more
148  * complicated and the probability of a false positive is rather low anyways.
149  *
150  * Note that LZ4 uses a compatible format, so even though its compressed frames have a
151  * different magic number, a valid LZ4 file might also start with a skippable frame matching
152  * the second check here.
153  *
154  * For more details, see https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md
155  */
156 
157  uint32_t magic = *((uint32_t *)header);
158  if (magic == 0xFD2FB528) {
159  return true;
160  }
161  if ((magic >> 4) == 0x184D2A5) {
162  return true;
163  }
164  return false;
165 }
166 
167 bool BLI_file_is_writable(const char *filepath)
168 {
169  bool writable;
170  if (BLI_access(filepath, W_OK) == 0) {
171  /* file exists and I can write to it */
172  writable = true;
173  }
174  else if (errno != ENOENT) {
175  /* most likely file or containing directory cannot be accessed */
176  writable = false;
177  }
178  else {
179  /* file doesn't exist -- check I can create it in parent directory */
180  char parent[FILE_MAX];
181  BLI_split_dirfile(filepath, parent, NULL, sizeof(parent), 0);
182 #ifdef WIN32
183  /* windows does not have X_OK */
184  writable = BLI_access(parent, W_OK) == 0;
185 #else
186  writable = BLI_access(parent, X_OK | W_OK) == 0;
187 #endif
188  }
189  return writable;
190 }
191 
192 bool BLI_file_touch(const char *file)
193 {
194  FILE *f = BLI_fopen(file, "r+b");
195 
196  if (f != NULL) {
197  int c = getc(f);
198 
199  if (c == EOF) {
200  /* Empty file, reopen in truncate write mode... */
201  fclose(f);
202  f = BLI_fopen(file, "w+b");
203  }
204  else {
205  /* Otherwise, rewrite first byte. */
206  rewind(f);
207  putc(c, f);
208  }
209  }
210  else {
211  f = BLI_fopen(file, "wb");
212  }
213  if (f) {
214  fclose(f);
215  return true;
216  }
217  return false;
218 }
219 
220 #ifdef WIN32
221 
222 static void callLocalErrorCallBack(const char *err)
223 {
224  printf("%s\n", err);
225 }
226 
227 FILE *BLI_fopen(const char *filepath, const char *mode)
228 {
229  BLI_assert(!BLI_path_is_rel(filepath));
230 
231  return ufopen(filepath, mode);
232 }
233 
234 void BLI_get_short_name(char short_name[256], const char *filepath)
235 {
236  wchar_t short_name_16[256];
237  int i = 0;
238 
239  UTF16_ENCODE(filepath);
240 
241  GetShortPathNameW(filepath_16, short_name_16, 256);
242 
243  for (i = 0; i < 256; i++) {
244  short_name[i] = (char)short_name_16[i];
245  }
246 
247  UTF16_UN_ENCODE(filepath);
248 }
249 
250 void *BLI_gzopen(const char *filepath, const char *mode)
251 {
252  gzFile gzfile;
253 
254  BLI_assert(!BLI_path_is_rel(filepath));
255 
256  /* XXX: Creates file before transcribing the path. */
257  if (mode[0] == 'w') {
258  FILE *file = ufopen(filepath, "a");
259  if (file == NULL) {
260  /* File couldn't be opened, e.g. due to permission error. */
261  return NULL;
262  }
263  fclose(file);
264  }
265 
266  /* temporary #if until we update all libraries to 1.2.7
267  * for correct wide char path handling */
268 # if ZLIB_VERNUM >= 0x1270
269  UTF16_ENCODE(filepath);
270 
271  gzfile = gzopen_w(filepath_16, mode);
272 
273  UTF16_UN_ENCODE(filepath);
274 # else
275  {
276  char short_name[256];
277  BLI_get_short_name(short_name, filepath);
278  gzfile = gzopen(short_name, mode);
279  }
280 # endif
281 
282  return gzfile;
283 }
284 
285 int BLI_open(const char *filepath, int oflag, int pmode)
286 {
287  BLI_assert(!BLI_path_is_rel(filepath));
288 
289  return uopen(filepath, oflag, pmode);
290 }
291 
292 int BLI_access(const char *filepath, int mode)
293 {
294  BLI_assert(!BLI_path_is_rel(filepath));
295 
296  return uaccess(filepath, mode);
297 }
298 
299 static bool delete_soft(const wchar_t *path_16, const char **error_message)
300 {
301  /* Deletes file or directory to recycling bin. The latter moves all contained files and
302  * directories recursively to the recycling bin as well. */
303  IFileOperation *pfo;
304  IShellItem *psi;
305 
306  HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
307 
308  if (SUCCEEDED(hr)) {
309  /* This is also the case when COM was previously initialized and CoInitializeEx returns
310  * S_FALSE, which is not an error. Both HRESULT values S_OK and S_FALSE indicate success. */
311 
312  hr = CoCreateInstance(
313  &CLSID_FileOperation, NULL, CLSCTX_ALL, &IID_IFileOperation, (void **)&pfo);
314 
315  if (SUCCEEDED(hr)) {
316  /* Flags for deletion:
317  * FOF_ALLOWUNDO: Enables moving file to recycling bin.
318  * FOF_SILENT: Don't show progress dialog box.
319  * FOF_WANTNUKEWARNING: Show dialog box if file can't be moved to recycling bin. */
320  hr = pfo->lpVtbl->SetOperationFlags(pfo, FOF_ALLOWUNDO | FOF_SILENT | FOF_WANTNUKEWARNING);
321 
322  if (SUCCEEDED(hr)) {
323  hr = SHCreateItemFromParsingName(path_16, NULL, &IID_IShellItem, (void **)&psi);
324 
325  if (SUCCEEDED(hr)) {
326  hr = pfo->lpVtbl->DeleteItem(pfo, psi, NULL);
327 
328  if (SUCCEEDED(hr)) {
329  hr = pfo->lpVtbl->PerformOperations(pfo);
330 
331  if (FAILED(hr)) {
332  *error_message = "Failed to prepare delete operation";
333  }
334  }
335  else {
336  *error_message = "Failed to prepare delete operation";
337  }
338  psi->lpVtbl->Release(psi);
339  }
340  else {
341  *error_message = "Failed to parse path";
342  }
343  }
344  else {
345  *error_message = "Failed to set operation flags";
346  }
347  pfo->lpVtbl->Release(pfo);
348  }
349  else {
350  *error_message = "Failed to create FileOperation instance";
351  }
352  CoUninitialize();
353  }
354  else {
355  *error_message = "Failed to initialize COM";
356  }
357 
358  return FAILED(hr);
359 }
360 
361 static bool delete_unique(const char *path, const bool dir)
362 {
363  bool err;
364 
365  UTF16_ENCODE(path);
366 
367  if (dir) {
368  err = !RemoveDirectoryW(path_16);
369  if (err) {
370  printf("Unable to remove directory\n");
371  }
372  }
373  else {
374  err = !DeleteFileW(path_16);
375  if (err) {
376  callLocalErrorCallBack("Unable to delete file");
377  }
378  }
379 
380  UTF16_UN_ENCODE(path);
381 
382  return err;
383 }
384 
385 static bool delete_recursive(const char *dir)
386 {
387  struct direntry *filelist, *fl;
388  bool err = false;
389  uint filelist_num, i;
390 
391  i = filelist_num = BLI_filelist_dir_contents(dir, &filelist);
392  fl = filelist;
393  while (i--) {
394  const char *file = BLI_path_basename(fl->path);
395 
396  if (FILENAME_IS_CURRPAR(file)) {
397  /* Skip! */
398  }
399  else if (S_ISDIR(fl->type)) {
400  char path[FILE_MAXDIR];
401 
402  /* dir listing produces dir path without trailing slash... */
403  BLI_strncpy(path, fl->path, sizeof(path));
405 
406  if (delete_recursive(path)) {
407  err = true;
408  }
409  }
410  else {
411  if (delete_unique(fl->path, false)) {
412  err = true;
413  }
414  }
415  fl++;
416  }
417 
418  if (!err && delete_unique(dir, true)) {
419  err = true;
420  }
421 
422  BLI_filelist_free(filelist, filelist_num);
423 
424  return err;
425 }
426 
427 int BLI_delete(const char *file, bool dir, bool recursive)
428 {
429  int err;
430 
432 
433  if (recursive) {
434  err = delete_recursive(file);
435  }
436  else {
437  err = delete_unique(file, dir);
438  }
439 
440  return err;
441 }
442 
446 int BLI_delete_soft(const char *file, const char **error_message)
447 {
448  int err;
449 
451 
453 
454  err = delete_soft(file_16, error_message);
455 
457 
458  return err;
459 }
460 
461 /* Not used anywhere! */
462 # if 0
463 int BLI_move(const char *file, const char *to)
464 {
465  char str[MAXPATHLEN + 12];
466  int err;
467 
468  /* windows doesn't support moving to a directory
469  * it has to be 'mv filepath filepath' and not
470  * 'mv filepath destination_directory' */
471 
472  BLI_strncpy(str, to, sizeof(str));
473  /* points 'to' to a directory ? */
474  if (BLI_path_slash_rfind(str) == (str + strlen(str) - 1)) {
475  if (BLI_path_slash_rfind(file) != NULL) {
476  strcat(str, BLI_path_slash_rfind(file) + 1);
477  }
478  }
479 
481  UTF16_ENCODE(str);
482  err = !MoveFileW(file_16, str_16);
485 
486  if (err) {
487  callLocalErrorCallBack("Unable to move file");
488  printf(" Move from '%s' to '%s' failed\n", file, str);
489  }
490 
491  return err;
492 }
493 # endif
494 
495 int BLI_copy(const char *file, const char *to)
496 {
497  char str[MAXPATHLEN + 12];
498  int err;
499 
500  /* windows doesn't support copying to a directory
501  * it has to be 'cp filepath filepath' and not
502  * 'cp filepath destdir' */
503 
504  BLI_strncpy(str, to, sizeof(str));
505  /* points 'to' to a directory ? */
506  if (BLI_path_slash_rfind(str) == (str + strlen(str) - 1)) {
507  if (BLI_path_slash_rfind(file) != NULL) {
508  strcat(str, BLI_path_slash_rfind(file) + 1);
509  }
510  }
511 
513  UTF16_ENCODE(str);
514  err = !CopyFileW(file_16, str_16, false);
517 
518  if (err) {
519  callLocalErrorCallBack("Unable to copy file!");
520  printf(" Copy from '%s' to '%s' failed\n", file, str);
521  }
522 
523  return err;
524 }
525 
526 # if 0
527 int BLI_create_symlink(const char *file, const char *to)
528 {
529  /* See patch from T30870, should this ever become needed. */
530  callLocalErrorCallBack("Linking files is unsupported on Windows");
531  (void)file;
532  (void)to;
533  return 1;
534 }
535 # endif
536 
538 bool BLI_dir_create_recursive(const char *dirname)
539 {
540  char *lslash;
541  char tmp[MAXPATHLEN];
542  bool ret = true;
543 
544  /* First remove possible slash at the end of the dirname.
545  * This routine otherwise tries to create
546  * blah1/blah2/ (with slash) after creating
547  * blah1/blah2 (without slash) */
548 
549  BLI_strncpy(tmp, dirname, sizeof(tmp));
552 
553  /* check special case "c:\foo", don't try create "c:", harmless but prints an error below */
554  if (isalpha(tmp[0]) && (tmp[1] == ':') && tmp[2] == '\0') {
555  return true;
556  }
557 
558  if (BLI_is_dir(tmp)) {
559  return true;
560  }
561  else if (BLI_exists(tmp)) {
562  return false;
563  }
564 
565  lslash = (char *)BLI_path_slash_rfind(tmp);
566 
567  if (lslash) {
568  /* Split about the last slash and recurse */
569  *lslash = 0;
570  if (!BLI_dir_create_recursive(tmp)) {
571  ret = false;
572  }
573  }
574 
575  if (ret && dirname[0]) { /* patch, this recursive loop tries to create a nameless directory */
576  if (umkdir(dirname) == -1) {
577  printf("Unable to create directory %s\n", dirname);
578  ret = false;
579  }
580  }
581  return ret;
582 }
583 
584 int BLI_rename(const char *from, const char *to)
585 {
586  if (!BLI_exists(from)) {
587  return 0;
588  }
589 
590  /* Make sure `from` & `to` are different (case insensitive) before removing. */
591  if (BLI_exists(to) && BLI_strcasecmp(from, to)) {
592  if (BLI_delete(to, false, false)) {
593  return 1;
594  }
595  }
596 
597  return urename(from, to);
598 }
599 
600 #else /* The UNIX world */
601 
602 /* results from recursive_operation and its callbacks */
603 enum {
604  /* operation succeeded */
606 
607  /* operation requested not to perform recursive digging for current path */
609 
610  /* error occurred in callback and recursive walking should stop immediately */
612 };
613 
614 typedef int (*RecursiveOp_Callback)(const char *from, const char *to);
615 
616 /* appending of filename to dir (ensures for buffer size before appending) */
617 static void join_dirfile_alloc(char **dst, size_t *alloc_len, const char *dir, const char *file)
618 {
619  size_t len = strlen(dir) + strlen(file) + 1;
620 
621  if (*dst == NULL) {
622  *dst = MEM_mallocN(len + 1, "join_dirfile_alloc path");
623  }
624  else if (*alloc_len < len) {
625  *dst = MEM_reallocN(*dst, len + 1);
626  }
627 
628  *alloc_len = len;
629 
630  BLI_join_dirfile(*dst, len + 1, dir, file);
631 }
632 
633 static char *strip_last_slash(const char *dir)
634 {
635  char *result = BLI_strdup(dir);
637 
638  return result;
639 }
640 
654 static int recursive_operation(const char *startfrom,
655  const char *startto,
656  RecursiveOp_Callback callback_dir_pre,
657  RecursiveOp_Callback callback_file,
658  RecursiveOp_Callback callback_dir_post)
659 {
660  struct stat st;
661  char *from = NULL, *to = NULL;
662  char *from_path = NULL, *to_path = NULL;
663  struct dirent **dirlist = NULL;
664  size_t from_alloc_len = -1, to_alloc_len = -1;
665  int i, n = 0, ret = 0;
666 
667  do { /* once */
668  /* ensure there's no trailing slash in file path */
669  from = strip_last_slash(startfrom);
670  if (startto) {
671  to = strip_last_slash(startto);
672  }
673 
674  ret = lstat(from, &st);
675  if (ret < 0) {
676  /* source wasn't found, nothing to operate with */
677  break;
678  }
679 
680  if (!S_ISDIR(st.st_mode)) {
681  /* source isn't a directory, can't do recursive walking for it,
682  * so just call file callback and leave */
683  if (callback_file != NULL) {
684  ret = callback_file(from, to);
685  if (ret != RecursiveOp_Callback_OK) {
686  ret = -1;
687  }
688  }
689  break;
690  }
691 
692  n = scandir(startfrom, &dirlist, NULL, alphasort);
693  if (n < 0) {
694  /* error opening directory for listing */
695  perror("scandir");
696  ret = -1;
697  break;
698  }
699 
700  if (callback_dir_pre != NULL) {
701  ret = callback_dir_pre(from, to);
702  if (ret != RecursiveOp_Callback_OK) {
704  /* callback requested not to perform recursive walking, not an error */
705  ret = 0;
706  }
707  else {
708  ret = -1;
709  }
710  break;
711  }
712  }
713 
714  for (i = 0; i < n; i++) {
715  const struct dirent *const dirent = dirlist[i];
716 
718  continue;
719  }
720 
721  join_dirfile_alloc(&from_path, &from_alloc_len, from, dirent->d_name);
722  if (to) {
723  join_dirfile_alloc(&to_path, &to_alloc_len, to, dirent->d_name);
724  }
725 
726  bool is_dir;
727 
728 # ifdef __HAIKU__
729  {
730  struct stat st_dir;
731  char filepath[FILE_MAX];
732  BLI_path_join(filepath, sizeof(filepath), startfrom, dirent->d_name, NULL);
733  lstat(filepath, &st_dir);
734  is_dir = S_ISDIR(st_dir.st_mode);
735  }
736 # else
737  is_dir = (dirent->d_type == DT_DIR);
738 # endif
739 
740  if (is_dir) {
741  /* Recurse into sub-directories. */
743  from_path, to_path, callback_dir_pre, callback_file, callback_dir_post);
744  }
745  else if (callback_file != NULL) {
746  ret = callback_file(from_path, to_path);
747  if (ret != RecursiveOp_Callback_OK) {
748  ret = -1;
749  }
750  }
751 
752  if (ret != 0) {
753  break;
754  }
755  }
756  if (ret != 0) {
757  break;
758  }
759 
760  if (callback_dir_post != NULL) {
761  ret = callback_dir_post(from, to);
762  if (ret != RecursiveOp_Callback_OK) {
763  ret = -1;
764  }
765  }
766  } while (false);
767 
768  if (dirlist != NULL) {
769  for (i = 0; i < n; i++) {
770  free(dirlist[i]);
771  }
772  free(dirlist);
773  }
774  if (from_path != NULL) {
775  MEM_freeN(from_path);
776  }
777  if (to_path != NULL) {
778  MEM_freeN(to_path);
779  }
780  if (from != NULL) {
781  MEM_freeN(from);
782  }
783  if (to != NULL) {
784  MEM_freeN(to);
785  }
786 
787  return ret;
788 }
789 
790 static int delete_callback_post(const char *from, const char *UNUSED(to))
791 {
792  if (rmdir(from)) {
793  perror("rmdir");
794 
796  }
797 
799 }
800 
801 static int delete_single_file(const char *from, const char *UNUSED(to))
802 {
803  if (unlink(from)) {
804  perror("unlink");
805 
807  }
808 
810 }
811 
812 # ifdef __APPLE__
813 static int delete_soft(const char *file, const char **error_message)
814 {
815  int ret = -1;
816 
817  Class NSAutoreleasePoolClass = objc_getClass("NSAutoreleasePool");
818  SEL allocSel = sel_registerName("alloc");
819  SEL initSel = sel_registerName("init");
820  id poolAlloc = ((id(*)(Class, SEL))objc_msgSend)(NSAutoreleasePoolClass, allocSel);
821  id pool = ((id(*)(id, SEL))objc_msgSend)(poolAlloc, initSel);
822 
823  Class NSStringClass = objc_getClass("NSString");
824  SEL stringWithUTF8StringSel = sel_registerName("stringWithUTF8String:");
825  id pathString = ((
826  id(*)(Class, SEL, const char *))objc_msgSend)(NSStringClass, stringWithUTF8StringSel, file);
827 
828  Class NSFileManagerClass = objc_getClass("NSFileManager");
829  SEL defaultManagerSel = sel_registerName("defaultManager");
830  id fileManager = ((id(*)(Class, SEL))objc_msgSend)(NSFileManagerClass, defaultManagerSel);
831 
832  Class NSURLClass = objc_getClass("NSURL");
833  SEL fileURLWithPathSel = sel_registerName("fileURLWithPath:");
834  id nsurl = ((id(*)(Class, SEL, id))objc_msgSend)(NSURLClass, fileURLWithPathSel, pathString);
835 
836  SEL trashItemAtURLSel = sel_registerName("trashItemAtURL:resultingItemURL:error:");
837  BOOL deleteSuccessful = ((
838  BOOL(*)(id, SEL, id, id, id))objc_msgSend)(fileManager, trashItemAtURLSel, nsurl, nil, nil);
839 
840  if (deleteSuccessful) {
841  ret = 0;
842  }
843  else {
844  *error_message = "The Cocoa API call to delete file or directory failed";
845  }
846 
847  SEL drainSel = sel_registerName("drain");
848  ((void (*)(id, SEL))objc_msgSend)(pool, drainSel);
849 
850  return ret;
851 }
852 # else
853 static int delete_soft(const char *file, const char **error_message)
854 {
855  const char *args[5];
856  const char *process_failed;
857 
858  char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
859  char *xdg_session_desktop = getenv("XDG_SESSION_DESKTOP");
860 
861  if ((xdg_current_desktop != NULL && STREQ(xdg_current_desktop, "KDE")) ||
862  (xdg_session_desktop != NULL && STREQ(xdg_session_desktop, "KDE"))) {
863  args[0] = "kioclient5";
864  args[1] = "move";
865  args[2] = file;
866  args[3] = "trash:/";
867  args[4] = NULL;
868  process_failed = "kioclient5 reported failure";
869  }
870  else {
871  args[0] = "gio";
872  args[1] = "trash";
873  args[2] = file;
874  args[3] = NULL;
875  process_failed = "gio reported failure";
876  }
877 
878  int pid = fork();
879 
880  if (pid != 0) {
881  /* Parent process */
882  int wstatus = 0;
883 
884  waitpid(pid, &wstatus, 0);
885 
886  if (!WIFEXITED(wstatus)) {
887  *error_message =
888  "Blender may not support moving files or directories to trash on your system.";
889  return -1;
890  }
891  if (WIFEXITED(wstatus) && WEXITSTATUS(wstatus)) {
892  *error_message = process_failed;
893  return -1;
894  }
895 
896  return 0;
897  }
898 
899  execvp(args[0], (char **)args);
900 
901  *error_message = "Forking process failed.";
902  return -1; /* This should only be reached if execvp fails and stack isn't replaced. */
903 }
904 # endif
905 
906 FILE *BLI_fopen(const char *filepath, const char *mode)
907 {
908  BLI_assert(!BLI_path_is_rel(filepath));
909 
910  return fopen(filepath, mode);
911 }
912 
913 void *BLI_gzopen(const char *filepath, const char *mode)
914 {
915  BLI_assert(!BLI_path_is_rel(filepath));
916 
917  return gzopen(filepath, mode);
918 }
919 
920 int BLI_open(const char *filepath, int oflag, int pmode)
921 {
922  BLI_assert(!BLI_path_is_rel(filepath));
923 
924  return open(filepath, oflag, pmode);
925 }
926 
927 int BLI_access(const char *filepath, int mode)
928 {
929  BLI_assert(!BLI_path_is_rel(filepath));
930 
931  return access(filepath, mode);
932 }
933 
934 int BLI_delete(const char *file, bool dir, bool recursive)
935 {
937 
938  if (recursive) {
940  }
941  if (dir) {
942  return rmdir(file);
943  }
944  return remove(file);
945 }
946 
947 int BLI_delete_soft(const char *file, const char **error_message)
948 {
950 
951  return delete_soft(file, error_message);
952 }
953 
957 static bool check_the_same(const char *path_a, const char *path_b)
958 {
959  struct stat st_a, st_b;
960 
961  if (lstat(path_a, &st_a)) {
962  return false;
963  }
964 
965  if (lstat(path_b, &st_b)) {
966  return false;
967  }
968 
969  return st_a.st_dev == st_b.st_dev && st_a.st_ino == st_b.st_ino;
970 }
971 
975 static int set_permissions(const char *file, const struct stat *st)
976 {
977  if (chown(file, st->st_uid, st->st_gid)) {
978  perror("chown");
979  return -1;
980  }
981 
982  if (chmod(file, st->st_mode)) {
983  perror("chmod");
984  return -1;
985  }
986 
987  return 0;
988 }
989 
990 /* pre-recursive callback for copying operation
991  * creates a destination directory where all source content fill be copied to */
992 static int copy_callback_pre(const char *from, const char *to)
993 {
994  struct stat st;
995 
996  if (check_the_same(from, to)) {
997  fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
999  }
1000 
1001  if (lstat(from, &st)) {
1002  perror("stat");
1004  }
1005 
1006  /* create a directory */
1007  if (mkdir(to, st.st_mode)) {
1008  perror("mkdir");
1010  }
1011 
1012  /* set proper owner and group on new directory */
1013  if (chown(to, st.st_uid, st.st_gid)) {
1014  perror("chown");
1016  }
1017 
1018  return RecursiveOp_Callback_OK;
1019 }
1020 
1021 static int copy_single_file(const char *from, const char *to)
1022 {
1023  FILE *from_stream, *to_stream;
1024  struct stat st;
1025  char buf[4096];
1026  size_t len;
1027 
1028  if (check_the_same(from, to)) {
1029  fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
1031  }
1032 
1033  if (lstat(from, &st)) {
1034  perror("lstat");
1036  }
1037 
1038  if (S_ISLNK(st.st_mode)) {
1039  /* symbolic links should be copied in special way */
1040  char *link_buffer;
1041  int need_free;
1042  ssize_t link_len;
1043 
1044  /* get large enough buffer to read link content */
1045  if ((st.st_size + 1) < sizeof(buf)) {
1046  link_buffer = buf;
1047  need_free = 0;
1048  }
1049  else {
1050  link_buffer = MEM_callocN(st.st_size + 2, "copy_single_file link_buffer");
1051  need_free = 1;
1052  }
1053 
1054  link_len = readlink(from, link_buffer, st.st_size + 1);
1055  if (link_len < 0) {
1056  perror("readlink");
1057 
1058  if (need_free) {
1059  MEM_freeN(link_buffer);
1060  }
1061 
1063  }
1064 
1065  link_buffer[link_len] = '\0';
1066 
1067  if (symlink(link_buffer, to)) {
1068  perror("symlink");
1069  if (need_free) {
1070  MEM_freeN(link_buffer);
1071  }
1073  }
1074 
1075  if (need_free) {
1076  MEM_freeN(link_buffer);
1077  }
1078 
1079  return RecursiveOp_Callback_OK;
1080  }
1081  if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
1082  /* copy special type of file */
1083  if (mknod(to, st.st_mode, st.st_rdev)) {
1084  perror("mknod");
1086  }
1087 
1088  if (set_permissions(to, &st)) {
1090  }
1091 
1092  return RecursiveOp_Callback_OK;
1093  }
1094  if (!S_ISREG(st.st_mode)) {
1095  fprintf(stderr, "Copying of this kind of files isn't supported yet\n");
1097  }
1098 
1099  from_stream = fopen(from, "rb");
1100  if (!from_stream) {
1101  perror("fopen");
1103  }
1104 
1105  to_stream = fopen(to, "wb");
1106  if (!to_stream) {
1107  perror("fopen");
1108  fclose(from_stream);
1110  }
1111 
1112  while ((len = fread(buf, 1, sizeof(buf), from_stream)) > 0) {
1113  fwrite(buf, 1, len, to_stream);
1114  }
1115 
1116  fclose(to_stream);
1117  fclose(from_stream);
1118 
1119  if (set_permissions(to, &st)) {
1121  }
1122 
1123  return RecursiveOp_Callback_OK;
1124 }
1125 
1126 /* Not used anywhere! */
1127 # if 0
1128 static int move_callback_pre(const char *from, const char *to)
1129 {
1130  int ret = rename(from, to);
1131 
1132  if (ret) {
1133  return copy_callback_pre(from, to);
1134  }
1135 
1137 }
1138 
1139 static int move_single_file(const char *from, const char *to)
1140 {
1141  int ret = rename(from, to);
1142 
1143  if (ret) {
1144  return copy_single_file(from, to);
1145  }
1146 
1147  return RecursiveOp_Callback_OK;
1148 }
1149 
1150 /* if *file represents a directory, moves all its contents into *to, else renames
1151  * file itself to *to. */
1152 int BLI_move(const char *file, const char *to)
1153 {
1154  int ret = recursive_operation(file, to, move_callback_pre, move_single_file, NULL);
1155 
1156  if (ret && ret != -1) {
1158  }
1159 
1160  return ret;
1161 }
1162 # endif
1163 
1164 static const char *check_destination(const char *file, const char *to)
1165 {
1166  struct stat st;
1167 
1168  if (!stat(to, &st)) {
1169  if (S_ISDIR(st.st_mode)) {
1170  char *str, *path;
1171  const char *filename;
1172  size_t len = 0;
1173 
1175  filename = BLI_path_slash_rfind(str);
1176 
1177  if (!filename) {
1178  MEM_freeN(str);
1179  return (char *)to;
1180  }
1181 
1182  /* skip slash */
1183  filename += 1;
1184 
1185  len = strlen(to) + strlen(filename) + 1;
1186  path = MEM_callocN(len + 1, "check_destination path");
1187  BLI_join_dirfile(path, len + 1, to, filename);
1188 
1189  MEM_freeN(str);
1190 
1191  return path;
1192  }
1193  }
1194 
1195  return to;
1196 }
1197 
1198 int BLI_copy(const char *file, const char *to)
1199 {
1200  const char *actual_to = check_destination(file, to);
1201  int ret;
1202 
1204 
1205  if (actual_to != to) {
1206  MEM_freeN((void *)actual_to);
1207  }
1208 
1209  return ret;
1210 }
1211 
1212 # if 0
1213 int BLI_create_symlink(const char *file, const char *to)
1214 {
1215  return symlink(to, file);
1216 }
1217 # endif
1218 
1220 {
1221  char *lslash;
1222  size_t size;
1223 # ifdef MAXPATHLEN
1224  char static_buf[MAXPATHLEN];
1225 # endif
1226  char *tmp;
1227  bool ret = true;
1228 
1229  if (BLI_is_dir(dirname)) {
1230  return true;
1231  }
1232  if (BLI_exists(dirname)) {
1233  return false;
1234  }
1235 
1236 # ifdef MAXPATHLEN
1237  size = MAXPATHLEN;
1238  tmp = static_buf;
1239 # else
1240  size = strlen(dirname) + 1;
1241  tmp = MEM_callocN(size, __func__);
1242 # endif
1243 
1244  BLI_strncpy(tmp, dirname, size);
1245 
1246  /* Avoids one useless recursion in case of '/foo/bar/' path... */
1247  BLI_path_slash_rstrip(tmp);
1248 
1249  lslash = (char *)BLI_path_slash_rfind(tmp);
1250  if (lslash) {
1251  /* Split about the last slash and recurse */
1252  *lslash = 0;
1253  if (!BLI_dir_create_recursive(tmp)) {
1254  ret = false;
1255  }
1256  }
1257 
1258 # ifndef MAXPATHLEN
1259  MEM_freeN(tmp);
1260 # endif
1261 
1262  if (ret) {
1263  ret = (mkdir(dirname, 0777) == 0);
1264  }
1265  return ret;
1266 }
1267 
1268 int BLI_rename(const char *from, const char *to)
1269 {
1270  if (!BLI_exists(from)) {
1271  return 1;
1272  }
1273 
1274  if (BLI_exists(to)) {
1275  if (BLI_delete(to, false, false)) {
1276  return 1;
1277  }
1278  }
1279 
1280  return rename(from, to);
1281 }
1282 
1283 #endif
#define BLI_assert(a)
Definition: BLI_assert.h:46
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:314
unsigned int BLI_filelist_dir_contents(const char *dir, struct direntry **r_filelist)
Definition: BLI_filelist.c:218
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:397
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
Definition: BLI_filelist.c:420
Some types for dealing with directories.
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
const char * BLI_path_basename(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:1653
bool BLI_path_is_rel(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:347
void BLI_split_dirfile(const char *string, char *dir, char *file, size_t dirlen, size_t filelen)
Definition: path_util.c:1465
#define FILE_MAX
#define FILENAME_IS_CURRPAR(_n)
void BLI_path_slash_rstrip(char *string) ATTR_NONNULL()
Definition: path_util.c:1791
size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path_first,...) ATTR_NONNULL(1
int BLI_path_slash_ensure(char *string) ATTR_NONNULL()
Definition: path_util.c:1780
void BLI_path_slash_native(char *path) ATTR_NONNULL()
Definition: path_util.c:1805
const char * BLI_path_slash_rfind(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:1765
#define FILE_MAXDIR
void BLI_join_dirfile(char *__restrict dst, size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL()
Definition: path_util.c:1531
int BLI_strcasecmp(const char *s1, const char *s2) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:623
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:42
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
unsigned int uint
Definition: BLI_sys_types.h:67
#define UNUSED(x)
#define STREQ(a, b)
Compatibility-like things for windows.
SSIZE_T ssize_t
Definition: BLI_winstuff.h:71
#define S_ISDIR(x)
Definition: BLI_winstuff.h:48
const char * dirname(char *path)
#define MAXPATHLEN
Definition: BLI_winstuff.h:42
#define S_ISREG(x)
Definition: BLI_winstuff.h:45
Read Guarded memory(de)allocation.
#define MEM_reallocN(vmemh, len)
ATTR_WARN_UNUSED_RESULT const BMFlagLayer const short oflag
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
StackEntry * from
FILE * file
SyclQueue void void size_t num_bytes void
int len
Definition: draw_manager.c:108
#define str(s)
static int set_permissions(const char *file, const struct stat *st)
Definition: fileops.c:975
FILE * BLI_fopen(const char *filepath, const char *mode)
Definition: fileops.c:906
int BLI_delete_soft(const char *file, const char **error_message)
Definition: fileops.c:947
int BLI_access(const char *filepath, int mode)
Definition: fileops.c:927
static bool check_the_same(const char *path_a, const char *path_b)
Definition: fileops.c:957
size_t BLI_file_unzstd_to_mem_at_pos(void *buf, size_t len, FILE *file, size_t file_offset)
Definition: fileops.c:96
int(* RecursiveOp_Callback)(const char *from, const char *to)
Definition: fileops.c:614
static const char * check_destination(const char *file, const char *to)
Definition: fileops.c:1164
bool BLI_file_magic_is_gzip(const char header[4])
Definition: fileops.c:133
int BLI_copy(const char *file, const char *to)
Definition: fileops.c:1198
bool BLI_dir_create_recursive(const char *dirname)
Definition: fileops.c:1219
int BLI_open(const char *filepath, int oflag, int pmode)
Definition: fileops.c:920
static char * strip_last_slash(const char *dir)
Definition: fileops.c:633
bool BLI_file_is_writable(const char *filepath)
Definition: fileops.c:167
bool BLI_file_magic_is_zstd(const char header[4])
Definition: fileops.c:140
static int copy_single_file(const char *from, const char *to)
Definition: fileops.c:1021
static int delete_single_file(const char *from, const char *UNUSED(to))
Definition: fileops.c:801
static void join_dirfile_alloc(char **dst, size_t *alloc_len, const char *dir, const char *file)
Definition: fileops.c:617
bool BLI_file_touch(const char *file)
Definition: fileops.c:192
static int recursive_operation(const char *startfrom, const char *startto, RecursiveOp_Callback callback_dir_pre, RecursiveOp_Callback callback_file, RecursiveOp_Callback callback_dir_post)
Definition: fileops.c:654
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:49
void * BLI_gzopen(const char *filepath, const char *mode)
Definition: fileops.c:913
static int delete_callback_post(const char *from, const char *UNUSED(to))
Definition: fileops.c:790
int BLI_rename(const char *from, const char *to)
Definition: fileops.c:1268
static int delete_soft(const char *file, const char **error_message)
Definition: fileops.c:853
@ RecursiveOp_Callback_StopRecurs
Definition: fileops.c:608
@ RecursiveOp_Callback_OK
Definition: fileops.c:605
@ RecursiveOp_Callback_Error
Definition: fileops.c:611
static int copy_callback_pre(const char *from, const char *to)
Definition: fileops.c:992
int BLI_delete(const char *file, bool dir, bool recursive)
Definition: fileops.c:934
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_global KernelShaderEvalInput * input
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static unsigned c
Definition: RandGen.cpp:83
bool remove(void *owner, const AttributeIDRef &attribute_id)
static const pxr::TfToken st("st", pxr::TfToken::Immortal)
return ret
unsigned int uint32_t
Definition: stdint.h:80
char * d_name
Definition: BLI_winstuff.h:80
const char * path
static int magic(const Tex *tex, const float texvec[3], TexResult *texres)
int umkdir(const char *pathname)
Definition: utf_winfunc.c:89
FILE * ufopen(const char *filename, const char *mode)
Definition: utf_winfunc.c:18
int uopen(const char *filename, int oflag, int pmode)
Definition: utf_winfunc.c:40
int uaccess(const char *filename, int mode)
Definition: utf_winfunc.c:60
int urename(const char *oldname, const char *newname)
Definition: utf_winfunc.c:74
#define UTF16_ENCODE(in8str)
Definition: utfconv.h:83
#define UTF16_UN_ENCODE(in8str)
Definition: utfconv.h:87
static FT_Error err