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