diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index 5b4d0e5de74..8df2d6d534f 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -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; diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index b22fa5817e7..7073d69ae54 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -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. diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index a627233ede5..5cfeeeeeebc 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -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 * \{ */ diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 2b519cb3dec..37967e00e71 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -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] == ':'); diff --git a/source/blender/windowmanager/intern/wm_files.cc b/source/blender/windowmanager/intern/wm_files.cc index 6991b0757e6..a6c644d155a 100644 --- a/source/blender/windowmanager/intern/wm_files.cc +++ b/source/blender/windowmanager/intern/wm_files.cc @@ -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)); diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 3940d3509cc..4d60f0515fc 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -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);