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