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