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                         for (md = ob->modifiers.first; md; md = md->next) {
482                                 if (md->type == eModifierType_Fluidsim) {
483                                         FluidsimModifierData *fluidmd = (FluidsimModifierData *)md;
484                                         if (fluidmd->fss) {
485                                                 rewrite_path_fixed(fluidmd->fss->surfdataPath, visit_cb, absbase, bpath_user_data);
486                                         }
487                                 }
488                                 else if (md->type == eModifierType_Smoke) {
489                                         SmokeModifierData *smd = (SmokeModifierData *)md;
490                                         if (smd->type & MOD_SMOKE_TYPE_DOMAIN) {
491                                                 BPATH_TRAVERSE_POINTCACHE(smd->domain->ptcaches[0]);
492                                         }
493                                 }
494                                 else if (md->type == eModifierType_Cloth) {
495                                         ClothModifierData *clmd = (ClothModifierData *) md;
496                                         BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches);
497                                 }
498                                 else if (md->type == eModifierType_Ocean) {
499                                         OceanModifierData *omd = (OceanModifierData *) md;
500                                         rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data);
501                                 }
502                                 else if (md->type == eModifierType_MeshCache) {
503                                         MeshCacheModifierData *mcmd = (MeshCacheModifierData *) md;
504                                         rewrite_path_fixed(mcmd->filepath, visit_cb, absbase, bpath_user_data);
505                                 }
506                         }
507
508                         if (ob->soft) {
509                                 BPATH_TRAVERSE_POINTCACHE(ob->soft->shared->ptcaches);
510                         }
511
512                         for (psys = ob->particlesystem.first; psys; psys = psys->next) {
513                                 BPATH_TRAVERSE_POINTCACHE(psys->ptcaches);
514                         }
515
516 #undef BPATH_TRAVERSE_POINTCACHE
517
518                         break;
519                 }
520                 case ID_SO:
521                 {
522                         bSound *sound = (bSound *)id;
523                         if (sound->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
524                                 rewrite_path_fixed(sound->name, visit_cb, absbase, bpath_user_data);
525                         }
526                         break;
527                 }
528                 case ID_TXT:
529                         if (((Text *)id)->name) {
530                                 rewrite_path_alloc(&((Text *)id)->name, visit_cb, absbase, bpath_user_data);
531                         }
532                         break;
533                 case ID_VF:
534                 {
535                         VFont *vfont = (VFont *)id;
536                         if (vfont->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
537                                 if (BKE_vfont_is_builtin(vfont) == false) {
538                                         rewrite_path_fixed(((VFont *)id)->name, visit_cb, absbase, bpath_user_data);
539                                 }
540                         }
541                         break;
542                 }
543                 case ID_MA:
544                 {
545                         Material *ma = (Material *)id;
546                         bNodeTree *ntree = ma->nodetree;
547
548                         if (ntree) {
549                                 bNode *node;
550
551                                 for (node = ntree->nodes.first; node; node = node->next) {
552                                         if (node->type == SH_NODE_SCRIPT) {
553                                                 NodeShaderScript *nss = (NodeShaderScript *)node->storage;
554                                                 rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
555                                         }
556                                         else if (node->type == SH_NODE_TEX_IES) {
557                                                 NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage;
558                                                 rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data);
559                                         }
560                                 }
561                         }
562                         break;
563                 }
564                 case ID_NT:
565                 {
566                         bNodeTree *ntree = (bNodeTree *)id;
567                         bNode *node;
568
569                         if (ntree->type == NTREE_SHADER) {
570                                 /* same as lines above */
571                                 for (node = ntree->nodes.first; node; node = node->next) {
572                                         if (node->type == SH_NODE_SCRIPT) {
573                                                 NodeShaderScript *nss = (NodeShaderScript *)node->storage;
574                                                 rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
575                                         }
576                                         else if (node->type == SH_NODE_TEX_IES) {
577                                                 NodeShaderTexIES *ies = (NodeShaderTexIES *)node->storage;
578                                                 rewrite_path_fixed(ies->filepath, visit_cb, absbase, bpath_user_data);
579                                         }
580                                 }
581                         }
582                         break;
583                 }
584                 case ID_SCE:
585                 {
586                         Scene *scene = (Scene *)id;
587                         if (scene->ed) {
588                                 Sequence *seq;
589
590                                 SEQ_BEGIN(scene->ed, seq)
591                                 {
592                                         if (SEQ_HAS_PATH(seq)) {
593                                                 StripElem *se = seq->strip->stripdata;
594
595                                                 if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_SOUND_RAM) && se) {
596                                                         rewrite_path_fixed_dirfile(seq->strip->dir, se->name,
597                                                                                    visit_cb, absbase, bpath_user_data);
598                                                 }
599                                                 else if ((seq->type == SEQ_TYPE_IMAGE) && se) {
600                                                         /* might want an option not to loop over all strips */
601                                                         unsigned int len = (unsigned int)MEM_allocN_len(se) / (unsigned int)sizeof(*se);
602                                                         unsigned int i;
603
604                                                         if (flag & BKE_BPATH_TRAVERSE_SKIP_MULTIFILE) {
605                                                                 /* only operate on one path */
606                                                                 len = MIN2(1u, len);
607                                                         }
608
609                                                         for (i = 0; i < len; i++, se++) {
610                                                                 rewrite_path_fixed_dirfile(seq->strip->dir, se->name,
611                                                                                            visit_cb, absbase, bpath_user_data);
612                                                         }
613                                                 }
614                                                 else {
615                                                         /* simple case */
616                                                         rewrite_path_fixed(seq->strip->dir, visit_cb, absbase, bpath_user_data);
617                                                 }
618                                         }
619
620                                 }
621                                 SEQ_END
622                         }
623                         break;
624                 }
625                 case ID_ME:
626                 {
627                         Mesh *me = (Mesh *)id;
628                         if (me->ldata.external) {
629                                 rewrite_path_fixed(me->ldata.external->filename, visit_cb, absbase, bpath_user_data);
630                         }
631                         break;
632                 }
633                 case ID_LI:
634                 {
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                 {
646                         MovieClip *clip = (MovieClip *)id;
647                         rewrite_path_fixed(clip->name, visit_cb, absbase, bpath_user_data);
648                         break;
649                 }
650                 case ID_CF:
651                 {
652                         CacheFile *cache_file = (CacheFile *)id;
653                         rewrite_path_fixed(cache_file->filepath, visit_cb, absbase, bpath_user_data);
654                         break;
655                 }
656                 default:
657                         /* Nothing to do for other IDs that don't contain file paths. */
658                         break;
659         }
660 }
661
662 void BKE_bpath_traverse_id_list(Main *bmain, ListBase *lb, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
663 {
664         ID *id;
665         for (id = lb->first; id; id = id->next) {
666                 BKE_bpath_traverse_id(bmain, id, visit_cb, flag, bpath_user_data);
667         }
668 }
669
670 void BKE_bpath_traverse_main(Main *bmain, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
671 {
672         ListBase *lbarray[MAX_LIBARRAY];
673         int a = set_listbasepointers(bmain, lbarray);
674         while (a--) {
675                 BKE_bpath_traverse_id_list(bmain, lbarray[a], visit_cb, flag, bpath_user_data);
676         }
677 }
678
679 /* Rewrites a relative path to be relative to the main file - unless the path is
680  * absolute, in which case it is not altered. */
681 bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *path_src)
682 {
683         /* be sure there is low chance of the path being too short */
684         char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
685         const char *base_new = ((char **)pathbase_v)[0];
686         const char *base_old = ((char **)pathbase_v)[1];
687
688         if (BLI_path_is_rel(base_old)) {
689                 printf("%s: error, old base path '%s' is not absolute.\n",
690                        __func__, base_old);
691                 return false;
692         }
693
694         /* Make referenced file absolute. This would be a side-effect of
695          * BLI_cleanup_file, but we do it explicitly so we know if it changed. */
696         BLI_strncpy(filepath, path_src, FILE_MAX);
697         if (BLI_path_abs(filepath, base_old)) {
698                 /* Path was relative and is now absolute. Remap.
699                  * Important BLI_cleanup_dir runs before the path is made relative
700                  * because it wont work for paths that start with "//../" */
701                 BLI_cleanup_file(base_new, filepath);
702                 BLI_path_rel(filepath, base_new);
703                 BLI_strncpy(path_dst, filepath, FILE_MAX);
704                 return true;
705         }
706         else {
707                 /* Path was not relative to begin with. */
708                 return false;
709         }
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 }