- remove bpath iterator and replace all uses with visitor.
[blender.git] / source / blender / blenlib / 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
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/blenlib/intern/bpath.c
29  *  \ingroup bli
30  */
31
32
33 #include <sys/stat.h>
34
35 #include <string.h>
36 #include <assert.h>
37
38 /* path/file handeling stuff */
39 #ifndef WIN32
40   #include <dirent.h>
41   #include <unistd.h>
42 #else
43   #include <io.h>
44   #include "BLI_winstuff.h"
45 #endif
46
47 #include "MEM_guardedalloc.h"
48
49 #include "DNA_mesh_types.h"
50 #include "DNA_scene_types.h" /* to get the current frame */
51 #include "DNA_image_types.h"
52 #include "DNA_texture_types.h"
53 #include "DNA_text_types.h"
54 #include "DNA_sound_types.h"
55 #include "DNA_sequence_types.h"
56 #include "DNA_vfont_types.h"
57 #include "DNA_object_types.h"
58 #include "DNA_object_fluidsim.h"
59
60 #include "BLI_blenlib.h"
61 #include "BLI_bpath.h"
62 #include "BLI_utildefines.h"
63
64 #include "BKE_image.h" /* so we can check the image's type */
65 #include "BKE_sequencer.h"
66 #include "BKE_main.h"
67 #include "BKE_utildefines.h"
68 #include "BKE_report.h"
69 #include "BKE_library.h"
70
71 static int checkMissingFiles_visit_cb(void *userdata, char *UNUSED(path_dst), const char *path_src)
72 {
73         ReportList *reports= (ReportList *)userdata;
74
75         if (!BLI_exists(path_src)) {
76                 BKE_reportf(reports, RPT_WARNING, "Path Not Found \"%s\"", path_src);
77         }
78
79         return FALSE;
80 }
81
82 /* high level function */
83 void checkMissingFiles(Main *bmain, ReportList *reports)
84 {
85         bpath_traverse_main(bmain, checkMissingFiles_visit_cb, BPATH_TRAVERSE_ABS, reports);
86 }
87
88 typedef struct BPathRemap_Data
89 {
90         const char *basedir;
91         ReportList *reports;
92
93         int count_tot;
94         int count_changed;
95         int count_failed;
96 } BPathRemap_Data;
97
98 static int makeFilesRelative_visit_cb(void *userdata, char *path_dst, const char *path_src)
99 {
100         BPathRemap_Data *data= (BPathRemap_Data *)userdata;
101
102         data->count_tot++;
103
104         if(strncmp(path_src, "//", 2)==0) {
105                 return FALSE; /* already relative */
106         }
107         else {
108                 strcpy(path_dst, path_src);
109                 BLI_path_rel(path_dst, data->basedir);
110                 if (strncmp(path_dst, "//", 2)==0) {
111                         data->count_changed++;
112                 }
113                 else {
114                         BKE_reportf(data->reports, RPT_WARNING, "Path cant be made relative \"%s\"", path_src);
115                         data->count_failed++;
116                 }
117                 return TRUE;
118         }
119 }
120
121 void makeFilesRelative(Main *bmain, const char *basedir, ReportList *reports)
122 {
123         BPathRemap_Data data= {0};
124
125         if(basedir[0] == '\0') {
126                 printf("%s: basedir='', this is a bug\n", __func__);
127                 return;
128         }
129
130         data.basedir= basedir;
131         data.reports= reports;
132
133         bpath_traverse_main(bmain, makeFilesRelative_visit_cb, 0, (void *)&data);
134
135         BKE_reportf(reports, data.count_failed ? RPT_WARNING : RPT_INFO,
136                     "Total files %d|Changed %d|Failed %d",
137                     data.count_tot, data.count_changed, data.count_failed);
138 }
139
140 static int makeFilesAbsolute_visit_cb(void *userdata, char *path_dst, const char *path_src)
141 {
142         BPathRemap_Data *data= (BPathRemap_Data *)userdata;
143
144         data->count_tot++;
145
146         if(strncmp(path_src, "//", 2)!=0) {
147                 return FALSE; /* already absolute */
148         }
149         else {
150                 strcpy(path_dst, path_src);
151                 BLI_path_abs(path_dst, data->basedir);
152                 if (strncmp(path_dst, "//", 2)!=0) {
153                         data->count_changed++;
154                 }
155                 else {
156                         BKE_reportf(data->reports, RPT_WARNING, "Path cant be made absolute \"%s\"", path_src);
157                         data->count_failed++;
158                 }
159                 return TRUE;
160         }
161 }
162
163 /* similar to makeFilesRelative - keep in sync! */
164 void makeFilesAbsolute(Main *bmain, const char *basedir, ReportList *reports)
165 {
166         BPathRemap_Data data= {0};
167
168         if(basedir[0] == '\0') {
169                 printf("%s: basedir='', this is a bug\n", __func__);
170                 return;
171         }
172
173         data.basedir= basedir;
174         data.reports= reports;
175
176         bpath_traverse_main(bmain, makeFilesAbsolute_visit_cb, 0, (void *)&data);
177
178         BKE_reportf(reports, data.count_failed ? RPT_WARNING : RPT_INFO,
179                     "Total files %d|Changed %d|Failed %d",
180                     data.count_tot, data.count_changed, data.count_failed);
181 }
182
183
184 /* find this file recursively, use the biggest file so thumbnails dont get used by mistake
185  - dir: subdir to search
186  - filename: set this filename
187  - filesize: filesize for the file
188 */
189 #define MAX_RECUR 16
190 static int findFileRecursive(char *filename_new, const char *dirname, const char *filename, int *filesize, int *recur_depth)
191 {
192         /* file searching stuff */
193         DIR *dir;
194         struct dirent *de;
195         struct stat status;
196         char path[FILE_MAX];
197         int size;
198
199         dir= opendir(dirname);
200
201         if (dir==NULL)
202                 return 0;
203
204         if (*filesize == -1)
205                 *filesize= 0; /* dir opened fine */
206
207         while ((de= readdir(dir)) != NULL) {
208
209                 if (strcmp(".", de->d_name)==0 || strcmp("..", de->d_name)==0)
210                         continue;
211
212                 BLI_join_dirfile(path, sizeof(path), dirname, de->d_name);
213
214                 if (stat(path, &status) != 0)
215                         continue; /* cant stat, dont bother with this file, could print debug info here */
216
217                 if (S_ISREG(status.st_mode)) { /* is file */
218                         if (strncmp(filename, de->d_name, FILE_MAX)==0) { /* name matches */
219                                 /* open the file to read its size */
220                                 size= status.st_size;
221                                 if ((size > 0) && (size > *filesize)) { /* find the biggest file */
222                                         *filesize= size;
223                                         BLI_strncpy(filename_new, path, FILE_MAX);
224                                 }
225                         }
226                 }
227                 else if (S_ISDIR(status.st_mode)) { /* is subdir */
228                         if (*recur_depth <= MAX_RECUR) {
229                                 (*recur_depth)++;
230                                 findFileRecursive(filename_new, path, filename, filesize, recur_depth);
231                                 (*recur_depth)--;
232                         }
233                 }
234         }
235         closedir(dir);
236         return 1;
237 }
238
239 typedef struct BPathFind_Data
240 {
241         const char *basedir;
242         char searchdir[FILE_MAX];
243         ReportList *reports;
244 } BPathFind_Data;
245
246 static int findMissingFiles_visit_cb(void *userdata, char *path_dst, const char *path_src)
247 {
248         BPathFind_Data *data= (BPathFind_Data *)userdata;
249         char filename_new[FILE_MAX];
250
251         int filesize= -1;
252         int recur_depth= 0;
253
254         findFileRecursive(filename_new,
255                           data->searchdir, BLI_path_basename((char *)path_src),
256                           &filesize, &recur_depth);
257
258         if (filesize == -1) { /* could not open dir */
259                 BKE_reportf(data->reports, RPT_WARNING,
260                             "Could not find \"%s\" in \"%s\"",
261                             BLI_path_basename((char *)path_src), data->searchdir);
262                 return FALSE;
263         }
264         else {
265                 strcpy(path_dst, filename_new);
266                 return TRUE;
267         }
268 }
269
270 void findMissingFiles(Main *bmain, const char *searchpath, ReportList *reports)
271 {
272         struct BPathFind_Data data= {0};
273
274         data.reports= reports;
275         BLI_split_dir_part(searchpath, data.searchdir, sizeof(data.searchdir));
276
277         bpath_traverse_main(bmain, findMissingFiles_visit_cb, 0, (void *)&data);
278 }
279
280 /* Run a visitor on a string, replacing the contents of the string as needed. */
281 static int rewrite_path_fixed(char *path, BPathVisitor visit_cb, const char *absbase, void *userdata)
282 {
283         char path_src_buf[FILE_MAX];
284         const char *path_src;
285         char path_dst[FILE_MAX];
286
287         if (absbase) {
288                 BLI_strncpy(path_src_buf, path, sizeof(path_src_buf));
289                 BLI_path_abs(path_src_buf, absbase);
290                 path_src= path_src_buf;
291         }
292         else {
293                 path_src= path;
294         }
295
296         if (visit_cb(userdata, path_dst, path_src)) {
297                 BLI_strncpy(path, path_dst, FILE_MAX);
298                 return TRUE;
299         }
300         else {
301                 return FALSE;
302         }
303 }
304
305 static int rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR], char path_file[FILE_MAXFILE], BPathVisitor visit_cb, const char *absbase, void *userdata)
306 {
307         char path_src[FILE_MAX];
308         char path_dst[FILE_MAX];
309
310         BLI_join_dirfile(path_src, sizeof(path_src), path_dir, path_file);
311
312         if (absbase) {
313                 BLI_path_abs(path_src, absbase);
314         }
315
316         if (visit_cb(userdata, path_dst, (const char *)path_src)) {
317                 BLI_split_dirfile(path_dst, path_dir, path_file,
318                                   sizeof(path_dir), sizeof(path_file));
319                 return TRUE;
320         }
321         else {
322                 return FALSE;
323         }
324 }
325
326 static int rewrite_path_alloc(char **path, BPathVisitor visit_cb, const char *absbase, void *userdata)
327 {
328         char path_src_buf[FILE_MAX];
329         const char *path_src;
330         char path_dst[FILE_MAX];
331
332         if (absbase) {
333                 BLI_strncpy(path_src_buf, *path, sizeof(path_src_buf));
334                 BLI_path_abs(path_src_buf, absbase);
335                 path_src= path_src_buf;
336         }
337         else {
338                 path_src= *path;
339         }
340
341         if (visit_cb(userdata, path_dst, path_src)) {
342                 MEM_freeN((*path));
343                 (*path)= BLI_strdup(path_dst);
344                 return TRUE;
345         }
346         else {
347                 return FALSE;
348         }
349 }
350
351 /* Run visitor function 'visit' on all paths contained in 'id'. */
352 void bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, int flag, void *userdata)
353 {
354         Image *ima;
355         const char *absbase= (flag & BPATH_TRAVERSE_ABS) ? (id->lib ? id->lib->filepath : bmain->name) : NULL;
356
357
358         switch(GS(id->name)) {
359         case ID_IM:
360                 ima = (Image *)id;
361                 if (ELEM3(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE))
362                         rewrite_path_fixed(ima->name, visit_cb, absbase, userdata);
363                 break;
364         case ID_OB:
365                 {
366                         Object *ob= (Object *)id;
367                         if (ob->fluidsimSettings) {
368                                 rewrite_path_fixed(ob->fluidsimSettings->surfdataPath, visit_cb, absbase, userdata);
369                         }
370                         /* TODO: add modifiers, e.g. point cache for particles. */
371                 }
372                 break;
373         case ID_SO:
374                 rewrite_path_fixed(((bSound *)id)->name, visit_cb, absbase, userdata);
375                 break;
376         case ID_TXT:
377                 if (((Text*)id)->name) {
378                         rewrite_path_alloc(&((Text *)id)->name, visit_cb, absbase, userdata);
379                 }
380                 break;
381         case ID_VF:
382                 if (strcmp(((VFont*)id)->name, FO_BUILTIN_NAME) != 0) {
383                         rewrite_path_fixed(((VFont *)id)->name, visit_cb, absbase, userdata);
384                 }
385                 break;
386         case ID_TE:
387                 {
388                         Tex *tex = (Tex *)id;
389                         if (tex->plugin) {
390                                 /* FIXME: rewrite_path assumes path length of FILE_MAX, but
391                                            tex->plugin->name is 160. ... is this field even a path? */
392                                 //rewrite_path(tex->plugin->name, visit_cb, userdata);
393                         }
394                         if (tex->type == TEX_VOXELDATA && TEX_VD_IS_SOURCE_PATH(tex->vd->file_format)) {
395                                 rewrite_path_fixed(tex->vd->source_path, visit_cb, absbase, userdata);
396                         }
397                 }
398                 break;
399
400         case ID_SCE:
401                 {
402                         Scene *scene= (Scene *)id;
403                         if (scene->ed) {
404                                 Sequence *seq;
405
406                                 SEQ_BEGIN(scene->ed, seq) {
407                                         if (SEQ_HAS_PATH(seq)) {
408                                                 if (ELEM3(seq->type, SEQ_IMAGE, SEQ_MOVIE, SEQ_SOUND)) {
409                                                         rewrite_path_fixed_dirfile(seq->strip->dir, seq->strip->stripdata->name, visit_cb, absbase, userdata);
410                                                 }
411                                                 else {
412                                                         /* simple case */
413                                                         rewrite_path_fixed(seq->strip->dir, visit_cb, absbase, userdata);
414                                                 }
415                                         }
416                                         else if (seq->plugin) {
417                                                 rewrite_path_fixed(seq->plugin->name, visit_cb, absbase, userdata);
418                                         }
419
420                                 }
421                                 SEQ_END
422                         }
423                 }
424                 break;
425         case ID_ME:
426                 {
427                         Mesh *me= (Mesh *)id;
428                         if (me->fdata.external) {
429                                 rewrite_path_fixed(me->fdata.external->filename, visit_cb, absbase, userdata);
430                         }
431                 }
432                 break;
433         case ID_LI:
434                 {
435                         Library *lib= (Library *)id;
436                         if(rewrite_path_fixed(lib->name, visit_cb, absbase, userdata)) {
437                                 BKE_library_filepath_set(lib, lib->name);
438                         }
439                 }
440                 break;
441         /* TODO: add other ID types e.g. object (modifiers) */
442         default:
443                 /* Nothing to do for other IDs that don't contain file paths. */
444                 break;
445         }
446 }
447
448 void bpath_traverse_id_list(Main *bmain, ListBase *lb, BPathVisitor visit_cb, int flag, void *userdata)
449 {
450         ID *id;
451         for(id= lb->first; id; id= id->next) {
452                 bpath_traverse_id(bmain, id, visit_cb, flag, userdata);
453         }
454 }
455
456 void bpath_traverse_main(Main *bmain, BPathVisitor visit_cb, int flag, void *userdata)
457 {
458         ListBase *lbarray[MAX_LIBARRAY];
459         int a= set_listbasepointers(bmain, lbarray);
460         while(a--) bpath_traverse_id_list(bmain, lbarray[a], visit_cb, flag, userdata);
461 }
462
463 /* Rewrites a relative path to be relative to the main file - unless the path is
464    absolute, in which case it is not altered. */
465 int bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *path_src)
466 {
467         /* be sure there is low chance of the path being too short */
468         char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
469         const char *base_new= ((char **)pathbase_v)[0];
470         const char *base_old= ((char **)pathbase_v)[1];
471
472         if (strncmp(base_old, "//", 2) == 0) {
473                 printf("%s: error, old base path '%s' is not absolute.\n",
474                        __func__, base_old);
475                 return 0;
476         }
477
478         /* Make referenced file absolute. This would be a side-effect of
479            BLI_cleanup_file, but we do it explicitely so we know if it changed. */
480         BLI_strncpy(filepath, path_src, FILE_MAX);
481         if (BLI_path_abs(filepath, base_old)) {
482                 /* Path was relative and is now absolute. Remap.
483                  * Important BLI_cleanup_dir runs before the path is made relative
484                  * because it wont work for paths that start with "//../" */
485                 BLI_cleanup_file(base_new, filepath);
486                 BLI_path_rel(filepath, base_new);
487                 BLI_strncpy(path_dst, filepath, FILE_MAX);
488                 return 1;
489         }
490         else {
491                 /* Path was not relative to begin with. */
492                 return 0;
493         }
494 }