BLI_path: add native path canonicalize function

This function handles cleaning valid system paths that are functional
when passed to `open(..)` but may be relative to the current working
directory or have redundant slashers that can be normalized.
This commit is contained in:
Campbell Barton
2023-05-17 13:07:45 +10:00
parent 0099f51cdd
commit e27cb91442
6 changed files with 74 additions and 13 deletions

View File

@@ -120,7 +120,18 @@ enum {
typedef struct Main {
struct Main *next, *prev;
/** The file-path of this blend file, an empty string indicates an unsaved file. */
/**
* The file-path of this blend file, an empty string indicates an unsaved file.
*
* \note For the current loaded blend file this path should be absolute & normalized
* to prevent redundant leading slashes or current-working-directory relative paths
* from causing problems with absolute/relative patch conversion that relies on this being
* an absolute path. See #BLI_path_canonicalize_native.
*
* This rule is not strictly enforced as in some cases loading a #Main is performed
* to read data temporarily (preferences & startup) for e.g.
* where the `filepath` is not persistent or used as a basis for other paths.
*/
char filepath[1024]; /* 1024 = FILE_MAX */
short versionfile, subversionfile; /* see BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION */
short minversionfile, minsubversionfile;

View File

@@ -897,8 +897,7 @@ void BKE_appdir_program_path_init(const char *argv0)
* Otherwise other methods of detecting the binary that override this argument
* which must point to the Python module for data-files to be detected. */
STRNCPY(g_app.program_filepath, argv0);
BLI_path_abs_from_cwd(g_app.program_filepath, sizeof(g_app.program_filepath));
BLI_path_normalize_native(g_app.program_filepath);
BLI_path_canonicalize_native(g_app.program_filepath, sizeof(g_app.program_filepath));
if (g_app.program_dirname[0] == '\0') {
/* First time initializing, the file binary path isn't valid from a Python module.

View File

@@ -185,6 +185,28 @@ void BLI_path_normalize_unc(char *path, int path_maxncpy);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Path Canonicalize
* \{ */
/**
* Convert `path` to a canonical representation.
* This is intended for system paths (passed in as command-line arguments of via scripts)
* which are valid in that they resolve to a file/directory and but could be `CWD` relative or
* contain redundant slashes that cause absolute/relative conversion to fail.
* (specifically the "//" prefix used by Blender).
*
* Perform the following operations:
*
* - Make absolute (relative to the current working directory).
* - Convert slash direction (WIN32 only, as other systems may use back-slashes in filenames).
* - Normalize redundant slashes.
* - Strip trailing slashes.
*/
int BLI_path_canonicalize_native(char *path, int path_maxncpy);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Path FileName Manipulation
* \{ */

View File

@@ -43,6 +43,7 @@ static int BLI_path_unc_prefix_len(const char *path);
#ifdef WIN32
static bool BLI_path_is_abs_win32(const char *path);
static int BLI_path_win32_prefix_len(const char *path);
#endif /* WIN32 */
/**
@@ -384,6 +385,29 @@ int BLI_path_normalize_dir(char *dir, size_t dir_maxncpy)
return BLI_path_slash_ensure_ex(dir, dir_maxncpy, dir_len);
}
int BLI_path_canonicalize_native(char *path, int path_maxncpy)
{
BLI_path_abs_from_cwd(path, path_maxncpy);
/* As these are system level paths, only convert slashes
* if the alternate direction is accepted as a slash. */
if (BLI_path_slash_is_native_compat(ALTSEP)) {
BLI_path_slash_native(path);
}
int path_len = BLI_path_normalize_native(path);
/* Strip trailing slash but don't strip `/` away to nothing. */
if (path_len > 1 && path[path_len - 1] == SEP) {
#ifdef WIN32
/* Don't strip `C:\` -> `C:` as this is no longer a valid directory. */
if (BLI_path_win32_prefix_len(path) + 1 < path_len)
#endif
{
path_len -= 1;
path[path_len] = '\0';
}
}
return path_len;
}
bool BLI_path_make_safe_filename_ex(char *fname, bool allow_tokens)
{
#define INVALID_CHARS \
@@ -529,6 +553,16 @@ static int BLI_path_unc_prefix_len(const char *path)
return 0;
}
#ifdef WIN32
static int BLI_path_win32_prefix_len(const char *path)
{
if (BLI_path_is_win32_drive(path)) {
return 2;
}
return BLI_path_unc_prefix_len(path);
}
#endif
bool BLI_path_is_win32_drive(const char *path)
{
return isalpha(path[0]) && (path[1] == ':');

View File

@@ -2797,8 +2797,7 @@ static int wm_open_mainfile__open(bContext *C, wmOperator *op)
bool success;
RNA_string_get(op->ptr, "filepath", filepath);
BLI_path_abs_from_cwd(filepath, sizeof(filepath));
BLI_path_normalize_native(filepath);
BLI_path_canonicalize_native(filepath, sizeof(filepath));
/* re-use last loaded setting so we can reload a file without changing */
wm_open_init_load_ui(op, false);
@@ -3103,8 +3102,7 @@ static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
bool success;
RNA_string_get(op->ptr, "filepath", filepath);
BLI_path_abs_from_cwd(filepath, sizeof(filepath));
BLI_path_normalize_native(filepath);
BLI_path_canonicalize_native(filepath, sizeof(filepath));
wm_open_init_use_scripts(op, true);
SET_FLAG_FROM_TEST(G.f, RNA_boolean_get(op->ptr, "use_scripts"), G_FLAG_SCRIPT_AUTOEXEC);
@@ -3246,8 +3244,7 @@ static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
const bool is_filepath_set = RNA_struct_property_is_set(op->ptr, "filepath");
if (is_filepath_set) {
RNA_string_get(op->ptr, "filepath", filepath);
BLI_path_abs_from_cwd(filepath, sizeof(filepath));
BLI_path_normalize_native(filepath);
BLI_path_canonicalize_native(filepath, sizeof(filepath));
}
else {
STRNCPY(filepath, BKE_main_blendfile_path(bmain));

View File

@@ -1866,7 +1866,7 @@ static int arg_handle_python_file_run(int argc, const char **argv, void *data)
/* Make the path absolute because its needed for relative linked blends to be found */
char filepath[FILE_MAX];
STRNCPY(filepath, argv[1]);
BLI_path_abs_from_cwd(filepath, sizeof(filepath));
BLI_path_canonicalize_native(filepath, sizeof(filepath));
bool ok;
BPY_CTX_SETUP(ok = BPY_run_filepath(C, filepath, NULL));
@@ -2063,9 +2063,7 @@ static int arg_handle_load_file(int UNUSED(argc), const char **argv, void *data)
}
STRNCPY(filepath, argv[0]);
BLI_path_slash_native(filepath);
BLI_path_abs_from_cwd(filepath, sizeof(filepath));
BLI_path_normalize_native(filepath);
BLI_path_canonicalize_native(filepath, sizeof(filepath));
/* load the file */
BKE_reports_init(&reports, RPT_PRINT);