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