BLI_path_utils: fix/enhance BLI_filename_make_safe(), add BLI_path_make_safe().

BLI_filename_make_safe had several issues:
* Would replace all dots ('.'), not a great idea since it would break extensions.
* Was not checking for 'control' ASCII chars (though unlikely, better to be safe here).
* Win32 only: was not checking for forbidden names (con, aux, ltp0, com0, etc.).

New BLI_path_make_safe() simply checks each path's item with BLI_filename_make_safe().
This commit is contained in:
Bastien Montagne 2015-07-14 18:42:22 +02:00
parent d0c5eac4b7
commit d7b9202567
2 changed files with 121 additions and 6 deletions

View File

@ -112,7 +112,8 @@ void BLI_cleanup_dir(const char *relabase, char *dir) ATTR_NONNULL(2);
/* doesn't touch trailing slash */
void BLI_cleanup_path(const char *relabase, char *path) ATTR_NONNULL(2);
void BLI_filename_make_safe(char *fname) ATTR_NONNULL(1);
bool BLI_filename_make_safe(char *fname) ATTR_NONNULL(1);
bool BLI_path_make_safe(char *path) ATTR_NONNULL(1);
/* go back one directory */
bool BLI_parent_dir(char *path) ATTR_NONNULL();

View File

@ -56,15 +56,25 @@
# include <windows.h>
# include <shlobj.h>
# include "BLI_winstuff.h"
# include "MEM_guardedalloc.h"
# include "BLI_alloca.h"
#else
# include "unistd.h"
#endif /* WIN32 */
#include "MEM_guardedalloc.h"
/* local */
#define UNIQUE_NAME_MAX 128
/* Declarations */
#ifdef WIN32
/* return true if the path is absolute ie starts with a drive specifier (eg A:\) or is a UNC path */
static bool BLI_path_is_abs(const char *name);
#endif /* WIN32 */
/* implementation */
/**
@ -435,17 +445,121 @@ void BLI_cleanup_file(const char *relabase, char *path)
/**
* Make given name safe to be used in paths.
*
* \return true if \a fname was changed, false otherwise.
*
* For now, simply replaces reserved chars (as listed in
* http://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words )
* by underscores ('_').
*
* \note Space case ' ' is a bit of an edge case here - in theory it is allowed, but again can be an issue
* in some cases, so we simply replace it by an underscore too (good practice anyway).
*
* \note On Windows, it also ensures there is no '.' (dot char) at the end of the file, this can lead to issues...
*
* \note On Windows, it also checks for forbidden names
* (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx ).
*/
void BLI_filename_make_safe(char *fname)
bool BLI_filename_make_safe(char *fname)
{
const char *invalid = "/\\?%*:|\"<>. ";
const char *invalid = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"/\\?%*:|\"<> ";
char *fn;
bool changed = false;
for (; *fname && (fname = strpbrk(fname, invalid)); fname++) {
*fname = '_';
if (*fname == '\0') {
return changed;
}
for (fn = fname; *fn && (fn = strpbrk(fn, invalid)); fn++) {
*fn = '_';
changed = true;
}
/* Forbid only dots. */
for (fn = fname; *fn == '.'; fn++);
if (*fn == '\0') {
*fname = '_';
changed = true;
}
#ifdef WIN32
{
const size_t len = strlen(fname);
const char *invalid_names[] = {
"con", "prn", "aux", "null",
"com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9",
"lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9",
NULL
};
char *lower_fname = BLI_strdup(fname);
const char **iname;
/* Forbid trailing dot (trailing space has already been replaced above). */
if (fname[len - 1] == '.') {
fname[len - 1] = '_';
changed = true;
}
/* Check for forbidden names - not we have to check all combination of upper and lower cases, hence the usage
* of lower_fname (more efficient than using BLI_strcasestr repeatedly). */
BLI_str_tolower_ascii(lower_fname, len);
for (iname = invalid_names; *iname; iname++) {
if (strstr(lower_fname, *iname) == lower_fname) {
const size_t iname_len = strlen(*iname);
/* Only invalid if the whole name is made of the invalid chunk, or it has an (assumed extension) dot
* just after. This means it will also catch 'valid' names like 'aux.foo.bar', but should be
* good enough for us! */
if ((iname_len == len) || (lower_fname[iname_len] == '.')) {
*fname = '_';
changed = true;
break;
}
}
}
MEM_freeN(lower_fname);
}
#endif
return changed;
}
/**
* Make given path OS-safe.
*
* \return true if \a path was changed, false otherwise.
*/
bool BLI_path_make_safe(char *path)
{
/* Simply apply BLI_filename_make_safe() over each component of the path.
* Luckily enough, same 'sfae' rules applies to filenames and dirnames. */
char *curr_slash, *curr_path = path;
bool changed = false;
bool skip_first = false;
#ifdef WIN32
if (BLI_path_is_abs(path)) {
/* Do not make safe 'C:' in 'C:\foo\bar'... */
skip_first = true;
}
#endif
for (curr_slash = (char *)BLI_first_slash(curr_path); curr_slash; curr_slash = (char *)BLI_first_slash(curr_path)) {
const char backup = *curr_slash;
*curr_slash = '\0';
if (!skip_first && (*curr_path != '\0') && BLI_filename_make_safe(curr_path)) {
changed = true;
}
skip_first = false;
curr_path = curr_slash + 1;
*curr_slash = backup;
}
if (BLI_filename_make_safe(curr_path)) {
changed = true;
}
return changed;
}
/**