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