Index: source/blender/python/api2_2x/Sys.c =================================================================== --- source/blender/python/api2_2x/Sys.c (revision 83) +++ source/blender/python/api2_2x/Sys.c (working copy) @@ -25,7 +25,7 @@ * * This is a new part of Blender. * - * Contributor(s): Willian P. Germano, Campbell Barton + * Contributor(s): Willian P. Germano, Campbell Barton, Dietrich Bollmann. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -61,6 +61,7 @@ static PyObject *M_sys_time( PyObject * self ); static PyObject *M_sys_sleep( PyObject * self, PyObject * args ); static PyObject *M_sys_expandpath( PyObject *self, PyObject *args); +static PyObject *M_sys_cleanpath( PyObject *self, PyObject *args); /* (dietrich) */ /*****************************************************************************/ /* The following string definitions are used for documentation strings. */ @@ -127,6 +128,16 @@ This function expands these to their actual content, returning a valid path.\n\ If the special chars are not found in the given path, it is simply returned."; +static char M_sys_cleanpath_doc[] = +"(path) - Simplify a path to its canonical form.\n\ +(path) - The string path to simplify.\n\n\ +Cleanup a path containing redundand expressions like '/./',\n\ +'/dummy/../' and '//' and transform it into its canonical form.\n\ +Special Blender path character sequences like the prefix '//'\n\ +for paths relative to the directory of the current Blender file\n\ +and the postfix '#' for the current frame number are not changed.\n\n\ +Ex: '///foo///.//dummy///.././bar/#' --> '//foo/bar/#'"; + /*****************************************************************************/ /* Python method structure definition for Blender.sys module: */ /*****************************************************************************/ @@ -142,6 +153,7 @@ {"sleep", M_sys_sleep, METH_VARARGS, M_sys_sleep_doc}, {"time", ( PyCFunction ) M_sys_time, METH_NOARGS, M_sys_time_doc}, {"expandpath", M_sys_expandpath, METH_VARARGS, M_sys_expandpath_doc}, + {"cleanpath", M_sys_cleanpath, METH_VARARGS, M_sys_cleanpath_doc}, {NULL, NULL, 0, NULL} }; @@ -394,3 +406,18 @@ return PyString_FromString(expanded); } + +static PyObject *M_sys_cleanpath( PyObject * self, PyObject * args ) +{ + char *path = NULL; + char cleaned[FILE_MAXDIR + FILE_MAXFILE]; + + if (!PyArg_ParseTuple( args, "s", &path )) + return EXPP_ReturnPyObjError( PyExc_TypeError, + "expected string argument" ); + + BLI_strncpy(cleaned, path, FILE_MAXDIR + FILE_MAXFILE); + BLI_clean_blender_path(cleaned); + + return PyString_FromString(cleaned); +} Index: source/blender/blenlib/BLI_blenlib.h =================================================================== --- source/blender/blenlib/BLI_blenlib.h (revision 83) +++ source/blender/blenlib/BLI_blenlib.h (working copy) @@ -25,7 +25,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Dietrich Bollmann. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** * @@ -134,6 +134,23 @@ */ void BLI_cleanup_dir(const char *relabase, char *dir); +/** + Transform path into its canonical form + by cleaning it up from redundancies like '/./', '/dummy/../' and '//'. +*/ +void BLI_clean_path(char *path); + +/** + Similar to `BLI_clean_path()' but preserves the Blender convention + to start paths relative to the current blender file with two + separators. + + Example: + + `///foo/.//dummy/../bar' is simplified to `//foo/bar' and not to `/foo/bar'. +*/ +void BLI_clean_blender_path(char *path); + /** * Blender's path code replacement function. * Bases @a path strings leading with "//" by the Index: source/blender/blenlib/intern/util.c =================================================================== --- source/blender/blenlib/intern/util.c (revision 83) +++ source/blender/blenlib/intern/util.c (working copy) @@ -29,7 +29,7 @@ * * The Original Code is: all of this file. * - * Contributor(s): none yet. + * Contributor(s): Dietrich Bollmann. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** * @@ -53,7 +53,6 @@ #include "BLI_util.h" #include "BKE_utildefines.h" - #ifdef HAVE_CONFIG_H #include #endif @@ -683,84 +682,210 @@ void BLI_cleanup_dir(const char *relabase, char *dir) { short a; - char *start, *eind; - + BLI_convertstringcode(dir, relabase, 0); #ifdef WIN32 if(dir[0]=='.') { /* happens for example in FILE_MAIN */ get_default_root(dir); return; - } + } +#else + if(dir[0]=='.') { /* happens, for example in FILE_MAIN */ + dir[0]= '/'; + dir[1]= 0; + return; + } +#endif - while ( (start = strstr(dir, "\\..\\")) ) { - eind = start + strlen("\\..\\") - 1; - a = start-dir-1; - while (a>0) { - if (dir[a] == '\\') break; + /* clean path from redundancies like '/./', '/dummy/../' and '//' */ + BLI_clean_path(dir); + + /* ensure that the dir terminates in a separator */ +#ifdef WIN32 + if((a = strlen(dir))){ /* remove the '\\' at the end */ + while(a>0 && dir[a-1] == '\\'){ a--; + dir[a] = 0; } - strcpy(dir+a,eind); } - - while ( (start = strstr(dir,"\\.\\")) ){ - eind = start + strlen("\\.\\") - 1; - strcpy(start,eind); + strcat(dir, "\\"); +#else + if( (a = strlen(dir)) ){ /* remove all '/' at the end */ + while(a>0 && dir[a-1] == '/'){ + a--; + dir[a] = 0; + if (a<=0) break; + } } + strcat(dir, "/"); +#endif +} - while ( (start = strstr(dir,"\\\\" )) ){ +/** + Transform path into its canonical form + by cleaning it up from redundancies like '/./', '/dummy/../' and '//'. +*/ +/* + This used to be part of BLI_cleanup_dir() and was even more ugly + than it is probably now :). + + I changed the order of the reductions for not reducing '//../' and + '/./../' anymore to '/' and corrected it in a way that things like + '/foo/../../bar' are correctly reduced to '/../bar'. + + (dietrich, 2007/07/24) + */ +void BLI_clean_path(char *path) +{ + short a; + char *rest, *start, *eind; + int found; + +#ifdef WIN32 + + /* 1. '\\' --> '\' + This has to be done before the reduction of '\..\' + as something like '\\..\' else would be reduced to '\'. + */ + rest = path; + while ( (start = strstr(rest, "\\\\")) ){ eind = start + strlen("\\\\") - 1; - strcpy(start,eind); + strcpy(start, eind); + /* continue with rest */ + rest = start; } - if((a = strlen(dir))){ /* remove the '\\' at the end */ - while(a>0 && dir[a-1] == '\\'){ + /* 2: '\.\' --> '\' + This has to be done before the reduction of '\..\' + as cases like '\.\..\' else would be reduced to '\'. + */ + rest = path; + while ( (start = strstr(rest, "\\.\\")) ){ + eind = start + strlen("\\.\\") - 1; + strcpy(start, eind); + /* continue with rest */ + rest = start; + } + + /* 3: '\foo\..\' --> '\' */ + rest = path; + while ( (start = strstr(rest, "\\..\\")) ) { /* search for the '\..\' part */ + eind = start + strlen("\\..\\") - 1; + a = start - rest - 1; + /* search for the '\foo' part */ + found = 0; + while (a >= 0) { + if (rest[a] == '\\') { + found = 1; + break; + } a--; - dir[a] = 0; } + /* simplify if '\foo' was found; + continue with the rest of the string if not. + */ + if (found) { + /* simplify if a separator was found */ + strcpy(rest + a, eind); + } else { + /* No separator found - + The current '\..\' pattern can't be simplified + as there is no '\foo' part in front of it + which would allow the reduction '\foo\..\' -> '\'. + Continue with the rest of the string. + */ + rest = eind; + } } - strcat(dir, "\\"); -#else - if(dir[0]=='.') { /* happens, for example in FILE_MAIN */ - dir[0]= '/'; - dir[1]= 0; - return; - } +#else - while ( (start = strstr(dir, "/../")) ) { - eind = start + strlen("/../") - 1; - a = start-dir-1; - while (a>0) { - if (dir[a] == '/') break; - a--; - } - strcpy(dir+a,eind); + /* 1. '//' --> '/' + This has to be done before the reduction of '/../' + as something like '//../' else would be reduced to '/'. + */ + rest = path; + while ( (start = strstr(rest, "//")) ){ + eind = start + strlen("//") - 1; + strcpy(start, eind); + /* continue with rest */ + rest = start; } - while ( (start = strstr(dir,"/./")) ){ + /* 2: '/./' --> '/' + This has to be done before the reduction of '/../' + as cases like '/./../' else would be reduced to '/'. + */ + rest = path; + while ( (start = strstr(rest, "/./")) ){ eind = start + strlen("/./") - 1; - strcpy(start,eind); + strcpy(start, eind); + /* continue with rest */ + rest = start; } - while ( (start = strstr(dir,"//" )) ){ - eind = start + strlen("//") - 1; - strcpy(start,eind); - } - - if( (a = strlen(dir)) ){ /* remove all '/' at the end */ - while(dir[a-1] == '/'){ + /* 3: '/foo/../' --> '/' */ + rest = path; + while ( (start = strstr(rest, "/../")) ) { /* search for the '/../' part */ + eind = start + strlen("/../") - 1; + a = start - rest - 1; + /* search for the '/foo' part */ + found = 0; + while (a >= 0) { + if (rest[a] == '/') { + found = 1; + break; + } a--; - dir[a] = 0; - if (a<=0) break; } + /* simplify if '/foo' was found; + continue with the rest of the string if not. + */ + if (found) { + /* simplify if a separator was found */ + strcpy(rest + a, eind); + } else { + /* No separator found - + The current '/../' pattern can't be simplified + as there is no '/foo' part in front of it + which would allow the reduction '/foo/../' -> '/'. + Continue with the rest of the string. + */ + rest = eind; + } } - strcat(dir, "/"); #endif } +/** + Similar to `BLI_clean_path()' but preserves the Blender convention + to start paths relative to the current blender file with two + separators. + Example: + + `///foo/.//dummy/../bar' is simplified to `//foo/bar' and not to `/foo/bar'. +*/ +void BLI_clean_blender_path(char *path) +{ +#ifdef WIN32 + char sep = '\\'; +#else + char sep = '/'; +#endif + + /* preserve the leading prefix '//' + (or '\\\\' if windows is used) + in Blender paths */ + if (path[0] == sep && path[1] == sep) { + BLI_clean_path(path+1); + } else { + BLI_clean_path(path); + } +} + void BLI_makestringcode(const char *relfile, char *file) { char * p; @@ -1285,20 +1410,28 @@ /* simple appending of filename to dir, does not check for valid path! */ void BLI_join_dirfile(char *string, const char *dir, const char *file) { +#ifdef WIN32 + char sep = '\\'; +#else + char sep = '/'; +#endif + int sl_dir = strlen(dir); BLI_strncpy(string, dir, FILE_MAX); if (sl_dir > FILE_MAX-1) sl_dir = FILE_MAX-1; -#ifdef WIN32 - string[sl_dir] = '\\'; -#else - string[sl_dir] = '/'; -#endif - sl_dir++; + + /* add separator if not already ending in one (dietrich) */ + if (string[sl_dir-1] != sep) { + string[sl_dir] = sep; + sl_dir++; + } + if (sl_dir