Add Chromium-only Blender WebEngine parity work
This commit is contained in:
810
blender-5.2.0/source/blender/blenkernel/intern/bpath.cc
Normal file
810
blender-5.2.0/source/blender/blenkernel/intern/bpath.cc
Normal file
@@ -0,0 +1,810 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
* currently there are some cases we don't support.
|
||||
* - passing output paths to the visitor?, like render out.
|
||||
* - passing sequence strips with many images.
|
||||
* - passing directory paths - visitors don't know which path is a dir or a file.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
/* path/file handling stuff */
|
||||
#ifndef WIN32
|
||||
# include <dirent.h>
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include "BLI_winstuff.h"
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "BKE_bpath.hh" /* own include */
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
|
||||
#endif
|
||||
|
||||
static CLG_LogRef LOG = {"lib.bpath"};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic Utilities
|
||||
* \{ */
|
||||
|
||||
void BKE_bpath_summary_report(const BPathSummary &summary, ReportList *reports)
|
||||
{
|
||||
BKE_reportf(reports,
|
||||
summary.count_failed ? RPT_WARNING : RPT_INFO,
|
||||
"Total files %d | Changed %d | Failed %d",
|
||||
summary.count_total,
|
||||
summary.count_changed,
|
||||
summary.count_failed);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Generic File Path Traversal API
|
||||
* \{ */
|
||||
|
||||
void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, ID *id)
|
||||
{
|
||||
const eBPathForeachFlag flag = bpath_data->flag;
|
||||
const char *absbase = (flag & BKE_BPATH_FOREACH_PATH_ABSOLUTE) ?
|
||||
ID_BLEND_PATH(bpath_data->bmain, id) :
|
||||
nullptr;
|
||||
bpath_data->absolute_base_path = absbase;
|
||||
bpath_data->owner_id = id;
|
||||
bpath_data->is_path_modified = false;
|
||||
bpath_data->is_expanded = false;
|
||||
bpath_data->is_cache = false;
|
||||
bpath_data->is_readonly = false;
|
||||
|
||||
if ((flag & BKE_BPATH_FOREACH_PATH_SKIP_LINKED) && ID_IS_LINKED(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (id->library_weak_reference != nullptr &&
|
||||
(flag & BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES) == 0)
|
||||
{
|
||||
BKE_bpath_foreach_path_fixed_process(bpath_data,
|
||||
id->library_weak_reference->library_filepath,
|
||||
sizeof(id->library_weak_reference->library_filepath));
|
||||
}
|
||||
|
||||
bNodeTree *embedded_node_tree = bke::node_tree_from_id(id);
|
||||
if (embedded_node_tree != nullptr) {
|
||||
BKE_bpath_foreach_path_id(bpath_data, &embedded_node_tree->id);
|
||||
}
|
||||
|
||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
|
||||
|
||||
BLI_assert(id_type != nullptr);
|
||||
if (id_type == nullptr || id_type->foreach_path == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
id_type->foreach_path(id, bpath_data);
|
||||
|
||||
if (bpath_data->is_path_modified) {
|
||||
DEG_id_tag_update(id, ID_RECALC_SOURCE | ID_RECALC_SYNC_TO_EVAL);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_bpath_foreach_path_main(BPathForeachPathData *bpath_data)
|
||||
{
|
||||
ID *id;
|
||||
FOREACH_MAIN_ID_BEGIN (bpath_data->bmain, id) {
|
||||
BKE_bpath_foreach_path_id(bpath_data, id);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
}
|
||||
|
||||
bool BKE_bpath_foreach_path_fixed_process(BPathForeachPathData *bpath_data,
|
||||
char *path,
|
||||
size_t path_maxncpy)
|
||||
{
|
||||
const char *absolute_base_path = bpath_data->absolute_base_path;
|
||||
|
||||
char path_src_buf[FILE_MAX];
|
||||
const char *path_src;
|
||||
char path_dst[FILE_MAX];
|
||||
|
||||
if (absolute_base_path) {
|
||||
STRNCPY(path_src_buf, path);
|
||||
BLI_path_abs(path_src_buf, absolute_base_path);
|
||||
path_src = path_src_buf;
|
||||
}
|
||||
else {
|
||||
path_src = path;
|
||||
}
|
||||
|
||||
/* so functions can check old value */
|
||||
STRNCPY(path_dst, path);
|
||||
|
||||
if (bpath_data->callback_function(bpath_data, path_dst, sizeof(path_dst), path_src)) {
|
||||
BLI_strncpy(path, path_dst, path_maxncpy);
|
||||
bpath_data->is_path_modified = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BKE_bpath_foreach_path_readonly_process(BPathForeachPathData *bpath_data, const char *path)
|
||||
{
|
||||
char path_src[FILE_MAX];
|
||||
char path_dst[FILE_MAX];
|
||||
|
||||
STRNCPY(path_src, path);
|
||||
if (bpath_data->absolute_base_path) {
|
||||
BLI_path_abs(path_src, bpath_data->absolute_base_path);
|
||||
}
|
||||
/* Any modification to path_dst will be discarded. */
|
||||
STRNCPY(path_dst, path_src);
|
||||
|
||||
bpath_data->is_readonly = true;
|
||||
const bool changed = bpath_data->callback_function(
|
||||
bpath_data, path_dst, sizeof(path_dst), path_src);
|
||||
bpath_data->is_readonly = false;
|
||||
|
||||
BLI_assert_msg(!changed, "Read-only path edited in bpath foreach");
|
||||
UNUSED_VARS_NDEBUG(changed);
|
||||
}
|
||||
|
||||
void BKE_bpath_sequence_filepaths_foreach(const char *abs_filepath,
|
||||
FunctionRef<void(StringRef frame_filepath)> callback)
|
||||
{
|
||||
char dirname[FILE_MAX], filename[FILE_MAX];
|
||||
BLI_path_split_dir_file(abs_filepath, dirname, sizeof(dirname), filename, sizeof(filename));
|
||||
|
||||
char head[FILE_MAX], tail[FILE_MAX];
|
||||
ushort digits_len = 0;
|
||||
BLI_path_sequence_decode(filename, head, sizeof(head), tail, sizeof(tail), &digits_len);
|
||||
|
||||
if (digits_len == 0) {
|
||||
/* Not a numbered sequence, treat as a single file. */
|
||||
if (BLI_is_file(abs_filepath)) {
|
||||
callback(abs_filepath);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t head_len = strlen(head);
|
||||
const size_t tail_len = strlen(tail);
|
||||
const size_t expected_len = head_len + size_t(digits_len) + tail_len;
|
||||
|
||||
direntry *filelist = nullptr;
|
||||
const uint filelist_num = BLI_filelist_dir_contents(dirname, &filelist);
|
||||
for (uint i = 0; i < filelist_num; i++) {
|
||||
const direntry &entry = filelist[i];
|
||||
if (!(entry.type & S_IFREG)) {
|
||||
continue;
|
||||
}
|
||||
const char *name = entry.relname;
|
||||
const size_t name_len = strlen(name);
|
||||
|
||||
/* Match files of the form <head><digits><tail>. */
|
||||
if (name_len != expected_len) {
|
||||
continue;
|
||||
}
|
||||
if (!STRPREFIX(name, head) || !BLI_str_endswith(name, tail)) {
|
||||
continue;
|
||||
}
|
||||
bool all_digits = true;
|
||||
for (size_t c = head_len; c < head_len + digits_len; c++) {
|
||||
if (!(name[c] >= '0' && name[c] <= '9')) {
|
||||
all_digits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (all_digits) {
|
||||
callback(entry.path);
|
||||
}
|
||||
}
|
||||
BLI_filelist_free(filelist, filelist_num);
|
||||
}
|
||||
|
||||
bool BKE_bpath_foreach_path_dirfile_fixed_process(BPathForeachPathData *bpath_data,
|
||||
char *path_dir,
|
||||
size_t path_dir_maxncpy,
|
||||
char *path_file,
|
||||
size_t path_file_maxncpy)
|
||||
{
|
||||
const char *absolute_base_path = bpath_data->absolute_base_path;
|
||||
|
||||
char path_src[FILE_MAX];
|
||||
char path_dst[FILE_MAX];
|
||||
|
||||
BLI_path_join(path_src, sizeof(path_src), path_dir, path_file);
|
||||
|
||||
/* So that functions can access the old value. */
|
||||
STRNCPY(path_dst, path_src);
|
||||
|
||||
if (absolute_base_path) {
|
||||
BLI_path_abs(path_src, absolute_base_path);
|
||||
}
|
||||
|
||||
if (bpath_data->callback_function(
|
||||
bpath_data, path_dst, sizeof(path_dst), const_cast<const char *>(path_src)))
|
||||
{
|
||||
BLI_path_split_dir_file(path_dst, path_dir, path_dir_maxncpy, path_file, path_file_maxncpy);
|
||||
bpath_data->is_path_modified = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_bpath_foreach_path_allocated_process(BPathForeachPathData *bpath_data, char **path)
|
||||
{
|
||||
const char *absolute_base_path = bpath_data->absolute_base_path;
|
||||
|
||||
char path_src_buf[FILE_MAX];
|
||||
const char *path_src;
|
||||
char path_dst[FILE_MAX];
|
||||
|
||||
if (absolute_base_path) {
|
||||
STRNCPY(path_src_buf, *path);
|
||||
BLI_path_abs(path_src_buf, absolute_base_path);
|
||||
path_src = path_src_buf;
|
||||
}
|
||||
else {
|
||||
path_src = *path;
|
||||
}
|
||||
|
||||
if (bpath_data->callback_function(bpath_data, path_dst, sizeof(path_dst), path_src)) {
|
||||
MEM_delete(*path);
|
||||
(*path) = BLI_strdup(path_dst);
|
||||
bpath_data->is_path_modified = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Check Missing Files
|
||||
* \{ */
|
||||
|
||||
static bool check_missing_files_foreach_path_cb(BPathForeachPathData *bpath_data,
|
||||
char * /*path_dst*/,
|
||||
size_t /*path_dst_maxncpy*/,
|
||||
const char *path_src)
|
||||
{
|
||||
ReportList *reports = static_cast<ReportList *>(bpath_data->user_data);
|
||||
|
||||
if (!BLI_exists(path_src)) {
|
||||
ID *owner_id = bpath_data->owner_id;
|
||||
if (owner_id) {
|
||||
if (ID_IS_LINKED(owner_id)) {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"Path '%s' not found, from linked data-block '%s' (from library '%s')",
|
||||
path_src,
|
||||
owner_id->name,
|
||||
owner_id->lib->runtime->filepath_abs);
|
||||
}
|
||||
else {
|
||||
BKE_reportf(reports,
|
||||
RPT_WARNING,
|
||||
"Path '%s' not found, from local data-block '%s'",
|
||||
path_src,
|
||||
owner_id->name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BKE_reportf(
|
||||
reports, RPT_WARNING, "Path '%s' not found (no known owner data-block)", path_src);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BKE_bpath_missing_files_check(Main *bmain, ReportList *reports)
|
||||
{
|
||||
BPathForeachPathData path_data{};
|
||||
path_data.bmain = bmain;
|
||||
path_data.callback_function = check_missing_files_foreach_path_cb;
|
||||
path_data.flag = BKE_BPATH_FOREACH_PATH_ABSOLUTE | BKE_BPATH_FOREACH_PATH_SKIP_PACKED |
|
||||
BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN | BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES;
|
||||
path_data.user_data = reports;
|
||||
BKE_bpath_foreach_path_main(&path_data);
|
||||
|
||||
if (reports->list.is_empty()) {
|
||||
BKE_reportf(reports, RPT_INFO, "No missing files");
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Find Missing Files
|
||||
* \{ */
|
||||
|
||||
#define MAX_DIR_RECURSE 16
|
||||
#define FILESIZE_INVALID_DIRECTORY -1
|
||||
|
||||
/**
|
||||
* Find the given filename recursively in the given search directory and its sub-directories.
|
||||
*
|
||||
* \note Use the biggest matching file found, so that thumbnails don't get used by mistake.
|
||||
*
|
||||
* \param search_directory: Directory to search in.
|
||||
* \param filename_src: Search for this filename.
|
||||
* \param r_filepath_new: The path of the new found file will be copied here, caller must
|
||||
* initialize as empty string.
|
||||
* \param r_filesize: Size of the file, `FILESIZE_INVALID_DIRECTORY` if search directory could not
|
||||
* be opened.
|
||||
* \param r_recurse_depth: Current recursion depth.
|
||||
*
|
||||
* \return true if found, false otherwise.
|
||||
*/
|
||||
static bool missing_files_find__recursive(const char *search_directory,
|
||||
const char *filename_src,
|
||||
char r_filepath_new[FILE_MAX],
|
||||
int64_t *r_filesize,
|
||||
int *r_recurse_depth)
|
||||
{
|
||||
/* TODO: Move this function to BLI_path_utils? The 'biggest size' behavior is quite specific
|
||||
* though... */
|
||||
DIR *dir;
|
||||
BLI_stat_t status;
|
||||
char path[FILE_MAX];
|
||||
int64_t size;
|
||||
bool found = false;
|
||||
|
||||
BLI_assert(!BLI_path_is_rel(search_directory));
|
||||
dir = opendir(search_directory);
|
||||
|
||||
if (dir == nullptr) {
|
||||
return found;
|
||||
}
|
||||
|
||||
if (*r_filesize == FILESIZE_INVALID_DIRECTORY) {
|
||||
*r_filesize = 0; /* The directory opened fine. */
|
||||
}
|
||||
|
||||
for (dirent *de = readdir(dir); de != nullptr; de = readdir(dir)) {
|
||||
if (FILENAME_IS_CURRPAR(de->d_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BLI_path_join(path, sizeof(path), search_directory, de->d_name);
|
||||
|
||||
if (BLI_stat(path, &status) == -1) {
|
||||
CLOG_WARN(&LOG, "Cannot get file status (`stat()`) of '%s'", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISREG(status.st_mode)) { /* It is a file. */
|
||||
if (BLI_path_ncmp(filename_src, de->d_name, FILE_MAX) == 0) { /* Names match. */
|
||||
size = status.st_size;
|
||||
if ((size > 0) && (size > *r_filesize)) { /* Find the biggest matching file. */
|
||||
*r_filesize = size;
|
||||
BLI_strncpy(r_filepath_new, path, FILE_MAX);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (S_ISDIR(status.st_mode)) { /* It is a sub-directory. */
|
||||
if (*r_recurse_depth <= MAX_DIR_RECURSE) {
|
||||
(*r_recurse_depth)++;
|
||||
found |= missing_files_find__recursive(
|
||||
path, filename_src, r_filepath_new, r_filesize, r_recurse_depth);
|
||||
(*r_recurse_depth)--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return found;
|
||||
}
|
||||
|
||||
struct BPathFind_Data {
|
||||
const char *basedir;
|
||||
const char *searchdir;
|
||||
ReportList *reports;
|
||||
bool find_all; /* Also search for files which current path is still valid. */
|
||||
};
|
||||
|
||||
static bool missing_files_find_foreach_path_cb(BPathForeachPathData *bpath_data,
|
||||
char *path_dst,
|
||||
size_t path_dst_maxncpy,
|
||||
const char *path_src)
|
||||
{
|
||||
BPathFind_Data *data = static_cast<BPathFind_Data *>(bpath_data->user_data);
|
||||
char filepath_new[FILE_MAX];
|
||||
|
||||
int64_t filesize = FILESIZE_INVALID_DIRECTORY;
|
||||
int recurse_depth = 0;
|
||||
bool is_found;
|
||||
|
||||
if (!data->find_all && BLI_exists(path_src)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
filepath_new[0] = '\0';
|
||||
|
||||
is_found = missing_files_find__recursive(
|
||||
data->searchdir, BLI_path_basename(path_src), filepath_new, &filesize, &recurse_depth);
|
||||
|
||||
if (filesize == FILESIZE_INVALID_DIRECTORY) {
|
||||
BKE_reportf(data->reports,
|
||||
RPT_WARNING,
|
||||
"Could not open the directory '%s'",
|
||||
BLI_path_basename(data->searchdir));
|
||||
return false;
|
||||
}
|
||||
if (is_found == false) {
|
||||
BKE_reportf(data->reports,
|
||||
RPT_WARNING,
|
||||
"Could not find '%s' in '%s'",
|
||||
BLI_path_basename(path_src),
|
||||
data->searchdir);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Keep the path relative if the previous one was relative. */
|
||||
if (BLI_path_is_rel(path_dst)) {
|
||||
BLI_path_rel(filepath_new, data->basedir);
|
||||
}
|
||||
BLI_strncpy(path_dst, filepath_new, path_dst_maxncpy);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BKE_bpath_missing_files_find(Main *bmain,
|
||||
const char *searchpath,
|
||||
ReportList *reports,
|
||||
const bool find_all)
|
||||
{
|
||||
BPathFind_Data data = {nullptr};
|
||||
const int flag = BKE_BPATH_FOREACH_PATH_ABSOLUTE | BKE_BPATH_FOREACH_PATH_RELOAD_EDITED |
|
||||
BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN | BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES;
|
||||
|
||||
data.basedir = BKE_main_blendfile_path(bmain);
|
||||
data.reports = reports;
|
||||
data.searchdir = searchpath;
|
||||
data.find_all = find_all;
|
||||
|
||||
BPathForeachPathData path_data{};
|
||||
path_data.bmain = bmain;
|
||||
path_data.callback_function = missing_files_find_foreach_path_cb;
|
||||
path_data.flag = eBPathForeachFlag(flag);
|
||||
path_data.user_data = &data;
|
||||
BKE_bpath_foreach_path_main(&path_data);
|
||||
}
|
||||
|
||||
#undef MAX_DIR_RECURSE
|
||||
#undef FILESIZE_INVALID_DIRECTORY
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Rebase Relative Paths
|
||||
* \{ */
|
||||
|
||||
struct BPathRebase_Data {
|
||||
const char *basedir_src;
|
||||
const char *basedir_dst;
|
||||
ReportList *reports;
|
||||
|
||||
BPathSummary summary;
|
||||
};
|
||||
|
||||
static bool relative_rebase_foreach_path_cb(BPathForeachPathData *bpath_data,
|
||||
char *path_dst,
|
||||
size_t path_dst_maxncpy,
|
||||
const char *path_src)
|
||||
{
|
||||
BPathRebase_Data *data = static_cast<BPathRebase_Data *>(bpath_data->user_data);
|
||||
|
||||
data->summary.count_total++;
|
||||
|
||||
if (!BLI_path_is_rel(path_src)) {
|
||||
/* Absolute, leave this as-is. */
|
||||
return false;
|
||||
}
|
||||
|
||||
char filepath[(FILE_MAXDIR * 2) + FILE_MAXFILE];
|
||||
BLI_strncpy(filepath, path_src, FILE_MAX);
|
||||
if (!BLI_path_abs(filepath, data->basedir_src)) {
|
||||
BKE_reportf(data->reports, RPT_WARNING, "Path '%s' cannot be made absolute", path_src);
|
||||
data->summary.count_failed++;
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_path_normalize(filepath);
|
||||
|
||||
/* This may fail, if so it's fine to leave absolute since the path is still valid. */
|
||||
BLI_path_rel(filepath, data->basedir_dst);
|
||||
|
||||
BLI_strncpy(path_dst, filepath, path_dst_maxncpy);
|
||||
data->summary.count_changed++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BKE_bpath_relative_rebase(Main *bmain,
|
||||
const char *basedir_src,
|
||||
const char *basedir_dst,
|
||||
ReportList *reports,
|
||||
BPathSummary *r_summary)
|
||||
{
|
||||
BPathRebase_Data data = {nullptr};
|
||||
const int flag = (BKE_BPATH_FOREACH_PATH_SKIP_LINKED | BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE);
|
||||
|
||||
BLI_assert(basedir_src[0] != '\0');
|
||||
BLI_assert(basedir_dst[0] != '\0');
|
||||
|
||||
data.basedir_src = basedir_src;
|
||||
data.basedir_dst = basedir_dst;
|
||||
data.reports = reports;
|
||||
|
||||
BPathForeachPathData path_data{};
|
||||
path_data.bmain = bmain;
|
||||
path_data.callback_function = relative_rebase_foreach_path_cb;
|
||||
path_data.flag = eBPathForeachFlag(flag);
|
||||
path_data.user_data = &data;
|
||||
BKE_bpath_foreach_path_main(&path_data);
|
||||
|
||||
if (r_summary) {
|
||||
*r_summary = data.summary;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Make Paths Relative Or Absolute
|
||||
* \{ */
|
||||
|
||||
struct BPathRemap_Data {
|
||||
const char *basedir;
|
||||
ReportList *reports;
|
||||
|
||||
BPathSummary summary;
|
||||
};
|
||||
|
||||
static bool relative_convert_foreach_path_cb(BPathForeachPathData *bpath_data,
|
||||
char *path_dst,
|
||||
size_t path_dst_maxncpy,
|
||||
const char *path_src)
|
||||
{
|
||||
BPathRemap_Data *data = static_cast<BPathRemap_Data *>(bpath_data->user_data);
|
||||
|
||||
data->summary.count_total++;
|
||||
|
||||
if (BLI_path_is_rel(path_src)) {
|
||||
return false; /* Already relative. */
|
||||
}
|
||||
|
||||
char path_test[FILE_MAX];
|
||||
STRNCPY(path_test, path_src);
|
||||
|
||||
BLI_path_rel(path_test, data->basedir);
|
||||
if (!BLI_path_is_rel(path_test)) {
|
||||
const char *type_name = BKE_idtype_get_info_from_id(bpath_data->owner_id)->name;
|
||||
const char *id_name = bpath_data->owner_id->name + 2;
|
||||
BKE_reportf(data->reports,
|
||||
RPT_WARNING,
|
||||
"Path '%s' cannot be made relative for %s '%s'",
|
||||
path_src,
|
||||
type_name,
|
||||
id_name);
|
||||
data->summary.count_failed++;
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_strncpy(path_dst, path_test, path_dst_maxncpy);
|
||||
data->summary.count_changed++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool absolute_convert_foreach_path_cb(BPathForeachPathData *bpath_data,
|
||||
char *path_dst,
|
||||
size_t path_dst_maxncpy,
|
||||
const char *path_src)
|
||||
{
|
||||
BPathRemap_Data *data = static_cast<BPathRemap_Data *>(bpath_data->user_data);
|
||||
|
||||
data->summary.count_total++;
|
||||
|
||||
if (!BLI_path_is_rel(path_src)) {
|
||||
return false; /* Already absolute. */
|
||||
}
|
||||
|
||||
char path_test[FILE_MAX];
|
||||
STRNCPY(path_test, path_src);
|
||||
BLI_path_abs(path_test, data->basedir);
|
||||
if (BLI_path_is_rel(path_test)) {
|
||||
const char *type_name = BKE_idtype_get_info_from_id(bpath_data->owner_id)->name;
|
||||
const char *id_name = bpath_data->owner_id->name + 2;
|
||||
BKE_reportf(data->reports,
|
||||
RPT_WARNING,
|
||||
"Path '%s' cannot be made absolute for %s '%s'",
|
||||
path_src,
|
||||
type_name,
|
||||
id_name);
|
||||
data->summary.count_failed++;
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_strncpy(path_dst, path_test, path_dst_maxncpy);
|
||||
data->summary.count_changed++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bpath_absolute_relative_convert(Main *bmain,
|
||||
const char *basedir,
|
||||
ReportList *reports,
|
||||
BPathForeachPathFunctionCallback callback_function,
|
||||
BPathSummary *r_summary)
|
||||
{
|
||||
BPathRemap_Data data = {nullptr};
|
||||
const int flag = BKE_BPATH_FOREACH_PATH_SKIP_LINKED;
|
||||
|
||||
BLI_assert(basedir[0] != '\0');
|
||||
if (basedir[0] == '\0') {
|
||||
CLOG_ERROR(&LOG, "basedir='', this is a bug");
|
||||
return;
|
||||
}
|
||||
|
||||
data.basedir = basedir;
|
||||
data.reports = reports;
|
||||
|
||||
BPathForeachPathData path_data{};
|
||||
path_data.bmain = bmain;
|
||||
path_data.callback_function = callback_function;
|
||||
path_data.flag = eBPathForeachFlag(flag);
|
||||
path_data.user_data = &data;
|
||||
BKE_bpath_foreach_path_main(&path_data);
|
||||
|
||||
if (r_summary) {
|
||||
*r_summary = data.summary;
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_bpath_relative_convert(Main *bmain,
|
||||
const char *basedir,
|
||||
ReportList *reports,
|
||||
BPathSummary *r_summary)
|
||||
{
|
||||
bpath_absolute_relative_convert(
|
||||
bmain, basedir, reports, relative_convert_foreach_path_cb, r_summary);
|
||||
}
|
||||
|
||||
void BKE_bpath_absolute_convert(Main *bmain,
|
||||
const char *basedir,
|
||||
ReportList *reports,
|
||||
BPathSummary *r_summary)
|
||||
{
|
||||
bpath_absolute_relative_convert(
|
||||
bmain, basedir, reports, absolute_convert_foreach_path_cb, r_summary);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Backup/Restore/Free paths list functions.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct PathStore {
|
||||
PathStore *next, *prev;
|
||||
/** Over allocate. */
|
||||
char filepath[0];
|
||||
};
|
||||
|
||||
static bool bpath_list_append(BPathForeachPathData *bpath_data,
|
||||
char * /*path_dst*/,
|
||||
size_t /*path_dst_maxncpy*/,
|
||||
const char *path_src)
|
||||
{
|
||||
ListBaseT<PathStore> *path_list = static_cast<ListBaseT<PathStore> *>(bpath_data->user_data);
|
||||
size_t path_size = strlen(path_src) + 1;
|
||||
|
||||
/* NOTE: the PathStore and its string are allocated together in a single alloc. */
|
||||
PathStore *path_store = static_cast<PathStore *>(
|
||||
MEM_new_uninitialized(sizeof(PathStore) + path_size, __func__));
|
||||
|
||||
char *filepath = path_store->filepath;
|
||||
|
||||
memcpy(filepath, path_src, path_size);
|
||||
BLI_addtail(path_list, path_store);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool bpath_list_restore(BPathForeachPathData *bpath_data,
|
||||
char *path_dst,
|
||||
size_t path_dst_maxncpy,
|
||||
const char *path_src)
|
||||
{
|
||||
ListBaseT<PathStore> *path_list = static_cast<ListBaseT<PathStore> *>(bpath_data->user_data);
|
||||
|
||||
/* `ls->first` should never be nullptr, because the number of paths should not change.
|
||||
* If this happens, there is a bug in caller code. */
|
||||
BLI_assert(!path_list->is_empty());
|
||||
|
||||
PathStore *path_store = static_cast<PathStore *>(path_list->first);
|
||||
const char *filepath = path_store->filepath;
|
||||
bool is_path_changed = false;
|
||||
|
||||
if (!STREQ(path_src, filepath)) {
|
||||
BLI_strncpy(path_dst, filepath, path_dst_maxncpy);
|
||||
is_path_changed = true;
|
||||
}
|
||||
|
||||
BLI_freelinkN(path_list, path_store);
|
||||
return is_path_changed;
|
||||
}
|
||||
|
||||
void *BKE_bpath_list_backup(Main *bmain, const eBPathForeachFlag flag)
|
||||
{
|
||||
ListBaseT<PathStore> *path_list = MEM_new_zeroed<ListBaseT<PathStore>>(__func__);
|
||||
|
||||
BPathForeachPathData path_data{};
|
||||
path_data.bmain = bmain;
|
||||
path_data.callback_function = bpath_list_append;
|
||||
path_data.flag = flag;
|
||||
path_data.user_data = path_list;
|
||||
BKE_bpath_foreach_path_main(&path_data);
|
||||
|
||||
return path_list;
|
||||
}
|
||||
|
||||
void BKE_bpath_list_restore(Main *bmain, const eBPathForeachFlag flag, void *path_list_handle)
|
||||
{
|
||||
ListBaseT<PathStore> *path_list = static_cast<ListBaseT<PathStore> *>(path_list_handle);
|
||||
|
||||
BPathForeachPathData path_data{};
|
||||
path_data.bmain = bmain;
|
||||
path_data.callback_function = bpath_list_restore;
|
||||
path_data.flag = flag;
|
||||
path_data.user_data = path_list;
|
||||
BKE_bpath_foreach_path_main(&path_data);
|
||||
}
|
||||
|
||||
void BKE_bpath_list_free(void *path_list_handle)
|
||||
{
|
||||
ListBaseT<PathStore> *path_list = static_cast<ListBaseT<PathStore> *>(path_list_handle);
|
||||
/* The whole list should have been consumed by #BKE_bpath_list_restore, see also comment in
|
||||
* #bpath_list_restore. */
|
||||
BLI_assert(path_list->is_empty());
|
||||
|
||||
path_list->free_no_destruct();
|
||||
MEM_delete(path_list);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user