Merge branch 'temp_remove_particles' 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->lib) {
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 #define BPATH_TRAVERSE_POINTCACHE(ptcaches)                                    \
466         {                                                                          \
467                 PointCache *cache;                                                     \
468                 for (cache = (ptcaches).first; cache; cache = cache->next) {           \
469                         if (cache->flag & PTCACHE_DISK_CACHE) {                            \
470                                 rewrite_path_fixed(cache->path,                                \
471                                                    visit_cb,                                   \
472                                                    absbase,                                    \
473                                                    bpath_user_data);                           \
474                         }                                                                  \
475                 }                                                                      \
476         } (void)0
477
478                         /* do via modifiers instead */
479 #if 0
480                         if (ob->fluidsimSettings) {
481                                 rewrite_path_fixed(ob->fluidsimSettings->surfdataPath, visit_cb, absbase, bpath_user_data);
482                         }
483 #endif
484
485                         for (md = ob->modifiers.first; md; md = md->next) {
486                                 if (md->type == eModifierType_Fluidsim) {
487                                         FluidsimModifierData *fluidmd = (FluidsimModifierData *)md;
488                                         if (fluidmd->fss) {
489                                                 rewrite_path_fixed(fluidmd->fss->surfdataPath, visit_cb, absbase, bpath_user_data);
490                                         }
491                                 }
492                                 else if (md->type == eModifierType_Smoke) {
493                                         SmokeModifierData *smd = (SmokeModifierData *)md;
494                                         if (smd->type & MOD_SMOKE_TYPE_DOMAIN) {
495                                                 BPATH_TRAVERSE_POINTCACHE(smd->domain->ptcaches[0]);
496                                         }
497                                 }
498                                 else if (md->type == eModifierType_Cloth) {
499                                         ClothModifierData *clmd = (ClothModifierData *) md;
500                                         BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches);
501                                 }
502                                 else if (md->type == eModifierType_Ocean) {
503                                         OceanModifierData *omd = (OceanModifierData *) md;
504                                         rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data);
505                                 }
506                                 else if (md->type == eModifierType_MeshCache) {
507                                         MeshCacheModifierData *mcmd = (MeshCacheModifierData *) md;
508                                         rewrite_path_fixed(mcmd->filepath, visit_cb, absbase, bpath_user_data);
509                                 }
510                         }
511
512                         if (ob->soft) {
513                                 BPATH_TRAVERSE_POINTCACHE(ob->soft->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                                 }
557                         }
558                         break;
559                 }
560                 case ID_NT:
561                 {
562                         bNodeTree *ntree = (bNodeTree *)id;
563                         bNode *node;
564
565                         if (ntree->type == NTREE_SHADER) {
566                                 /* same as lines above */
567                                 for (node = ntree->nodes.first; node; node = node->next) {
568                                         if (node->type == SH_NODE_SCRIPT) {
569                                                 NodeShaderScript *nss = (NodeShaderScript *)node->storage;
570                                                 rewrite_path_fixed(nss->filepath, visit_cb, absbase, bpath_user_data);
571                                         }
572                                 }
573                         }
574                         break;
575                 }
576                 case ID_TE:
577                 {
578                         Tex *tex = (Tex *)id;
579                         if (tex->type == TEX_VOXELDATA && TEX_VD_IS_SOURCE_PATH(tex->vd->file_format)) {
580                                 rewrite_path_fixed(tex->vd->source_path, visit_cb, absbase, bpath_user_data);
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(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                 default:
651                         /* Nothing to do for other IDs that don't contain file paths. */
652                         break;
653         }
654 }
655
656 void BKE_bpath_traverse_id_list(Main *bmain, ListBase *lb, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
657 {
658         ID *id;
659         for (id = lb->first; id; id = id->next) {
660                 BKE_bpath_traverse_id(bmain, id, visit_cb, flag, bpath_user_data);
661         }
662 }
663
664 void BKE_bpath_traverse_main(Main *bmain, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
665 {
666         ListBase *lbarray[MAX_LIBARRAY];
667         int a = set_listbasepointers(bmain, lbarray);
668         while (a--) {
669                 BKE_bpath_traverse_id_list(bmain, lbarray[a], visit_cb, flag, bpath_user_data);
670         }
671 }
672
673 /* Rewrites a relative path to be relative to the main file - unless the path is
674  * absolute, in which case it is not altered. */
675 bool BKE_bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *path_src)
676 {
677         /* be sure there is low chance of the path being too short */
678         char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
679         const char *base_new = ((char **)pathbase_v)[0];
680         const char *base_old = ((char **)pathbase_v)[1];
681
682         if (BLI_path_is_rel(base_old)) {
683                 printf("%s: error, old base path '%s' is not absolute.\n",
684                        __func__, base_old);
685                 return false;
686         }
687
688         /* Make referenced file absolute. This would be a side-effect of
689          * BLI_cleanup_file, but we do it explicitly so we know if it changed. */
690         BLI_strncpy(filepath, path_src, FILE_MAX);
691         if (BLI_path_abs(filepath, base_old)) {
692                 /* Path was relative and is now absolute. Remap.
693                  * Important BLI_cleanup_dir runs before the path is made relative
694                  * because it wont work for paths that start with "//../" */
695                 BLI_cleanup_file(base_new, filepath);
696                 BLI_path_rel(filepath, base_new);
697                 BLI_strncpy(path_dst, filepath, FILE_MAX);
698                 return true;
699         }
700         else {
701                 /* Path was not relative to begin with. */
702                 return false;
703         }
704 }
705
706
707 /* -------------------------------------------------------------------- */
708 /**
709  * Backup/Restore/Free functions,
710  * \note These functions assume the data won't change order.
711  */
712
713 struct PathStore {
714         struct PathStore *next, *prev;
715 };
716
717 static bool bpath_list_append(void *userdata, char *UNUSED(path_dst), const char *path_src)
718 {
719         /* store the path and string in a single alloc */
720         ListBase *ls = userdata;
721         size_t path_size = strlen(path_src) + 1;
722         struct PathStore *path_store = MEM_mallocN(sizeof(struct PathStore) + path_size, __func__);
723         char *filepath = (char *)(path_store + 1);
724
725         memcpy(filepath, path_src, path_size);
726         BLI_addtail(ls, path_store);
727         return false;
728 }
729
730 static bool bpath_list_restore(void *userdata, char *path_dst, const char *path_src)
731 {
732         /* assume ls->first wont be NULL because the number of paths can't change!
733          * (if they do caller is wrong) */
734         ListBase *ls = userdata;
735         struct PathStore *path_store = ls->first;
736         const char *filepath = (char *)(path_store + 1);
737         bool ret;
738
739         if (STREQ(path_src, filepath)) {
740                 ret = false;
741         }
742         else {
743                 BLI_strncpy(path_dst, filepath, FILE_MAX);
744                 ret = true;
745         }
746
747         BLI_freelinkN(ls, path_store);
748         return ret;
749 }
750
751 /* return ls_handle */
752 void *BKE_bpath_list_backup(Main *bmain, const int flag)
753 {
754         ListBase *ls = MEM_callocN(sizeof(ListBase), __func__);
755
756         BKE_bpath_traverse_main(bmain, bpath_list_append, flag, ls);
757
758         return ls;
759 }
760
761 void BKE_bpath_list_restore(Main *bmain, const int flag, void *ls_handle)
762 {
763         ListBase *ls = ls_handle;
764
765         BKE_bpath_traverse_main(bmain, bpath_list_restore, flag, ls);
766 }
767
768 void BKE_bpath_list_free(void *ls_handle)
769 {
770         ListBase *ls = ls_handle;
771         BLI_assert(BLI_listbase_is_empty(ls));  /* assumes we were used */
772         BLI_freelistN(ls);
773         MEM_freeN(ls);
774 }