added function BLI_filepathsize - so you dont have to open the file to get its size.
[blender.git] / source / blender / blenlib / intern / bpath.c
1 /**
2  *
3  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version. The Blender
9  * Foundation also sells licenses for use in proprietary software under
10  * the Blender License.  See http://www.blender.org/BL/ for information
11  * about this.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): Campbell barton
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 #include "BLI_bpath.h"
33 #include "BKE_global.h"
34 #include "DNA_ID.h" /* Library */
35 #include "DNA_vfont_types.h"
36 #include "DNA_image_types.h"
37 #include "DNA_sound_types.h"
38 #include "DNA_scene_types.h" /* to get the current frame */
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "BKE_main.h" /* so we can access G.main->*.first */
43 #include "BKE_image.h" /* so we can check the image's type */
44
45 #include "blendef.h"
46 #include "BKE_utildefines.h"
47
48 /* for writing to a textblock */
49 #include "BKE_text.h" 
50 #include "BLI_blenlib.h"
51 #include "DNA_text_types.h"
52
53 /* path/file handeling stuff */
54 #ifndef WIN32
55   #include <dirent.h>
56   #include <unistd.h>
57 #else
58   #include "BLI_winstuff.h"
59   #include <io.h>
60 #endif
61
62 #include <sys/stat.h>
63 #include <sys/types.h>
64
65 #include <fcntl.h>
66 #include <stdio.h>
67 #include <string.h>
68 #include <stdlib.h>
69
70
71 #define FILE_MAX                        240
72
73
74 /* TODO - BPATH_PLUGIN, BPATH_SEQ */
75 enum BPathTypes {
76         BPATH_IMAGE = 0,
77         BPATH_SOUND,
78         BPATH_FONT,
79         BPATH_LIB,
80
81         BPATH_DONE
82 };
83
84
85 void BLI_bpathIterator_init( struct BPathIterator *bpi ) {
86         bpi->type = BPATH_IMAGE;
87         bpi->data = NULL;
88         BLI_bpathIterator_step(bpi);
89 }
90
91 char* BLI_bpathIterator_getPath( struct BPathIterator *bpi) {
92         return bpi->path;
93 }
94 void BLI_bpathIterator_copyPathExpanded( struct BPathIterator *bpi, char *path_expanded) {
95         char *filepath, *libpath;
96         
97         filepath = BLI_bpathIterator_getPath(bpi);
98         libpath = BLI_bpathIterator_getLib(bpi);
99         
100         BLI_strncpy(path_expanded, filepath, FILE_MAXDIR*2);
101         
102         if (libpath) { /* check the files location relative to its library path */
103                 BLI_convertstringcode(path_expanded, libpath, G.scene->r.cfra);
104         } else { /* local data, use the blend files path */
105                 BLI_convertstringcode(path_expanded, G.sce, G.scene->r.cfra);
106         }
107 }
108 char* BLI_bpathIterator_getLib( struct BPathIterator *bpi) {
109         return bpi->lib;
110 }
111 char* BLI_bpathIterator_getName( struct BPathIterator *bpi) {
112         return bpi->name;
113 }
114 int     BLI_bpathIterator_getType( struct BPathIterator *bpi) {
115         return bpi->type;
116 }
117 int     BLI_bpathIterator_getPathMaxLen( struct BPathIterator *bpi) {
118         return bpi->len;
119 }
120
121 /* gets the first or the next image that has a path - not a viewer node or generated image */
122 static struct Image *ima_getpath__internal(struct Image *ima, int step_next) {
123         if (ima==NULL)
124                 return NULL;
125         
126         if (step_next)
127                 ima = ima->id.next;
128         
129         while (ima) {
130                 if (ima->packedfile==NULL && ELEM3(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE))
131                         break;
132                 /* image is not a image with a path, skip it */
133                 ima = ima->id.next;
134         }       
135         return ima;
136 }
137
138 static struct VFont *vf_getpath__internal(struct VFont *vf, int step_next) {
139         if (vf==NULL)
140                 return NULL;
141         
142         if (step_next)
143                 vf = vf->id.next;
144         
145         while (vf) {
146                 if (vf->packedfile==NULL && BLI_streq(vf->name, "<builtin>")==0) {
147                         break;
148                 }
149                 
150                 /* font with no path, skip it */
151                 vf = vf->id.next;
152         }       
153         return vf;
154 }
155
156 static struct bSound *snd_getpath__internal(struct bSound *snd, int step_next) {
157         if (snd==NULL)
158                 return NULL;
159         
160         if (step_next)
161                 snd = snd->id.next;
162         
163         while (snd) {
164                 if (snd->packedfile==NULL) {
165                         break;
166                 }
167                 
168                 /* font with no path, skip it */
169                 snd = snd->id.next;
170         }       
171         return snd;
172 }
173
174 void BLI_bpathIterator_step( struct BPathIterator *bpi) {
175         while (bpi->type != BPATH_DONE) {
176                 
177                 if  ((bpi->type) == BPATH_IMAGE) {
178                         /*if (bpi->data)        bpi->data = ((ID *)bpi->data)->next;*/
179                         if (bpi->data)  bpi->data = ima_getpath__internal( (Image *)bpi->data, 1 ); /* must skip images that have no path */
180                         else                    bpi->data = ima_getpath__internal(G.main->image.first, 0);
181                         
182                         if (bpi->data) {
183                                 /* get the path info from this datatype */
184                                 Image *ima = (Image *)bpi->data;
185                                 
186                                 bpi->lib = ima->id.lib ? ima->id.lib->filename : NULL;
187                                 bpi->path = ima->name;
188                                 bpi->name = ima->id.name+2;
189                                 bpi->len = sizeof(ima->name);
190                                 
191                                 /* we are done, advancing to the next item, this type worked fine */
192                                 break;
193                                 
194                         } else {
195                                 bpi->type+=1; /* advance to the next type */
196                         }
197                         
198                         
199                 } else if  ((bpi->type) == BPATH_SOUND) {
200                         if (bpi->data)  bpi->data = snd_getpath__internal( (bSound *)bpi->data, 1 ); /* must skip images that have no path */
201                         else                    bpi->data = snd_getpath__internal(G.main->sound.first, 0);
202                         
203                         if (bpi->data) {
204                                 /* get the path info from this datatype */
205                                 bSound *snd = (bSound *)bpi->data;
206                                 
207                                 bpi->lib = snd->id.lib ? snd->id.lib->filename : NULL;
208                                 bpi->path = snd->sample->name;
209                                 bpi->name = snd->id.name+2;
210                                 bpi->len = sizeof(snd->sample->name);
211                                 
212                                 /* we are done, advancing to the next item, this type worked fine */
213                                 break;
214                         } else {
215                                 bpi->type+=1; /* advance to the next type */
216                         }
217                         
218                         
219                 } else if  ((bpi->type) == BPATH_FONT) {
220                         
221                         if (bpi->data)  bpi->data = vf_getpath__internal( (VFont *)bpi->data, 1 );
222                         else                    bpi->data = vf_getpath__internal( G.main->vfont.first, 0 );
223                         
224                         if (bpi->data) {
225                                 /* get the path info from this datatype */
226                                 VFont *vf = (VFont *)bpi->data;
227                                 
228                                 bpi->lib = vf->id.lib ? vf->id.lib->filename : NULL;
229                                 bpi->path = vf->name;
230                                 bpi->name = vf->id.name+2;
231                                 bpi->len = sizeof(vf->name);
232                                 
233                                 /* we are done, advancing to the next item, this type worked fine */
234                                 break;
235                         } else {
236                                 bpi->type+=1; /* advance to the next type */
237                         }
238                         
239                         
240                 } else if  ((bpi->type) == BPATH_LIB) {
241                         
242                         if (bpi->data)  bpi->data = ((ID *)bpi->data)->next;
243                         else                    bpi->data = G.main->library.first;
244                         
245                         if (bpi->data) {
246                                 /* get the path info from this datatype */
247                                 Library *lib = (Library *)bpi->data;
248                                 
249                                 bpi->lib = NULL;
250                                 bpi->path = lib->name;
251                                 bpi->name = NULL;
252                                 bpi->len = sizeof(lib->name);
253                                 
254                                 /* we are done, advancing to the next item, this type worked fine */
255                                 break;
256                         } else {
257                                 bpi->type+=1; /* advance to the next type */
258                         }
259                 }
260         }
261 }
262
263 int BLI_bpathIterator_isDone( struct BPathIterator *bpi) {
264         return bpi->type==BPATH_DONE;
265 }
266
267 /* include the path argument */
268 static void bpathToText(Text *btxt, struct BPathIterator *bpi)
269 {
270         char *name;
271         char path_expanded[FILE_MAXDIR*2];
272         
273         switch(BLI_bpathIterator_getType(bpi)) {
274         case BPATH_IMAGE:
275                 txt_insert_buf( btxt, "Image \"" );
276                 break;
277         case BPATH_SOUND:
278                 txt_insert_buf( btxt, "Sound \"" );
279                 break;
280         case BPATH_FONT:
281                 txt_insert_buf( btxt, "Font \"" );
282                 break;
283         case BPATH_LIB:
284                 txt_insert_buf( btxt, "Library \"" );
285                 break;
286         default:
287                 txt_insert_buf( btxt, "Unknown \"" );
288                 break;
289         }
290         
291         name = BLI_bpathIterator_getName(bpi);
292         
293         if (name) {
294                 txt_insert_buf( btxt, name );
295         }
296         txt_insert_buf( btxt, "\" " );
297         
298         BLI_bpathIterator_copyPathExpanded(bpi, path_expanded);
299         
300         txt_insert_buf( btxt, path_expanded );
301         txt_insert_buf( btxt, "\n" );
302         txt_move_eof( btxt, 0 );
303 }
304
305 /* high level function */
306 void checkMissingFiles( char *txtname ) {
307         Text *btxt = NULL;
308         struct BPathIterator bpi;
309         
310         /* be sure there is low chance of the path being too short */
311         char filepath_expanded[FILE_MAXDIR*2]; 
312         char *filepath, *libpath;
313         int files_missing = 0;
314         
315         BLI_bpathIterator_init(&bpi);
316         while (!BLI_bpathIterator_isDone(&bpi)) {
317                 filepath = BLI_bpathIterator_getPath(&bpi);
318                 libpath = BLI_bpathIterator_getLib(&bpi);
319                 
320                 BLI_bpathIterator_copyPathExpanded( &bpi, filepath_expanded );
321                 
322                 if (!BLI_exists(filepath_expanded)) {
323                         if (!btxt) {
324                                 btxt = add_empty_text( "missing_files.log" );
325                                 if (txtname) {
326                                         BLI_strncpy(txtname, btxt->id.name+2, 24);
327                                 }
328                         }
329                         bpathToText(btxt, &bpi);
330                         files_missing = 1;
331                 }
332                 BLI_bpathIterator_step(&bpi);
333         }
334 }
335
336 /* dont log any errors at the moment, should probably do this */
337 void makeFilesRelative(char *txtname, int *tot, int *changed, int *failed, int *linked) {
338         struct BPathIterator bpi;
339         char *filepath, *libpath;
340         
341         /* be sure there is low chance of the path being too short */
342         char filepath_relative[(FILE_MAXDIR * 2) + FILE_MAXFILE];
343         
344         Text *btxt = NULL;
345         
346         *tot = *changed = *failed = *linked = 0;
347         
348         BLI_bpathIterator_init(&bpi);
349         while (!BLI_bpathIterator_isDone(&bpi)) {
350                 filepath = BLI_bpathIterator_getPath(&bpi);
351                 libpath = BLI_bpathIterator_getLib(&bpi);
352                 
353                 if(strncmp(filepath, "//", 2)) {
354                         if (libpath) { /* cant make relative if we are library - TODO, LOG THIS */
355                                 (*linked)++;
356                         } else { /* local data, use the blend files path */
357                                 BLI_strncpy(filepath_relative, filepath, sizeof(filepath_relative));
358                                 /* Important BLI_cleanup_dir runs before the path is made relative
359                                  * because it wont work for paths that start with "//../" */ 
360                                 BLI_cleanup_file(G.sce, filepath_relative); /* fix any /foo/../foo/ */
361                                 BLI_makestringcode(G.sce, filepath_relative);
362                                 /* be safe and check the length */
363                                 if (BLI_bpathIterator_getPathMaxLen(&bpi) <= strlen(filepath_relative)) {
364                                         if (!btxt) {
365                                                 btxt = add_empty_text( "missing_no_rel.log" );
366                                                 if (txtname) {
367                                                         BLI_strncpy(txtname, btxt->id.name+2, 24);
368                                                 }
369                                         }
370                                         bpathToText(btxt, &bpi);
371                                         (*failed)++;
372                                 } else {
373                                         if(strncmp(filepath_relative, "//", 2)==0) {
374                                                 strcpy(filepath, filepath_relative);
375                                                 (*changed)++;
376                                         } else {
377                                                 if (!btxt) {
378                                                         btxt = add_empty_text( "missing_no_rel.log" );
379                                                         if (txtname) {
380                                                                 BLI_strncpy(txtname, btxt->id.name+2, 24);
381                                                         }
382                                                 }
383                                                 bpathToText(btxt, &bpi);
384                                                 (*failed)++;
385                                         }
386                                 }
387                         }
388                 }
389                 BLI_bpathIterator_step(&bpi);
390                 (*tot)++;
391         }
392 }
393
394 /* dont log any errors at the moment, should probably do this -
395  * Verry similar to makeFilesRelative - keep in sync! */
396 void makeFilesAbsolute(char *txtname, int *tot, int *changed, int *failed, int *linked) {
397         struct BPathIterator bpi;
398         char *filepath, *libpath;
399         
400         /* be sure there is low chance of the path being too short */
401         char filepath_absolute[(FILE_MAXDIR * 2) + FILE_MAXFILE];
402         
403         Text *btxt = NULL;
404         
405         *tot = *changed = *failed = *linked = 0;
406         
407         BLI_bpathIterator_init(&bpi);
408         while (!BLI_bpathIterator_isDone(&bpi)) {
409                 filepath = BLI_bpathIterator_getPath(&bpi);
410                 libpath = BLI_bpathIterator_getLib(&bpi);
411                 
412                 if(strncmp(filepath, "//", 2)==0) {
413                         if (libpath) { /* cant make absolute if we are library - TODO, LOG THIS */
414                                 (*linked)++;
415                         } else { /* get the expanded path and check it is relative or too long */
416                                 BLI_bpathIterator_copyPathExpanded( &bpi, filepath_absolute );
417                                 BLI_cleanup_file(G.sce, filepath_absolute); /* fix any /foo/../foo/ */
418                                 /* to be safe, check the length */
419                                 if (BLI_bpathIterator_getPathMaxLen(&bpi) <= strlen(filepath_absolute)) {
420                                         if (!btxt) {
421                                                 btxt = add_empty_text( "missing_no_abs.log" );
422                                                 if (txtname) {
423                                                         BLI_strncpy(txtname, btxt->id.name+2, 24);
424                                                 }
425                                         }
426                                         bpathToText(btxt, &bpi);
427                                         (*failed)++;
428                                 } else {
429                                         if(strncmp(filepath_absolute, "//", 2)) {
430                                                 strcpy(filepath, filepath_absolute);
431                                                 (*changed)++;
432                                         } else {
433                                                 if (!btxt) {
434                                                         btxt = add_empty_text( "missing_no_abs.log" );
435                                                         if (txtname) {
436                                                                 BLI_strncpy(txtname, btxt->id.name+2, 24);
437                                                         }
438                                                 }
439                                                 bpathToText(btxt, &bpi);
440                                                 (*failed)++;
441                                         }
442                                 }
443                         }
444                 }
445                 BLI_bpathIterator_step(&bpi);
446                 (*tot)++;
447         }
448 }
449
450
451 /* find this file recursively, use the biggest file so thumbnails dont get used by mistake
452  - dir: subdir to search
453  - filename: set this filename
454  - filesize: filesize for the file
455 */
456 #define MAX_RECUR 16
457 static int findFileRecursive(char *filename_new, const char *dirname, const char *filename, int *filesize, int *recur_depth)
458 {
459         /* file searching stuff */
460         DIR *dir;
461         struct dirent *de;
462         struct stat status;
463         char path[FILE_MAX];
464         int size;
465         
466         dir = opendir(dirname);
467         
468         if (dir==0)
469                 return 0;
470         
471         if (*filesize == -1)
472                 *filesize = 0; /* dir opened fine */
473         
474         while ((de = readdir(dir)) != NULL) {
475                 
476                 if (strncmp(".", de->d_name, 2)==0 || strncmp("..", de->d_name, 3)==0)
477                         continue;
478                 
479                 BLI_join_dirfile(path, dirname, de->d_name);
480                 
481                 if (stat(path, &status) != 0)
482                         continue; /* cant stat, dont bother with this file, could print debug info here */
483                 
484                 if (S_ISREG(status.st_mode)) { /* is file */
485                         if (strncmp(filename, de->d_name, FILE_MAX)==0) { /* name matches */
486                                 /* open the file to read its size */
487                                 size = BLI_filepathsize(path);
488                                 if ((size > 0) && (size > *filesize)) { /* find the biggest file */
489                                         *filesize = size;
490                                         BLI_strncpy(filename_new, path, FILE_MAX);
491                                 }
492                         }
493                 } else if (S_ISDIR(status.st_mode)) { /* is subdir */
494                         if (*recur_depth <= MAX_RECUR) {
495                                 (*recur_depth)++;
496                                 findFileRecursive(filename_new, path, filename, filesize, recur_depth);
497                                 (*recur_depth)--;
498                         }
499                 }
500         }
501         closedir(dir);
502         return 1;
503 }
504
505 /* high level function - call from fileselector */
506 void findMissingFiles(char *str) {
507         struct BPathIterator bpi;
508         
509         /* be sure there is low chance of the path being too short */
510         char filepath_expanded[FILE_MAXDIR*2]; 
511         char *filepath, *libpath;
512         int filesize, recur_depth;
513         
514         char dirname[FILE_MAX], filename[FILE_MAX], filename_new[FILE_MAX], dummyname[FILE_MAX];
515         
516         BLI_split_dirfile(str, dirname, dummyname);
517         
518         BLI_bpathIterator_init(&bpi);
519         
520         while (!BLI_bpathIterator_isDone(&bpi)) {
521                 filepath = BLI_bpathIterator_getPath(&bpi);
522                 libpath = BLI_bpathIterator_getLib(&bpi);
523                 
524                 if (libpath==NULL) {
525                         
526                         BLI_bpathIterator_copyPathExpanded( &bpi, filepath_expanded );
527                         
528                         if (!BLI_exists(filepath_expanded)) {
529                                 /* can the dir be opened? */
530                                 filesize = -1;
531                                 recur_depth = 0;
532                                 BLI_split_dirfile(filepath, dummyname, filename); /* the file to find */
533                                 
534                                 findFileRecursive(filename_new, dirname, filename, &filesize, &recur_depth);
535                                 if (filesize == -1) { /* could not open dir */
536                                         printf("Could not open dir \"%s\"\n", dirname);
537                                         return;
538                                 }
539                                 
540                                 if (filesize > 0) {
541                                         
542                                         if (BLI_bpathIterator_getPathMaxLen( &bpi ) < strlen(filename_new)) { 
543                                                 printf("cannot set path \"%s\" too long!", filename_new);
544                                         } else {
545                                                 /* copy the found path into the old one */
546                                                 if (G.relbase_valid)
547                                                         BLI_makestringcode(G.sce, filename_new);
548                                                 
549                                                 strcpy( BLI_bpathIterator_getPath( &bpi ), filename_new );
550                                         }
551                                 }
552                         }
553                 }
554                 BLI_bpathIterator_step(&bpi);
555         }
556 }