Code cleanup: use r_ prefix for return args
[blender.git] / source / blender / blenlib / intern / fileops.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/blenlib/intern/fileops.c
29  *  \ingroup bli
30  */
31
32
33 #include <stdlib.h>  /* malloc */
34 #include <string.h>
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39
40 #include <errno.h>
41
42 #include "zlib.h"
43
44 #ifdef WIN32
45 #  ifdef __MINGW32__
46 #    include <ctype.h>
47 #  endif
48 #  include <io.h>
49 #  include "BLI_winstuff.h"
50 #  include "BLI_callbacks.h"
51 #  include "utf_winfunc.h"
52 #  include "utfconv.h"
53 #else
54 #  include <unistd.h> // for read close
55 #  include <sys/param.h>
56 #  include <dirent.h>
57 #  include <unistd.h>
58 #  include <sys/stat.h>
59 #endif
60
61 #include "MEM_guardedalloc.h"
62
63 #include "BLI_utildefines.h"
64 #include "BLI_listbase.h"
65 #include "BLI_string.h"
66 #include "BLI_path_util.h"
67 #include "BLI_fileops.h"
68 #include "BLI_sys_types.h" // for intptr_t support
69
70
71 /* gzip the file in from and write it to "to". 
72  * return -1 if zlib fails, -2 if the originating file does not exist
73  * note: will remove the "from" file
74  */
75 int BLI_file_gzip(const char *from, const char *to)
76 {
77         char buffer[10240];
78         int file;
79         int readsize = 0;
80         int rval = 0, err;
81         gzFile gzfile;
82
83         /* level 1 is very close to 3 (the default) in terms of file size,
84          * but about twice as fast, best use for speedy saving - campbell */
85         gzfile = BLI_gzopen(to, "wb1");
86         if (gzfile == NULL)
87                 return -1;
88         file = BLI_open(from, O_BINARY | O_RDONLY, 0);
89         if (file < 0)
90                 return -2;
91
92         while (1) {
93                 readsize = read(file, buffer, sizeof(buffer));
94
95                 if (readsize < 0) {
96                         rval = -2; /* error happened in reading */
97                         fprintf(stderr, "Error reading file %s: %s.\n", from, strerror(errno));
98                         break;
99                 }
100                 else if (readsize == 0)
101                         break;  /* done reading */
102                 
103                 if (gzwrite(gzfile, buffer, readsize) <= 0) {
104                         rval = -1; /* error happened in writing */
105                         fprintf(stderr, "Error writing gz file %s: %s.\n", to, gzerror(gzfile, &err));
106                         break;
107                 }
108         }
109         
110         gzclose(gzfile);
111         close(file);
112
113         return rval;
114 }
115
116 /* gzip the file in from_file and write it to memory to_mem, at most size bytes.
117  * return the unziped size
118  */
119 char *BLI_file_ungzip_to_mem(const char *from_file, int *r_size)
120 {
121         gzFile gzfile;
122         int readsize, size, alloc_size = 0;
123         char *mem = NULL;
124         const int chunk_size = 512 * 1024;
125
126         size = 0;
127
128         gzfile = BLI_gzopen(from_file, "rb");
129         for (;; ) {
130                 if (mem == NULL) {
131                         mem = MEM_callocN(chunk_size, "BLI_ungzip_to_mem");
132                         alloc_size = chunk_size;
133                 }
134                 else {
135                         mem = MEM_reallocN(mem, size + chunk_size);
136                         alloc_size += chunk_size;
137                 }
138
139                 readsize = gzread(gzfile, mem + size, chunk_size);
140                 if (readsize > 0) {
141                         size += readsize;
142                 }
143                 else {
144                         break;
145                 }
146         }
147         
148         gzclose(gzfile);
149
150         if (size == 0) {
151                 MEM_freeN(mem);
152                 mem = NULL;
153         }
154         else if (alloc_size != size)
155                 mem = MEM_reallocN(mem, size);
156
157         *r_size = size;
158
159         return mem;
160 }
161
162 /**
163  * Returns true if the file with the specified name can be written.
164  * This implementation uses access(2), which makes the check according
165  * to the real UID and GID of the process, not its effective UID and GID.
166  * This shouldn't matter for Blender, which is not going to run privileged
167  * anyway.
168  */
169 bool BLI_file_is_writable(const char *filename)
170 {
171         bool writable;
172         if (BLI_access(filename, W_OK) == 0) {
173                 /* file exists and I can write to it */
174                 writable = true;
175         }
176         else if (errno != ENOENT) {
177                 /* most likely file or containing directory cannot be accessed */
178                 writable = false;
179         }
180         else {
181                 /* file doesn't exist -- check I can create it in parent directory */
182                 char parent[FILE_MAX];
183                 BLI_split_dirfile(filename, parent, NULL, sizeof(parent), 0);
184 #ifdef WIN32
185                 /* windows does not have X_OK */
186                 writable = BLI_access(parent, W_OK) == 0;
187 #else
188                 writable = BLI_access(parent, X_OK | W_OK) == 0;
189 #endif
190         }
191         return writable;
192 }
193
194 /**
195  * Creates the file with nothing in it, or updates its last-modified date if it already exists.
196  * Returns true if successful. (like the unix touch command)
197  */
198 bool BLI_file_touch(const char *file)
199 {
200         FILE *f = BLI_fopen(file, "r+b");
201         if (f != NULL) {
202                 int c = getc(f);
203                 rewind(f);
204                 putc(c, f);
205         }
206         else {
207                 f = BLI_fopen(file, "wb");
208         }
209         if (f) {
210                 fclose(f);
211                 return true;
212         }
213         return false;
214 }
215
216 #ifdef WIN32
217
218 static void callLocalErrorCallBack(const char *err)
219 {
220         printf("%s\n", err);
221 }
222
223 static char str[MAXPATHLEN + 12];
224
225 FILE *BLI_fopen(const char *filename, const char *mode)
226 {
227         return ufopen(filename, mode);
228 }
229
230 void BLI_get_short_name(char short_name[256], const char *filename)
231 {
232         wchar_t short_name_16[256];
233         int i = 0;
234
235         UTF16_ENCODE(filename);
236
237         GetShortPathNameW(filename_16, short_name_16, 256);
238
239         for (i = 0; i < 256; i++) {
240                 short_name[i] = (char)short_name_16[i];
241         }
242
243         UTF16_UN_ENCODE(filename);
244 }
245
246 void *BLI_gzopen(const char *filename, const char *mode)
247 {
248         gzFile gzfile;
249
250         if (!filename || !mode) {
251                 return 0;
252         }
253         else {
254                 /* xxx Creates file before transcribing the path */
255                 if (mode[0] == 'w')
256                         fclose(ufopen(filename, "a"));
257
258                 /* temporary #if until we update all libraries to 1.2.7
259                  * for correct wide char path handling */
260 #if ZLIB_VERNUM >= 0x1270 && !defined(FREE_WINDOWS)
261                 UTF16_ENCODE(filename);
262
263                 gzfile = gzopen_w(filename_16, mode);
264
265                 UTF16_UN_ENCODE(filename);
266 #else
267                 {
268                         char short_name[256];
269                         BLI_get_short_name(short_name, filename);
270                         gzfile = gzopen(short_name, mode);
271                 }
272 #endif
273         }
274
275         return gzfile;
276 }
277
278 int   BLI_open(const char *filename, int oflag, int pmode)
279 {
280         return uopen(filename, oflag, pmode);
281 }
282
283 int   BLI_access(const char *filename, int mode)
284 {
285         return uaccess(filename, mode);
286 }
287
288 int BLI_delete(const char *file, bool dir, bool recursive)
289 {
290         int err;
291         
292         UTF16_ENCODE(file);
293
294         if (recursive) {
295                 callLocalErrorCallBack("Recursive delete is unsupported on Windows");
296                 err = 1;
297         }
298         else if (dir) {
299                 err = !RemoveDirectoryW(file_16);
300                 if (err) printf("Unable to remove directory");
301         }
302         else {
303                 err = !DeleteFileW(file_16);
304                 if (err) callLocalErrorCallBack("Unable to delete file");
305         }
306
307         UTF16_UN_ENCODE(file);
308
309         return err;
310 }
311
312 /* Not used anywhere! */
313 #if 0
314 int BLI_move(const char *file, const char *to)
315 {
316         int err;
317
318         /* windows doesn't support moving to a directory
319          * it has to be 'mv filename filename' and not
320          * 'mv filename destdir' */
321
322         BLI_strncpy(str, to, sizeof(str));
323         /* points 'to' to a directory ? */
324         if (BLI_last_slash(str) == (str + strlen(str) - 1)) {
325                 if (BLI_last_slash(file) != NULL) {
326                         strcat(str, BLI_last_slash(file) + 1);
327                 }
328         }
329         
330         UTF16_ENCODE(file);
331         UTF16_ENCODE(str);
332         err = !MoveFileW(file_16, str_16);
333         UTF16_UN_ENCODE(str);
334         UTF16_UN_ENCODE(file);
335
336         if (err) {
337                 callLocalErrorCallBack("Unable to move file");
338                 printf(" Move from '%s' to '%s' failed\n", file, str);
339         }
340
341         return err;
342 }
343 #endif
344
345 int BLI_copy(const char *file, const char *to)
346 {
347         int err;
348
349         /* windows doesn't support copying to a directory
350          * it has to be 'cp filename filename' and not
351          * 'cp filename destdir' */
352
353         BLI_strncpy(str, to, sizeof(str));
354         /* points 'to' to a directory ? */
355         if (BLI_last_slash(str) == (str + strlen(str) - 1)) {
356                 if (BLI_last_slash(file) != NULL) {
357                         strcat(str, BLI_last_slash(file) + 1);
358                 }
359         }
360
361         UTF16_ENCODE(file);
362         UTF16_ENCODE(str);
363         err = !CopyFileW(file_16, str_16, FALSE);
364         UTF16_UN_ENCODE(str);
365         UTF16_UN_ENCODE(file);
366
367         if (err) {
368                 callLocalErrorCallBack("Unable to copy file!");
369                 printf(" Copy from '%s' to '%s' failed\n", file, str);
370         }
371
372         return err;
373 }
374
375 int BLI_create_symlink(const char *file, const char *to)
376 {
377         callLocalErrorCallBack("Linking files is unsupported on Windows");
378         (void)file;
379         (void)to;
380         return 1;
381 }
382
383 void BLI_dir_create_recursive(const char *dirname)
384 {
385         char *lslash;
386         char tmp[MAXPATHLEN];
387
388         /* First remove possible slash at the end of the dirname.
389          * This routine otherwise tries to create
390          * blah1/blah2/ (with slash) after creating
391          * blah1/blah2 (without slash) */
392
393         BLI_strncpy(tmp, dirname, sizeof(tmp));
394         lslash = (char *)BLI_last_slash(tmp);
395
396         if (lslash && (*(lslash + 1) == '\0')) {
397                 *lslash = '\0';
398         }
399
400         /* check special case "c:\foo", don't try create "c:", harmless but prints an error below */
401         if (isalpha(tmp[0]) && (tmp[1] == ':') && tmp[2] == '\0') return;
402
403         if (BLI_exists(tmp)) return;
404
405         lslash = (char *)BLI_last_slash(tmp);
406
407         if (lslash) {
408                 /* Split about the last slash and recurse */
409                 *lslash = 0;
410                 BLI_dir_create_recursive(tmp);
411         }
412
413         if (dirname[0]) {  /* patch, this recursive loop tries to create a nameless directory */
414                 if (umkdir(dirname) == -1) {
415                         printf("Unable to create directory %s\n", dirname);
416                 }
417         }
418 }
419
420 int BLI_rename(const char *from, const char *to)
421 {
422         if (!BLI_exists(from)) return 0;
423
424         /* make sure the filenames are different (case insensitive) before removing */
425         if (BLI_exists(to) && BLI_strcasecmp(from, to))
426                 if (BLI_delete(to, false, false)) return 1;
427         
428         return urename(from, to);
429 }
430
431 #else /* The UNIX world */
432
433 /* results from recursive_operation and its callbacks */
434 enum {
435         /* operation succeeded */
436         RecursiveOp_Callback_OK = 0,
437
438         /* operation requested not to perform recursive digging for current path */
439         RecursiveOp_Callback_StopRecurs = 1,
440
441         /* error occured in callback and recursive walking should stop immediately */
442         RecursiveOp_Callback_Error = 2
443 };
444
445 typedef int (*RecursiveOp_Callback)(const char *from, const char *to);
446
447 /* appending of filename to dir (ensures for buffer size before appending) */
448 static void join_dirfile_alloc(char **dst, size_t *alloc_len, const char *dir, const char *file)
449 {
450         size_t len = strlen(dir) + strlen(file) + 1;
451
452         if (*dst == NULL)
453                 *dst = MEM_mallocN(len + 1, "join_dirfile_alloc path");
454         else if (*alloc_len < len)
455                 *dst = MEM_reallocN(*dst, len + 1);
456
457         *alloc_len = len;
458
459         BLI_join_dirfile(*dst, len + 1, dir, file);
460 }
461
462 static char *strip_last_slash(const char *dir)
463 {
464         char *result = BLI_strdup(dir);
465         BLI_del_slash(result);
466
467         return result;
468 }
469
470
471
472 /**
473  * Scans \a startfrom, generating a corresponding destination name for each item found by
474  * prefixing it with startto, recursively scanning subdirectories, and invoking the specified
475  * callbacks for files and subdirectories found as appropriate.
476  *
477  * \param startfrom  Top-level source path.
478  * \param startto  Top-level destination path.
479  * \param callback_dir_pre  Optional, to be invoked before entering a subdirectory, can return
480  *                          RecursiveOp_Callback_StopRecurs to skip the subdirectory.
481  * \param callback_file  Optional, to be invoked on each file found.
482  * \param callback_dir_post  optional, to be invoked after leaving a subdirectory.
483  * \return
484  */
485 static int recursive_operation(const char *startfrom, const char *startto,
486                                RecursiveOp_Callback callback_dir_pre,
487                                RecursiveOp_Callback callback_file, RecursiveOp_Callback callback_dir_post)
488 {
489         struct stat st;
490         char *from = NULL, *to = NULL;
491         char *from_path = NULL, *to_path = NULL;
492         struct dirent **dirlist = NULL;
493         size_t from_alloc_len = -1, to_alloc_len = -1;
494         int i, n, ret = 0;
495
496         do {  /* once */
497                 /* ensure there's no trailing slash in file path */
498                 from = strip_last_slash(startfrom);
499                 if (startto)
500                         to = strip_last_slash(startto);
501
502                 ret = lstat(from, &st);
503                 if (ret < 0)
504                         /* source wasn't found, nothing to operate with */
505                         break;
506
507                 if (!S_ISDIR(st.st_mode)) {
508                         /* source isn't a directory, can't do recursive walking for it,
509                          * so just call file callback and leave */
510                         if (callback_file != NULL) {
511                                 ret = callback_file(from, to);
512                                 if (ret != RecursiveOp_Callback_OK)
513                                         ret = -1;
514                         }
515                         break;
516                 }
517
518                 n = scandir(startfrom, &dirlist, NULL, alphasort);
519                 if (n < 0) {
520                         /* error opening directory for listing */
521                         perror("scandir");
522                         ret = -1;
523                         break;
524                 }
525
526                 if (callback_dir_pre != NULL) {
527                         ret = callback_dir_pre(from, to);
528                         if (ret != RecursiveOp_Callback_OK) {
529                                 if (ret == RecursiveOp_Callback_StopRecurs)
530                                         /* callback requested not to perform recursive walking, not an error */
531                                         ret = 0;
532                                 else
533                                         ret = -1;
534                                 break;
535                         }
536                 }
537
538                 for (i = 0; i < n; i++) {
539                         const struct dirent * const dirent = dirlist[i];
540
541                         if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, ".."))
542                                 continue;
543
544                         join_dirfile_alloc(&from_path, &from_alloc_len, from, dirent->d_name);
545                         if (to)
546                                 join_dirfile_alloc(&to_path, &to_alloc_len, to, dirent->d_name);
547
548                         if (dirent->d_type == DT_DIR) {
549                                 /* recursively dig into a subfolder */
550                                 ret = recursive_operation(from_path, to_path, callback_dir_pre, callback_file, callback_dir_post);
551                         }
552                         else if (callback_file != NULL) {
553                                 ret = callback_file(from_path, to_path);
554                                 if (ret != RecursiveOp_Callback_OK)
555                                         ret = -1;
556                         }
557
558                         if (ret != 0)
559                                 break;
560                 }
561                 if (ret != 0)
562                         break;
563
564                 if (callback_dir_post != NULL) {
565                         ret = callback_dir_post(from, to);
566                         if (ret != RecursiveOp_Callback_OK)
567                                 ret = -1;
568                 }
569         }
570         while (false);
571
572         if (dirlist != NULL) {
573                 for (i = 0; i < n; i++) {
574                         free(dirlist[i]);
575                 }
576                 free(dirlist);
577         }
578         if (from_path != NULL)
579                 MEM_freeN(from_path);
580         if (to_path != NULL)
581                 MEM_freeN(to_path);
582         if (from != NULL)
583                 MEM_freeN(from);
584         if (to != NULL)
585                 MEM_freeN(to);
586
587         return ret;
588 }
589
590 static int delete_callback_post(const char *from, const char *UNUSED(to))
591 {
592         if (rmdir(from)) {
593                 perror("rmdir");
594
595                 return RecursiveOp_Callback_Error;
596         }
597
598         return RecursiveOp_Callback_OK;
599 }
600
601 static int delete_single_file(const char *from, const char *UNUSED(to))
602 {
603         if (unlink(from)) {
604                 perror("unlink");
605
606                 return RecursiveOp_Callback_Error;
607         }
608
609         return RecursiveOp_Callback_OK;
610 }
611
612 FILE *BLI_fopen(const char *filename, const char *mode)
613 {
614         return fopen(filename, mode);
615 }
616
617 void *BLI_gzopen(const char *filename, const char *mode)
618 {
619         return gzopen(filename, mode);
620 }
621
622 int BLI_open(const char *filename, int oflag, int pmode)
623 {
624         return open(filename, oflag, pmode);
625 }
626
627 int   BLI_access(const char *filename, int mode)
628 {
629         return access(filename, mode);
630 }
631
632
633 /**
634  * Deletes the specified file or directory (depending on dir), optionally
635  * doing recursive delete of directory contents.
636  */
637 int BLI_delete(const char *file, bool dir, bool recursive)
638 {
639         if (strchr(file, '"')) {
640                 printf("Error: not deleted file %s because of quote!\n", file);
641         }
642         else {
643                 if (recursive) {
644                         return recursive_operation(file, NULL, NULL, delete_single_file, delete_callback_post);
645                 }
646                 else if (dir) {
647                         return rmdir(file);
648                 }
649                 else {
650                         return remove(file); //BLI_snprintf(str, sizeof(str), "/bin/rm -f \"%s\"", file);
651                 }
652         }
653         return -1;
654 }
655
656 /**
657  * Do the two paths denote the same filesystem object?
658  */
659 static bool check_the_same(const char *path_a, const char *path_b)
660 {
661         struct stat st_a, st_b;
662
663         if (lstat(path_a, &st_a))
664                 return false;
665
666         if (lstat(path_b, &st_b))
667                 return false;
668
669         return st_a.st_dev == st_b.st_dev && st_a.st_ino == st_b.st_ino;
670 }
671
672 /**
673  * Sets the mode and ownership of file to the values from st.
674  */
675 static int set_permissions(const char *file, const struct stat *st)
676 {
677         if (chown(file, st->st_uid, st->st_gid)) {
678                 perror("chown");
679                 return -1;
680         }
681
682         if (chmod(file, st->st_mode)) {
683                 perror("chmod");
684                 return -1;
685         }
686
687         return 0;
688 }
689
690 /* pre-recursive callback for copying operation
691  * creates a destination directory where all source content fill be copied to */
692 static int copy_callback_pre(const char *from, const char *to)
693 {
694         struct stat st;
695
696         if (check_the_same(from, to)) {
697                 fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
698                 return RecursiveOp_Callback_Error;
699         }
700
701         if (lstat(from, &st)) {
702                 perror("stat");
703                 return RecursiveOp_Callback_Error;
704         }
705
706         /* create a directory */
707         if (mkdir(to, st.st_mode)) {
708                 perror("mkdir");
709                 return RecursiveOp_Callback_Error;
710         }
711
712         /* set proper owner and group on new directory */
713         if (chown(to, st.st_uid, st.st_gid)) {
714                 perror("chown");
715                 return RecursiveOp_Callback_Error;
716         }
717
718         return RecursiveOp_Callback_OK;
719 }
720
721 static int copy_single_file(const char *from, const char *to)
722 {
723         FILE *from_stream, *to_stream;
724         struct stat st;
725         char buf[4096];
726         size_t len;
727
728         if (check_the_same(from, to)) {
729                 fprintf(stderr, "%s: '%s' is the same as '%s'\n", __func__, from, to);
730                 return RecursiveOp_Callback_Error;
731         }
732
733         if (lstat(from, &st)) {
734                 perror("lstat");
735                 return RecursiveOp_Callback_Error;
736         }
737
738         if (S_ISLNK(st.st_mode)) {
739                 /* symbolic links should be copied in special way */
740                 char *link_buffer;
741                 int need_free;
742                 ssize_t link_len;
743
744                 /* get large enough buffer to read link content */
745                 if (st.st_size < sizeof(buf)) {
746                         link_buffer = buf;
747                         need_free = 0;
748                 }
749                 else {
750                         link_buffer = MEM_callocN(st.st_size + 2, "copy_single_file link_buffer");
751                         need_free = 1;
752                 }
753
754                 link_len = readlink(from, link_buffer, st.st_size + 1);
755                 if (link_len < 0) {
756                         perror("readlink");
757
758                         if (need_free) MEM_freeN(link_buffer);
759
760                         return RecursiveOp_Callback_Error;
761                 }
762
763                 link_buffer[link_len] = 0;
764
765                 if (symlink(link_buffer, to)) {
766                         perror("symlink");
767                         if (need_free) MEM_freeN(link_buffer);
768                         return RecursiveOp_Callback_Error;
769                 }
770
771                 if (need_free)
772                         MEM_freeN(link_buffer);
773
774                 return RecursiveOp_Callback_OK;
775         }
776         else if (S_ISCHR(st.st_mode) ||
777                  S_ISBLK(st.st_mode) ||
778                  S_ISFIFO(st.st_mode) ||
779                  S_ISSOCK(st.st_mode))
780         {
781                 /* copy special type of file */
782                 if (mknod(to, st.st_mode, st.st_rdev)) {
783                         perror("mknod");
784                         return RecursiveOp_Callback_Error;
785                 }
786
787                 if (set_permissions(to, &st))
788                         return RecursiveOp_Callback_Error;
789
790                 return RecursiveOp_Callback_OK;
791         }
792         else if (!S_ISREG(st.st_mode)) {
793                 fprintf(stderr, "Copying of this kind of files isn't supported yet\n");
794                 return RecursiveOp_Callback_Error;
795         }
796
797         from_stream = fopen(from, "rb");
798         if (!from_stream) {
799                 perror("fopen");
800                 return RecursiveOp_Callback_Error;
801         }
802
803         to_stream = fopen(to, "wb");
804         if (!to_stream) {
805                 perror("fopen");
806                 fclose(from_stream);
807                 return RecursiveOp_Callback_Error;
808         }
809
810         while ((len = fread(buf, 1, sizeof(buf), from_stream)) > 0) {
811                 fwrite(buf, 1, len, to_stream);
812         }
813
814         fclose(to_stream);
815         fclose(from_stream);
816
817         if (set_permissions(to, &st))
818                 return RecursiveOp_Callback_Error;
819
820         return RecursiveOp_Callback_OK;
821 }
822
823 /* Not used anywhere! */
824 #if 0
825 static int move_callback_pre(const char *from, const char *to)
826 {
827         int ret = rename(from, to);
828
829         if (ret)
830                 return copy_callback_pre(from, to);
831
832         return RecursiveOp_Callback_StopRecurs;
833 }
834
835 static int move_single_file(const char *from, const char *to)
836 {
837         int ret = rename(from, to);
838
839         if (ret)
840                 return copy_single_file(from, to);
841
842         return RecursiveOp_Callback_OK;
843 }
844
845 /* if *file represents a directory, moves all its contents into *to, else renames
846  * file itself to *to. */
847 int BLI_move(const char *file, const char *to)
848 {
849         int ret = recursive_operation(file, to, move_callback_pre, move_single_file, NULL);
850
851         if (ret && ret != -1) {
852                 return recursive_operation(file, NULL, NULL, delete_single_file, delete_callback_post);
853         }
854
855         return ret;
856 }
857 #endif
858
859 static char *check_destination(const char *file, const char *to)
860 {
861         struct stat st;
862
863         if (!stat(to, &st)) {
864                 if (S_ISDIR(st.st_mode)) {
865                         char *str, *path;
866                         const char *filename;
867                         size_t len = 0;
868
869                         str = strip_last_slash(file);
870                         filename = BLI_last_slash(str);
871
872                         if (!filename) {
873                                 MEM_freeN(str);
874                                 return (char *)to;
875                         }
876
877                         /* skip slash */
878                         filename += 1;
879
880                         len = strlen(to) + strlen(filename) + 1;
881                         path = MEM_callocN(len + 1, "check_destination path");
882                         BLI_join_dirfile(path, len + 1, to, filename);
883
884                         MEM_freeN(str);
885
886                         return path;
887                 }
888         }
889
890         return (char *)to;
891 }
892
893 int BLI_copy(const char *file, const char *to)
894 {
895         char *actual_to = check_destination(file, to);
896         int ret;
897
898         ret = recursive_operation(file, actual_to, copy_callback_pre, copy_single_file, NULL);
899
900         if (actual_to != to)
901                 MEM_freeN(actual_to);
902
903         return ret;
904 }
905
906 int BLI_create_symlink(const char *file, const char *to)
907 {
908         return symlink(to, file);
909 }
910
911 void BLI_dir_create_recursive(const char *dirname)
912 {
913         char *lslash;
914         size_t size;
915 #ifdef MAXPATHLEN
916         char static_buf[MAXPATHLEN];
917 #endif
918         char *tmp;
919
920         if (BLI_exists(dirname)) return;
921
922 #ifdef MAXPATHLEN
923         size = MAXPATHLEN;
924         tmp = static_buf;
925 #else
926         size = strlen(dirname) + 1;
927         tmp = MEM_callocN(size, __func__);
928 #endif
929
930         BLI_strncpy(tmp, dirname, size);
931                 
932         lslash = (char *)BLI_last_slash(tmp);
933         if (lslash) {
934                 /* Split about the last slash and recurse */
935                 *lslash = 0;
936                 BLI_dir_create_recursive(tmp);
937         }
938
939 #ifndef MAXPATHLEN
940         MEM_freeN(tmp);
941 #endif
942
943         mkdir(dirname, 0777);
944 }
945
946 int BLI_rename(const char *from, const char *to)
947 {
948         if (!BLI_exists(from)) return 0;
949         
950         if (BLI_exists(to))
951                 if (BLI_delete(to, false, false)) return 1;
952
953         return rename(from, to);
954 }
955
956 #endif