Blender V2.61 - r43446

bpath.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): Campbell barton, Alex Fraser
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00032 /* TODO,
00033  * currently there are some cases we dont support.
00034  * - passing output paths to the visitor?, like render out.
00035  * - passing sequence strips with many images.
00036  * - passing directory paths - visitors dont know which path is a dir or a file.
00037  * */
00038 
00039 #include <sys/stat.h>
00040 
00041 #include <string.h>
00042 #include <assert.h>
00043 
00044 /* path/file handeling stuff */
00045 #ifndef WIN32
00046   #include <dirent.h>
00047   #include <unistd.h>
00048 #else
00049   #include <io.h>
00050   #include "BLI_winstuff.h"
00051 #endif
00052 
00053 #include "MEM_guardedalloc.h"
00054 
00055 #include "DNA_brush_types.h"
00056 #include "DNA_image_types.h"
00057 #include "DNA_mesh_types.h"
00058 #include "DNA_modifier_types.h"
00059 #include "DNA_movieclip_types.h"
00060 #include "DNA_object_fluidsim.h"
00061 #include "DNA_object_force.h"
00062 #include "DNA_object_types.h"
00063 #include "DNA_particle_types.h"
00064 #include "DNA_sequence_types.h"
00065 #include "DNA_sound_types.h"
00066 #include "DNA_text_types.h"
00067 #include "DNA_texture_types.h"
00068 #include "DNA_vfont_types.h"
00069 #include "DNA_scene_types.h"
00070 #include "DNA_smoke_types.h"
00071 
00072 #include "BLI_blenlib.h"
00073 #include "BLI_bpath.h"
00074 #include "BLI_utildefines.h"
00075 
00076 #include "BKE_library.h"
00077 #include "BKE_main.h"
00078 #include "BKE_report.h"
00079 #include "BKE_sequencer.h"
00080 #include "BKE_utildefines.h"
00081 #include "BKE_image.h" /* so we can check the image's type */
00082 
00083 static int checkMissingFiles_visit_cb(void *userdata, char *UNUSED(path_dst), const char *path_src)
00084 {
00085     ReportList *reports= (ReportList *)userdata;
00086 
00087     if (!BLI_exists(path_src)) {
00088         BKE_reportf(reports, RPT_WARNING, "Path Not Found \"%s\"", path_src);
00089     }
00090 
00091     return FALSE;
00092 }
00093 
00094 /* high level function */
00095 void checkMissingFiles(Main *bmain, ReportList *reports)
00096 {
00097     bpath_traverse_main(bmain, checkMissingFiles_visit_cb, BPATH_TRAVERSE_ABS, reports);
00098 }
00099 
00100 typedef struct BPathRemap_Data
00101 {
00102     const char *basedir;
00103     ReportList *reports;
00104 
00105     int count_tot;
00106     int count_changed;
00107     int count_failed;
00108 } BPathRemap_Data;
00109 
00110 static int makeFilesRelative_visit_cb(void *userdata, char *path_dst, const char *path_src)
00111 {
00112     BPathRemap_Data *data= (BPathRemap_Data *)userdata;
00113 
00114     data->count_tot++;
00115 
00116     if(strncmp(path_src, "//", 2)==0) {
00117         return FALSE; /* already relative */
00118     }
00119     else {
00120         strcpy(path_dst, path_src);
00121         BLI_path_rel(path_dst, data->basedir);
00122         if (strncmp(path_dst, "//", 2)==0) {
00123             data->count_changed++;
00124         }
00125         else {
00126             BKE_reportf(data->reports, RPT_WARNING, "Path cant be made relative \"%s\"", path_src);
00127             data->count_failed++;
00128         }
00129         return TRUE;
00130     }
00131 }
00132 
00133 void makeFilesRelative(Main *bmain, const char *basedir, ReportList *reports)
00134 {
00135     BPathRemap_Data data= {NULL};
00136 
00137     if(basedir[0] == '\0') {
00138         printf("%s: basedir='', this is a bug\n", __func__);
00139         return;
00140     }
00141 
00142     data.basedir= basedir;
00143     data.reports= reports;
00144 
00145     bpath_traverse_main(bmain, makeFilesRelative_visit_cb, 0, (void *)&data);
00146 
00147     BKE_reportf(reports, data.count_failed ? RPT_WARNING : RPT_INFO,
00148                 "Total files %d|Changed %d|Failed %d",
00149                 data.count_tot, data.count_changed, data.count_failed);
00150 }
00151 
00152 static int makeFilesAbsolute_visit_cb(void *userdata, char *path_dst, const char *path_src)
00153 {
00154     BPathRemap_Data *data= (BPathRemap_Data *)userdata;
00155 
00156     data->count_tot++;
00157 
00158     if(strncmp(path_src, "//", 2)!=0) {
00159         return FALSE; /* already absolute */
00160     }
00161     else {
00162         strcpy(path_dst, path_src);
00163         BLI_path_abs(path_dst, data->basedir);
00164         if (strncmp(path_dst, "//", 2)!=0) {
00165             data->count_changed++;
00166         }
00167         else {
00168             BKE_reportf(data->reports, RPT_WARNING, "Path cant be made absolute \"%s\"", path_src);
00169             data->count_failed++;
00170         }
00171         return TRUE;
00172     }
00173 }
00174 
00175 /* similar to makeFilesRelative - keep in sync! */
00176 void makeFilesAbsolute(Main *bmain, const char *basedir, ReportList *reports)
00177 {
00178     BPathRemap_Data data= {NULL};
00179 
00180     if(basedir[0] == '\0') {
00181         printf("%s: basedir='', this is a bug\n", __func__);
00182         return;
00183     }
00184 
00185     data.basedir= basedir;
00186     data.reports= reports;
00187 
00188     bpath_traverse_main(bmain, makeFilesAbsolute_visit_cb, 0, (void *)&data);
00189 
00190     BKE_reportf(reports, data.count_failed ? RPT_WARNING : RPT_INFO,
00191                 "Total files %d|Changed %d|Failed %d",
00192                 data.count_tot, data.count_changed, data.count_failed);
00193 }
00194 
00195 
00196 /* find this file recursively, use the biggest file so thumbnails dont get used by mistake
00197  - dir: subdir to search
00198  - filename: set this filename
00199  - filesize: filesize for the file
00200 */
00201 #define MAX_RECUR 16
00202 static int findFileRecursive(char *filename_new,
00203                              const char *dirname,
00204                              const char *filename,
00205                              int *filesize,
00206                              int *recur_depth)
00207 {
00208     /* file searching stuff */
00209     DIR *dir;
00210     struct dirent *de;
00211     struct stat status;
00212     char path[FILE_MAX];
00213     int size;
00214 
00215     dir= opendir(dirname);
00216 
00217     if (dir==NULL)
00218         return 0;
00219 
00220     if (*filesize == -1)
00221         *filesize= 0; /* dir opened fine */
00222 
00223     while ((de= readdir(dir)) != NULL) {
00224 
00225         if (strcmp(".", de->d_name)==0 || strcmp("..", de->d_name)==0)
00226             continue;
00227 
00228         BLI_join_dirfile(path, sizeof(path), dirname, de->d_name);
00229 
00230         if (stat(path, &status) != 0)
00231             continue; /* cant stat, dont bother with this file, could print debug info here */
00232 
00233         if (S_ISREG(status.st_mode)) { /* is file */
00234             if (strncmp(filename, de->d_name, FILE_MAX)==0) { /* name matches */
00235                 /* open the file to read its size */
00236                 size= status.st_size;
00237                 if ((size > 0) && (size > *filesize)) { /* find the biggest file */
00238                     *filesize= size;
00239                     BLI_strncpy(filename_new, path, FILE_MAX);
00240                 }
00241             }
00242         }
00243         else if (S_ISDIR(status.st_mode)) { /* is subdir */
00244             if (*recur_depth <= MAX_RECUR) {
00245                 (*recur_depth)++;
00246                 findFileRecursive(filename_new, path, filename, filesize, recur_depth);
00247                 (*recur_depth)--;
00248             }
00249         }
00250     }
00251     closedir(dir);
00252     return 1;
00253 }
00254 
00255 typedef struct BPathFind_Data
00256 {
00257     const char *basedir;
00258     char searchdir[FILE_MAX];
00259     ReportList *reports;
00260 } BPathFind_Data;
00261 
00262 static int findMissingFiles_visit_cb(void *userdata, char *path_dst, const char *path_src)
00263 {
00264     BPathFind_Data *data= (BPathFind_Data *)userdata;
00265     char filename_new[FILE_MAX];
00266 
00267     int filesize= -1;
00268     int recur_depth= 0;
00269 
00270     findFileRecursive(filename_new,
00271                       data->searchdir, BLI_path_basename((char *)path_src),
00272                       &filesize, &recur_depth);
00273 
00274     if (filesize == -1) { /* could not open dir */
00275         BKE_reportf(data->reports, RPT_WARNING,
00276                     "Could not find \"%s\" in \"%s\"",
00277                     BLI_path_basename((char *)path_src), data->searchdir);
00278         return FALSE;
00279     }
00280     else {
00281         strcpy(path_dst, filename_new);
00282         return TRUE;
00283     }
00284 }
00285 
00286 void findMissingFiles(Main *bmain, const char *searchpath, ReportList *reports)
00287 {
00288     struct BPathFind_Data data= {NULL};
00289 
00290     data.reports= reports;
00291     BLI_split_dir_part(searchpath, data.searchdir, sizeof(data.searchdir));
00292 
00293     bpath_traverse_main(bmain, findMissingFiles_visit_cb, 0, (void *)&data);
00294 }
00295 
00296 /* Run a visitor on a string, replacing the contents of the string as needed. */
00297 static int rewrite_path_fixed(char *path, BPathVisitor visit_cb, const char *absbase, void *userdata)
00298 {
00299     char path_src_buf[FILE_MAX];
00300     const char *path_src;
00301     char path_dst[FILE_MAX];
00302 
00303     if (absbase) {
00304         BLI_strncpy(path_src_buf, path, sizeof(path_src_buf));
00305         BLI_path_abs(path_src_buf, absbase);
00306         path_src= path_src_buf;
00307     }
00308     else {
00309         path_src= path;
00310     }
00311 
00312     if (visit_cb(userdata, path_dst, path_src)) {
00313         BLI_strncpy(path, path_dst, FILE_MAX);
00314         return TRUE;
00315     }
00316     else {
00317         return FALSE;
00318     }
00319 }
00320 
00321 static int rewrite_path_fixed_dirfile(char path_dir[FILE_MAXDIR],
00322                                       char path_file[FILE_MAXFILE],
00323                                       BPathVisitor visit_cb,
00324                                       const char *absbase,
00325                                       void *userdata)
00326 {
00327     char path_src[FILE_MAX];
00328     char path_dst[FILE_MAX];
00329 
00330     BLI_join_dirfile(path_src, sizeof(path_src), path_dir, path_file);
00331 
00332     if (absbase) {
00333         BLI_path_abs(path_src, absbase);
00334     }
00335 
00336     if (visit_cb(userdata, path_dst, (const char *)path_src)) {
00337         BLI_split_dirfile(path_dst, path_dir, path_file, FILE_MAXDIR, FILE_MAXFILE);
00338         return TRUE;
00339     }
00340     else {
00341         return FALSE;
00342     }
00343 }
00344 
00345 static int rewrite_path_alloc(char **path, BPathVisitor visit_cb, const char *absbase, void *userdata)
00346 {
00347     char path_src_buf[FILE_MAX];
00348     const char *path_src;
00349     char path_dst[FILE_MAX];
00350 
00351     if (absbase) {
00352         BLI_strncpy(path_src_buf, *path, sizeof(path_src_buf));
00353         BLI_path_abs(path_src_buf, absbase);
00354         path_src= path_src_buf;
00355     }
00356     else {
00357         path_src= *path;
00358     }
00359 
00360     if (visit_cb(userdata, path_dst, path_src)) {
00361         MEM_freeN((*path));
00362         (*path)= BLI_strdup(path_dst);
00363         return TRUE;
00364     }
00365     else {
00366         return FALSE;
00367     }
00368 }
00369 
00370 /* Run visitor function 'visit' on all paths contained in 'id'. */
00371 void bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
00372 {
00373     Image *ima;
00374     const char *absbase= (flag & BPATH_TRAVERSE_ABS) ? ID_BLEND_PATH(bmain, id) : NULL;
00375 
00376     if ((flag & BPATH_TRAVERSE_SKIP_LIBRARY) && id->lib) {
00377         return;
00378     }
00379 
00380     switch(GS(id->name)) {
00381     case ID_IM:
00382         ima= (Image *)id;
00383         if (ima->packedfile == NULL || (flag & BPATH_TRAVERSE_SKIP_PACKED) == 0) {
00384             if (ELEM3(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) {
00385                 rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data);
00386             }
00387         }
00388         break;
00389     case ID_BR:
00390         {
00391             Brush *brush= (Brush *)id;
00392             if (brush->icon_filepath[0]) {
00393                 rewrite_path_fixed(brush->icon_filepath, visit_cb, absbase, bpath_user_data);
00394             }
00395         }
00396         break;
00397     case ID_OB:
00398 
00399 #define BPATH_TRAVERSE_POINTCACHE(ptcaches)                                    \
00400     {                                                                          \
00401         PointCache *cache;                                                     \
00402         for(cache= (ptcaches).first; cache; cache= cache->next) {              \
00403             if(cache->flag & PTCACHE_DISK_CACHE) {                             \
00404                 rewrite_path_fixed(cache->path,                                \
00405                                    visit_cb,                                   \
00406                                    absbase,                                    \
00407                                    bpath_user_data);                           \
00408             }                                                                  \
00409         }                                                                      \
00410     }                                                                          \
00411 
00412 
00413         {
00414             Object *ob= (Object *)id;
00415             ModifierData *md;
00416             ParticleSystem *psys;
00417 
00418 
00419             /* do via modifiers instead */
00420 #if 0
00421             if (ob->fluidsimSettings) {
00422                 rewrite_path_fixed(ob->fluidsimSettings->surfdataPath, visit_cb, absbase, bpath_user_data);
00423             }
00424 #endif
00425 
00426             for (md= ob->modifiers.first; md; md= md->next) {
00427                 if (md->type == eModifierType_Fluidsim) {
00428                     FluidsimModifierData *fluidmd= (FluidsimModifierData *)md;
00429                     if (fluidmd->fss) {
00430                         rewrite_path_fixed(fluidmd->fss->surfdataPath, visit_cb, absbase, bpath_user_data);
00431                     }
00432                 }
00433                 else if (md->type == eModifierType_Smoke) {
00434                     SmokeModifierData *smd= (SmokeModifierData *)md;
00435                     if(smd->type & MOD_SMOKE_TYPE_DOMAIN) {
00436                         BPATH_TRAVERSE_POINTCACHE(smd->domain->ptcaches[0]);
00437                     }
00438                 }
00439                 else if (md->type==eModifierType_Cloth) {
00440                     ClothModifierData *clmd= (ClothModifierData*) md;
00441                     BPATH_TRAVERSE_POINTCACHE(clmd->ptcaches);
00442                 }
00443                 else if (md->type==eModifierType_Ocean) {
00444                     OceanModifierData *omd= (OceanModifierData*) md;
00445                     rewrite_path_fixed(omd->cachepath, visit_cb, absbase, bpath_user_data);
00446                 }
00447             }
00448 
00449             if (ob->soft) {
00450                 BPATH_TRAVERSE_POINTCACHE(ob->soft->ptcaches);
00451             }
00452 
00453             for (psys= ob->particlesystem.first; psys; psys= psys->next) {
00454                 BPATH_TRAVERSE_POINTCACHE(psys->ptcaches);
00455             }
00456         }
00457 
00458 #undef BPATH_TRAVERSE_POINTCACHE
00459 
00460         break;
00461     case ID_SO:
00462         {
00463             bSound *sound= (bSound *)id;
00464             if (sound->packedfile == NULL || (flag & BPATH_TRAVERSE_SKIP_PACKED) == 0) {
00465                 rewrite_path_fixed(sound->name, visit_cb, absbase, bpath_user_data);
00466             }
00467         }
00468         break;
00469     case ID_TXT:
00470         if (((Text*)id)->name) {
00471             rewrite_path_alloc(&((Text *)id)->name, visit_cb, absbase, bpath_user_data);
00472         }
00473         break;
00474     case ID_VF:
00475         {
00476             VFont *vf= (VFont *)id;
00477             if (vf->packedfile == NULL || (flag & BPATH_TRAVERSE_SKIP_PACKED) == 0) {
00478                 if (strcmp(vf->name, FO_BUILTIN_NAME) != 0) {
00479                     rewrite_path_fixed(((VFont *)id)->name, visit_cb, absbase, bpath_user_data);
00480                 }
00481             }
00482         }
00483         break;
00484     case ID_TE:
00485         {
00486             Tex *tex = (Tex *)id;
00487             if (tex->plugin) {
00488                 /* FIXME: rewrite_path assumes path length of FILE_MAX, but
00489                        tex->plugin->name is 160. ... is this field even a path? */
00490                 //rewrite_path(tex->plugin->name, visit_cb, bpath_user_data);
00491             }
00492             if (tex->type == TEX_VOXELDATA && TEX_VD_IS_SOURCE_PATH(tex->vd->file_format)) {
00493                 rewrite_path_fixed(tex->vd->source_path, visit_cb, absbase, bpath_user_data);
00494             }
00495         }
00496         break;
00497 
00498     case ID_SCE:
00499         {
00500             Scene *scene= (Scene *)id;
00501             if (scene->ed) {
00502                 Sequence *seq;
00503 
00504                 SEQ_BEGIN(scene->ed, seq) {
00505                     if (SEQ_HAS_PATH(seq)) {
00506                         if (ELEM(seq->type, SEQ_MOVIE, SEQ_SOUND)) {
00507                             rewrite_path_fixed_dirfile(seq->strip->dir, seq->strip->stripdata->name,
00508                                                        visit_cb, absbase, bpath_user_data);
00509                         }
00510                         else if (seq->type == SEQ_IMAGE) {
00511                             /* might want an option not to loop over all strips */
00512                             StripElem *se= seq->strip->stripdata;
00513                             int len= MEM_allocN_len(se) / sizeof(*se);
00514                             int i;
00515 
00516                             if (flag & BPATH_TRAVERSE_SKIP_MULTIFILE) {
00517                                 /* only operate on one path */
00518                                 len= MIN2(1, len);
00519                             }
00520 
00521                             for(i= 0; i < len; i++, se++) {
00522                                 rewrite_path_fixed_dirfile(seq->strip->dir, se->name,
00523                                                            visit_cb, absbase, bpath_user_data);
00524                             }
00525                         }
00526                         else {
00527                             /* simple case */
00528                             rewrite_path_fixed(seq->strip->dir, visit_cb, absbase, bpath_user_data);
00529                         }
00530                     }
00531                     else if (seq->plugin) {
00532                         rewrite_path_fixed(seq->plugin->name, visit_cb, absbase, bpath_user_data);
00533                     }
00534 
00535                 }
00536                 SEQ_END
00537             }
00538         }
00539         break;
00540     case ID_ME:
00541         {
00542             Mesh *me= (Mesh *)id;
00543             if (me->fdata.external) {
00544                 rewrite_path_fixed(me->fdata.external->filename, visit_cb, absbase, bpath_user_data);
00545             }
00546         }
00547         break;
00548     case ID_LI:
00549         {
00550             Library *lib= (Library *)id;
00551             if(rewrite_path_fixed(lib->name, visit_cb, absbase, bpath_user_data)) {
00552                 BKE_library_filepath_set(lib, lib->name);
00553             }
00554         }
00555         break;
00556     case ID_MC:
00557         {
00558             MovieClip *clip= (MovieClip *)id;
00559             rewrite_path_fixed(clip->name, visit_cb, absbase, bpath_user_data);
00560         }
00561         break;
00562     default:
00563         /* Nothing to do for other IDs that don't contain file paths. */
00564         break;
00565     }
00566 }
00567 
00568 void bpath_traverse_id_list(Main *bmain, ListBase *lb, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
00569 {
00570     ID *id;
00571     for(id= lb->first; id; id= id->next) {
00572         bpath_traverse_id(bmain, id, visit_cb, flag, bpath_user_data);
00573     }
00574 }
00575 
00576 void bpath_traverse_main(Main *bmain, BPathVisitor visit_cb, const int flag, void *bpath_user_data)
00577 {
00578     ListBase *lbarray[MAX_LIBARRAY];
00579     int a= set_listbasepointers(bmain, lbarray);
00580     while(a--) bpath_traverse_id_list(bmain, lbarray[a], visit_cb, flag, bpath_user_data);
00581 }
00582 
00583 /* Rewrites a relative path to be relative to the main file - unless the path is
00584    absolute, in which case it is not altered. */
00585 int bpath_relocate_visitor(void *pathbase_v, char *path_dst, const char *path_src)
00586 {
00587     /* be sure there is low chance of the path being too short */
00588     char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
00589     const char *base_new= ((char **)pathbase_v)[0];
00590     const char *base_old= ((char **)pathbase_v)[1];
00591 
00592     if (strncmp(base_old, "//", 2) == 0) {
00593         printf("%s: error, old base path '%s' is not absolute.\n",
00594                __func__, base_old);
00595         return FALSE;
00596     }
00597 
00598     /* Make referenced file absolute. This would be a side-effect of
00599        BLI_cleanup_file, but we do it explicitely so we know if it changed. */
00600     BLI_strncpy(filepath, path_src, FILE_MAX);
00601     if (BLI_path_abs(filepath, base_old)) {
00602         /* Path was relative and is now absolute. Remap.
00603          * Important BLI_cleanup_dir runs before the path is made relative
00604          * because it wont work for paths that start with "//../" */
00605         BLI_cleanup_file(base_new, filepath);
00606         BLI_path_rel(filepath, base_new);
00607         BLI_strncpy(path_dst, filepath, FILE_MAX);
00608         return TRUE;
00609     }
00610     else {
00611         /* Path was not relative to begin with. */
00612         return FALSE;
00613     }
00614 }