Cleanup: style, use braces for blenkernel
[blender.git] / source / blender / blenkernel / intern / bpath.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup bli
19  */
20
21 /* TODO,
22  * currently there are some cases we don't support.
23  * - passing output paths to the visitor?, like render out.
24  * - passing sequence strips with many images.
25  * - passing directory paths - visitors don't know which path is a dir or a file.
26  * */
27
28 #include <sys/stat.h>
29
30 #include <string.h>
31 #include <assert.h>
32
33 /* path/file handling stuff */
34 #ifndef WIN32
35 #  include <dirent.h>
36 #  include <unistd.h>
37 #else
38 #  include <io.h>
39 #  include "BLI_winstuff.h"
40 #endif
41
42 #include "MEM_guardedalloc.h"
43
44 #include "DNA_brush_types.h"
45 #include "DNA_cachefile_types.h"
46 #include "DNA_image_types.h"
47 #include "DNA_mesh_types.h"
48 #include "DNA_modifier_types.h"
49 #include "DNA_movieclip_types.h"
50 #include "DNA_object_fluidsim_types.h"
51 #include "DNA_object_force_types.h"
52 #include "DNA_object_types.h"
53 #include "DNA_particle_types.h"
54 #include "DNA_sequence_types.h"
55 #include "DNA_sound_types.h"
56 #include "DNA_text_types.h"
57 #include "DNA_material_types.h"
58 #include "DNA_node_types.h"
59 #include "DNA_texture_types.h"
60 #include "DNA_vfont_types.h"
61 #include "DNA_scene_types.h"
62 #include "DNA_smoke_types.h"
63 #include "DNA_freestyle_types.h"
64
65 #include "BLI_blenlib.h"
66 #include "BLI_utildefines.h"
67
68 #include "BKE_font.h"
69 #include "BKE_library.h"
70 #include "BKE_main.h"
71 #include "BKE_node.h"
72 #include "BKE_report.h"
73 #include "BKE_sequencer.h"
74 #include "BKE_image.h"
75
76 #include "BKE_bpath.h" /* own include */
77
78 #include "CLG_log.h"
79
80 #ifndef _MSC_VER
81 #  include "BLI_strict_flags.h"
82 #endif
83
84 static CLG_LogRef LOG = {"bke.bpath"};
85
86 static bool checkMissingFiles_visit_cb(void *userdata,
87                                        char *UNUSED(path_dst),
88                                        const char *path_src)
89 {
90   ReportList *reports = (ReportList *)userdata;
91
92   if (!BLI_exists(path_src)) {
93     BKE_reportf(reports, RPT_WARNING, "Path '%s' not found", path_src);
94   }
95
96   return false;
97 }
98
99 /* high level function */
100 void BKE_bpath_missing_files_check(Main *bmain, ReportList *reports)
101 {
102   BKE_bpath_traverse_main(bmain,
103                           checkMissingFiles_visit_cb,
104                           BKE_BPATH_TRAVERSE_ABS | BKE_BPATH_TRAVERSE_SKIP_PACKED,
105                           reports);
106 }
107
108 typedef struct BPathRemap_Data {
109   const char *basedir;
110   ReportList *reports;
111
112   int count_tot;
113   int count_changed;
114   int count_failed;
115 } BPathRemap_Data;
116
117 static bool bpath_relative_convert_visit_cb(void *userdata, char *path_dst, const char *path_src)
118 {
119   BPathRemap_Data *data = (BPathRemap_Data *)userdata;
120
121   data->count_tot++;
122
123   if (BLI_path_is_rel(path_src)) {
124     return false; /* already relative */
125   }
126   else {
127     strcpy(path_dst, path_src);
128     BLI_path_rel(path_dst, data->basedir);
129     if (BLI_path_is_rel(path_dst)) {
130       data->count_changed++;
131     }
132     else {
133       BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made relative", path_src);
134       data->count_failed++;
135     }
136     return true;
137   }
138 }
139
140 void BKE_bpath_relative_convert(Main *bmain, const char *basedir, ReportList *reports)
141 {
142   BPathRemap_Data data = {NULL};
143   const int flag = BKE_BPATH_TRAVERSE_SKIP_LIBRARY;
144
145   if (basedir[0] == '\0') {
146     CLOG_ERROR(&LOG, "basedir='', this is a bug");
147     return;
148   }
149
150   data.basedir = basedir;
151   data.reports = reports;
152
153   BKE_bpath_traverse_main(bmain, bpath_relative_convert_visit_cb, flag, (void *)&data);
154
155   BKE_reportf(reports,
156               data.count_failed ? RPT_WARNING : RPT_INFO,
157               "Total files %d | Changed %d | Failed %d",
158               data.count_tot,
159               data.count_changed,
160               data.count_failed);
161 }
162
163 static bool bpath_absolute_convert_visit_cb(void *userdata, char *path_dst, const char *path_src)
164 {
165   BPathRemap_Data *data = (BPathRemap_Data *)userdata;
166
167   data->count_tot++;
168
169   if (BLI_path_is_rel(path_src) == false) {
170     return false; /* already absolute */
171   }
172   else {
173     strcpy(path_dst, path_src);
174     BLI_path_abs(path_dst, data->basedir);
175     if (BLI_path_is_rel(path_dst) == false) {
176       data->count_changed++;
177     }
178     else {
179       BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src);
180       data->count_failed++;
181     }
182     return true;
183   }
184 }
185
186 /* similar to BKE_bpath_relative_convert - keep in sync! */
187 void BKE_bpath_absolute_convert(Main *bmain, const char *basedir, ReportList *reports)
188 {
189   BPathRemap_Data data = {NULL};
190   const int flag = BKE_BPATH_TRAVERSE_SKIP_LIBRARY;
191
192   if (basedir[0] == '\0') {
193     CLOG_ERROR(&LOG, "basedir='', this is a bug");
194     return;
195   }
196
197   data.basedir = basedir;
198   data.reports = reports;
199
200   BKE_bpath_traverse_main(bmain, bpath_absolute_convert_visit_cb, flag, (void *)&data);
201
202   BKE_reportf(reports,
203               data.count_failed ? RPT_WARNING : RPT_INFO,
204               "Total files %d | Changed %d | Failed %d",
205               data.count_tot,
206               data.count_changed,
207               data.count_failed);
208 }
209
210 /**
211  * find this file recursively, use the biggest file so thumbnails don't get used by mistake
212  * \param filename_new: the path will be copied here, caller must initialize as empty string.
213  * \param dirname: subdir to search
214  * \param filename: set this filename
215  * \param filesize: filesize for the file
216  *
217  * \returns found: 1/0.
218  */
219 #define MAX_RECUR 16
220 static bool missing_files_find__recursive(char *filename_new,
221                                           const char *dirname,
222                                           const char *filename,
223                                           int64_t *r_filesize,
224                                           int *r_recur_depth)
225 {
226   /* file searching stuff */
227   DIR *dir;
228   struct dirent *de;
229   BLI_stat_t status;
230   char path[FILE_MAX];
231   int64_t size;
232   bool found = false;
233
234   dir = opendir(dirname);
235
236   if (dir == NULL) {
237     return found;
238   }
239
240   if (*r_filesize == -1) {
241     *r_filesize = 0; /* dir opened fine */
242   }
243
244   while ((de = readdir(dir)) != NULL) {
245
246     if (FILENAME_IS_CURRPAR(de->d_name)) {
247       continue;
248     }
249
250     BLI_join_dirfile(path, sizeof(path), dirname, de->d_name);
251
252     if (BLI_stat(path, &status) == -1) {
253       continue; /* cant stat, don't bother with this file, could print debug info here */
254     }
255
256     if (S_ISREG(status.st_mode)) {                              /* is file */
257       if (BLI_path_ncmp(filename, de->d_name, FILE_MAX) == 0) { /* name matches */
258         /* open the file to read its size */
259         size = status.st_size;
260         if ((size > 0) && (size > *r_filesize)) { /* find the biggest file */
261           *r_filesize = size;
262           BLI_strncpy(filename_new, path, FILE_MAX);
263           found = true;
264         }
265       }
266     }
267     else if (S_ISDIR(status.st_mode)) { /* is subdir */
268       if (*r_recur_depth <= MAX_RECUR) {
269         (*r_recur_depth)++;
270         found |= missing_files_find__recursive(
271             filename_new, path, filename, r_filesize, r_recur_depth);
272         (*r_recur_depth)--;
273       }
274     }
275   }
276   closedir(dir);
277   return found;
278 }
279
280 typedef struct BPathFind_Data {
281   const char *basedir;
282   const char *searchdir;
283   ReportList *reports;
284   bool find_all;
285 } BPathFind_Data;
286
287 static bool missing_files_find__visit_cb(void *userdata, char *path_dst, const char *path_src)
288 {
289   BPathFind_Data *data = (BPathFind_Data *)userdata;
290   char filename_new[FILE_MAX];
291
292   int64_t filesize = -1;
293   int recur_depth = 0;
294   bool found;
295
296   if (data->find_all == false) {
297     if (BLI_exists(path_src)) {
298       return false;
299     }
300   }
301
302   filename_new[0] = '\0';
303
304   found = missing_files_find__recursive(
305       filename_new, data->searchdir, BLI_path_basename(path_src), &filesize, &recur_depth);
306
307   if (filesize == -1) { /* could not open dir */
308     BKE_reportf(data->reports,
309                 RPT_WARNING,
310                 "Could not open directory '%s'",
311                 BLI_path_basename(data->searchdir));
312     return false;
313   }
314   else if (found == false) {
315     BKE_reportf(data->reports,
316                 RPT_WARNING,
317                 "Could not find '%s' in '%s'",
318                 BLI_path_basename(path_src),
319                 data->searchdir);
320     return false;
321   }
322   else {
323     bool was_relative = BLI_path_is_rel(path_dst);
324
325     BLI_strncpy(path_dst, filename_new, FILE_MAX);
326
327     /* keep path relative if the previous one was relative */
328     if (was_relative) {
329       BLI_path_rel(path_dst, data->basedir);
330     }
331
332     return true;
333   }
334 }
335
336 void BKE_bpath_missing_files_find(Main *bmain,
337                                   const char *searchpath,
338                                   ReportList *reports,
339                                   const bool find_all)
340 {
341   struct BPathFind_Data data = {NULL};
342   const int flag = BKE_BPATH_TRAVERSE_ABS | BKE_BPATH_TRAVERSE_RELOAD_EDITED;
343
344   data.basedir = BKE_main_blendfile_path(bmain);
345   data.reports = reports;
346   data.searchdir = searchpath;
347   data.find_all = find_all;
348
349   BKE_bpath_traverse_main(bmain, missing_files_find__visit_cb, flag, (void *)&data);
350 }
351
352 /* Run a visitor on a string, replacing the contents of the string as needed. */
353 static bool rewrite_path_fixed(char *path,
354                                BPathVisitor visit_cb,
355                                const char *absbase,
356                                void *userdata)
357 {
358   char path_src_buf[FILE_MAX];
359   const char *path_src;
360   char path_dst[FILE_MAX];
361
362   if (absbase) {
363     BLI_strncpy(path_src_buf, path, sizeof(path_src_buf));
364     BLI_path_abs(path_src_buf, absbase);
365     path_src = path_src_buf;
366   }
367   else {
368     path_src = path;
369   }
370
371   /* so functions can check old value */
372   BLI_strncpy(path_dst, path, FILE_MAX);
373
374   if (visit_cb(userdata, path_dst, path_src)) {
375     BLI_strncpy(path, path_dst, FILE_MAX);
376     return true;
377   }
378   else {
379     return false;
380   }
381 }
382
383 static bool rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR],
384                                        char path_file[FILE_MAXFILE],
385                                        BPathVisitor visit_cb,
386                                        const char *absbase,
387                                        void *userdata)
388 {
389   char path_src[FILE_MAX];
390   char path_dst[FILE_MAX];
391
392   BLI_join_dirfile(path_src, sizeof(path_src), path_dir, path_file);
393
394   /* so functions can check old value */
395   BLI_strncpy(path_dst, path_src, FILE_MAX);
396
397   if (absbase) {
398     BLI_path_abs(path_src, absbase);
399   }
400
401   if (visit_cb(userdata, path_dst, (const char *)path_src)) {
402     BLI_split_dirfile(path_dst, path_dir, path_file, FILE_MAXDIR, FILE_MAXFILE);
403     return true;
404   }
405   else {
406     return false;
407   }
408 }
409
410 static bool rewrite_path_alloc(char **path,
411                                BPathVisitor visit_cb,
412                                const char *absbase,
413                                void *userdata)
414 {
415   char path_src_buf[FILE_MAX];
416   const char *path_src;
417   char path_dst[FILE_MAX];
418
419   if (absbase) {
420     BLI_strncpy(path_src_buf, *path, sizeof(path_src_buf));
421     BLI_path_abs(path_src_buf, absbase);
422     path_src = path_src_buf;
423   }
424   else {
425     path_src = *path;
426   }
427
428   if (visit_cb(userdata, path_dst, path_src)) {
429     MEM_freeN(*path);
430     (*path) = BLI_strdup(path_dst);
431     return true;
432   }
433   else {
434     return false;
435   }
436 }
437
438 /* Run visitor function 'visit' on all paths contained in 'id'. */
439 void BKE_bpath_traverse_id(
440     Main *bmain, ID *id, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
441 {
442   const char *absbase = (flag & BKE_BPATH_TRAVERSE_ABS) ? ID_BLEND_PATH(bmain, id) : NULL;
443
444   if ((flag & BKE_BPATH_TRAVERSE_SKIP_LIBRARY) && ID_IS_LINKED(id)) {
445     return;
446   }
447
448   switch (GS(id->name)) {
449     case ID_IM: {
450       Image *ima;
451       ima = (Image *)id;
452       if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
453         if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) {
454           if (rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data)) {
455             if (flag & BKE_BPATH_TRAVERSE_RELOAD_EDITED) {
456               if (!BKE_image_has_packedfile(ima) &&
457                   /* image may have been painted onto (and not saved, T44543) */
458                   !BKE_image_is_dirty(ima)) {
459                 BKE_image_signal(bmain, ima, NULL, IMA_SIGNAL_RELOAD);
460               }
461             }
462           }
463         }
464       }
465       break;
466     }
467     case ID_BR: {
468       Brush *brush = (Brush *)id;
469       if (brush->icon_filepath[0]) {
470         rewrite_path_fixed(brush->icon_filepath, visit_cb, absbase, bpath_user_data);
471       }
472       break;
473     }
474     case ID_OB: {
475       Object *ob = (Object *)id;
476       ModifierData *md;
477       ParticleSystem *psys;
478
479 #define BPATH_TRAVERSE_POINTCACHE(ptcaches) \
480   { \
481     PointCache *cache; \
482     for (cache = (ptcaches).first; cache; cache = cache->next) { \
483       if (cache->flag & PTCACHE_DISK_CACHE) { \
484         rewrite_path_fixed(cache->path, visit_cb, absbase, bpath_user_data); \
485       } \
486     } \
487   } \
488   (void)0
489
490       for (md = ob->modifiers.first; md; md = md->next) {
491         if (md->type == eModifierType_Fluidsim) {
492           FluidsimModifierData *fluidmd = (FluidsimModifierData *)md;
493           if (fluidmd->fss) {
494             rewrite_path_fixed(fluidmd->fss->surfdataPath, visit_cb, absbase, bpath_user_data);
495           }
496         }
497         else if (md->type == eModifierType_Smoke) {
498           SmokeModifierData *smd = (SmokeModifierData *)md;
499           if (smd->type & MOD_SMOKE_TYPE_DOMAIN) {
500             BPATH_TRAVERSE_POINTCACHE(smd->domain->ptcaches[0]);
501           }
502         }
503         else if (md->type == eModifierType_Cloth) {
504           ClothModifierData *clmd = (ClothModifierData *)md;
505           BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches);
506         }
507         else if (md->type == eModifierType_Ocean) {
508           OceanModifierData *omd = (OceanModifierData *)md;
509           rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data);
510         }
511         else if (md->type == eModifierType_MeshCache) {
512           MeshCacheModifierData *mcmd = (MeshCacheModifierData *)md;
513           rewrite_path_fixed(mcmd->filepath, visit_cb, absbase, bpath_user_data);
514         }
515       }
516
517       if (ob->soft) {
518         BPATH_TRAVERSE_POINTCACHE(ob->soft->shared->ptcaches);
519       }
520
521       for (psys = ob->particlesystem.first; psys; psys = psys->next) {
522         BPATH_TRAVERSE_POINTCACHE(psys->ptcaches);
523       }
524
525 #undef BPATH_TRAVERSE_POINTCACHE
526
527       break;
528     }
529     case ID_SO: {
530       bSound *sound = (bSound *)id;
531       if (sound->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
532         rewrite_path_fixed(sound->name, visit_cb, absbase, bpath_user_data);
533       }
534       break;
535     }
536     case ID_TXT:
537       if (((Text *)id)->name) {
538         rewrite_path_alloc(&((Text *)id)->name, visit_cb, absbase, bpath_user_data);
539       }
540       break;
541     case ID_VF: {
542       VFont *vfont = (VFont *)id;
543       if (vfont->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
544         if (BKE_vfont_is_builtin(vfont) == false) {
545           rewrite_path_fixed(((VFont *)id)->name, visit_cb, absbase, bpath_user_data);
546         }
547       }
548       break;
549     }
550     case ID_MA: {
551       Material *ma = (Material *)id;
552       bNodeTree *ntree = ma->nodetree;
553
554       if (ntree) {
555         bNode *node;
556
557         for (node = ntree->nodes.first; node; node = node->next) {
558           if (node->type == SH_NODE_SCRIPT) {
559             NodeShaderScript *nss = (NodeShaderScript *)node->storage;
560             rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
561           }
562           else if (node->type == SH_NODE_TEX_IES) {
563             NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage;
564             rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data);
565           }
566         }
567       }
568       break;
569     }
570     case ID_NT: {
571       bNodeTree *ntree = (bNodeTree *)id;
572       bNode *node;
573
574       if (ntree->type == NTREE_SHADER) {
575         /* same as lines above */
576         for (node = ntree->nodes.first; node; node = node->next) {
577           if (node->type == SH_NODE_SCRIPT) {
578             NodeShaderScript *nss = (NodeShaderScript *)node->storage;
579             rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
580           }
581           else if (node->type == SH_NODE_TEX_IES) {
582             NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage;
583             rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data);
584           }
585         }
586       }
587       break;
588     }
589     case ID_SCE: {
590       Scene *scene = (Scene *)id;
591       if (scene->ed) {
592         Sequence *seq;
593
594         SEQ_BEGIN (scene->ed, seq) {
595           if (SEQ_HAS_PATH(seq)) {
596             StripElem *se = seq->strip->stripdata;
597
598             if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM) && se) {
599               rewrite_path_fixed_dirfile(
600                   seq->strip->dir, se->name, visit_cb, absbase, bpath_user_data);
601             }
602             else if ((seq->type == SEQ_TYPE_IMAGE) && se) {
603               /* might want an option not to loop over all strips */
604               unsigned int len = (unsigned int)MEM_allocN_len(se) / (unsigned int)sizeof(*se);
605               unsigned int i;
606
607               if (flag & BKE_BPATH_TRAVERSE_SKIP_MULTIFILE) {
608                 /* only operate on one path */
609                 len = MIN2(1u, len);
610               }
611
612               for (i = 0; i < len; i++, se++) {
613                 rewrite_path_fixed_dirfile(
614                     seq->strip->dir, se->name, visit_cb, absbase, bpath_user_data);
615               }
616             }
617             else {
618               /* simple case */
619               rewrite_path_fixed(seq->strip->dir, visit_cb, absbase, bpath_user_data);
620             }
621           }
622         }
623         SEQ_END;
624       }
625       break;
626     }
627     case ID_ME: {
628       Mesh *me = (Mesh *)id;
629       if (me->ldata.external) {
630         rewrite_path_fixed(me->ldata.external->filename, visit_cb, absbase, bpath_user_data);
631       }
632       break;
633     }
634     case ID_LI: {
635       Library *lib = (Library *)id;
636       /* keep packedfile paths always relative to the blend */
637       if (lib->packedfile == NULL) {
638         if (rewrite_path_fixed(lib->name, visit_cb, absbase, bpath_user_data)) {
639           BKE_library_filepath_set(bmain, lib, lib->name);
640         }
641       }
642       break;
643     }
644     case ID_MC: {
645       MovieClip *clip = (MovieClip *)id;
646       rewrite_path_fixed(clip->name, visit_cb, absbase, bpath_user_data);
647       break;
648     }
649     case ID_CF: {
650       CacheFile *cache_file = (CacheFile *)id;
651       rewrite_path_fixed(cache_file->filepath, visit_cb, absbase, bpath_user_data);
652       break;
653     }
654     default:
655       /* Nothing to do for other IDs that don't contain file paths. */
656       break;
657   }
658 }
659
660 void BKE_bpath_traverse_id_list(
661     Main *bmain, ListBase *lb, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
662 {
663   ID *id;
664   for (id = lb->first; id; id = id->next) {
665     BKE_bpath_traverse_id(bmain, id, visit_cb, flag, bpath_user_data);
666   }
667 }
668
669 void BKE_bpath_traverse_main(Main *bmain,
670                              BPathVisitor visit_cb,
671                              const int flag,
672                              void *bpath_user_data)
673 {
674   ListBase *lbarray[MAX_LIBARRAY];
675   int a = set_listbasepointers(bmain, lbarray);
676   while (a--) {
677     BKE_bpath_traverse_id_list(bmain, lbarray[a], visit_cb, flag, bpath_user_data);
678   }
679 }
680
681 /* Rewrites a relative path to be relative to the main file - unless the path is
682  * absolute, in which case it is not altered. */
683 bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *path_src)
684 {
685   /* be sure there is low chance of the path being too short */
686   char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
687   const char *base_new = ((char **)pathbase_v)[0];
688   const char *base_old = ((char **)pathbase_v)[1];
689
690   if (BLI_path_is_rel(base_old)) {
691     CLOG_ERROR(&LOG, "old base path '%s' is not absolute.", base_old);
692     return false;
693   }
694
695   /* Make referenced file absolute. This would be a side-effect of
696    * BLI_cleanup_file, but we do it explicitly so we know if it changed. */
697   BLI_strncpy(filepath, path_src, FILE_MAX);
698   if (BLI_path_abs(filepath, base_old)) {
699     /* Path was relative and is now absolute. Remap.
700      * Important BLI_cleanup_dir runs before the path is made relative
701      * because it wont work for paths that start with "//../" */
702     BLI_cleanup_file(base_new, filepath);
703     BLI_path_rel(filepath, base_new);
704     BLI_strncpy(path_dst, filepath, FILE_MAX);
705     return true;
706   }
707   else {
708     /* Path was not relative to begin with. */
709     return false;
710   }
711 }
712
713 /* -------------------------------------------------------------------- */
714 /**
715  * Backup/Restore/Free functions,
716  * \note These functions assume the data won't change order.
717  */
718
719 struct PathStore {
720   struct PathStore *next, *prev;
721 };
722
723 static bool bpath_list_append(void *userdata, char *UNUSED(path_dst), const char *path_src)
724 {
725   /* store the path and string in a single alloc */
726   ListBase *ls = userdata;
727   size_t path_size = strlen(path_src) + 1;
728   struct PathStore *path_store = MEM_mallocN(sizeof(struct PathStore) + path_size, __func__);
729   char *filepath = (char *)(path_store + 1);
730
731   memcpy(filepath, path_src, path_size);
732   BLI_addtail(ls, path_store);
733   return false;
734 }
735
736 static bool bpath_list_restore(void *userdata, char *path_dst, const char *path_src)
737 {
738   /* assume ls->first wont be NULL because the number of paths can't change!
739    * (if they do caller is wrong) */
740   ListBase *ls = userdata;
741   struct PathStore *path_store = ls->first;
742   const char *filepath = (char *)(path_store + 1);
743   bool ret;
744
745   if (STREQ(path_src, filepath)) {
746     ret = false;
747   }
748   else {
749     BLI_strncpy(path_dst, filepath, FILE_MAX);
750     ret = true;
751   }
752
753   BLI_freelinkN(ls, path_store);
754   return ret;
755 }
756
757 /* return ls_handle */
758 void *BKE_bpath_list_backup(Main *bmain, const int flag)
759 {
760   ListBase *ls = MEM_callocN(sizeof(ListBase), __func__);
761
762   BKE_bpath_traverse_main(bmain, bpath_list_append, flag, ls);
763
764   return ls;
765 }
766
767 void BKE_bpath_list_restore(Main *bmain, const int flag, void *ls_handle)
768 {
769   ListBase *ls = ls_handle;
770
771   BKE_bpath_traverse_main(bmain, bpath_list_restore, flag, ls);
772 }
773
774 void BKE_bpath_list_free(void *ls_handle)
775 {
776   ListBase *ls = ls_handle;
777   BLI_assert(BLI_listbase_is_empty(ls)); /* assumes we were used */
778   BLI_freelistN(ls);
779   MEM_freeN(ls);
780 }