20#include "RNA_prototypes.hh"
29 if (this->strings_.contains(
name)) {
32 if (this->filepaths_.contains(
name)) {
35 if (this->integers_.contains(
name)) {
38 if (this->floats_.contains(
name)) {
46 if (this->strings_.remove(
name)) {
49 if (this->filepaths_.remove(
name)) {
52 if (this->integers_.remove(
name)) {
55 if (this->floats_.remove(
name)) {
66 this->strings_.add_new(
name, value);
75 this->filepaths_.add_new(
name, value);
84 this->integers_.add_new(
name, value);
93 this->floats_.add_new(
name, value);
99 const std::string *value = this->strings_.lookup_ptr(
name);
100 if (value ==
nullptr) {
108 const std::string *value = this->filepaths_.lookup_ptr(
name);
109 if (value ==
nullptr) {
117 const int64_t *value = this->integers_.lookup_ptr(
name);
118 if (value ==
nullptr) {
126 const double *value = this->floats_.lookup_ptr(
name);
127 if (value ==
nullptr) {
140 if (file_name[0] ==
'\0') {
144 else if (file_name_end == file_name) {
173 if (!success || dir_path[0] ==
'\0') {
227 if (
ptr ==
nullptr || prop ==
nullptr ||
249 scene =
reinterpret_cast<const Scene *
>(
ptr->owner_id);
266 const bNode *bnode =
reinterpret_cast<const bNode *
>(node_rna_ptr->data);
331 const bNode &owning_node)
338#define FORMAT_BUFFER_SIZE 512
342enum class FormatSpecifierType {
361struct FormatSpecifier {
362 FormatSpecifierType type = FormatSpecifierType::NONE;
366 std::optional<uint8_t> integer_digit_count;
367 std::optional<uint8_t> fractional_digit_count;
370enum class TokenType {
381 VARIABLE_SYNTAX_ERROR,
384 UNESCAPED_CURLY_BRACE_ERROR,
391 TokenType type = TokenType::VARIABLE_EXPRESSION;
426 r_output_string[0] =
'\0';
427 int output_length = 0;
430 case FormatSpecifierType::NONE: {
433 r_output_string[output_length] =
'\0';
437 case FormatSpecifierType::INTEGER: {
440 output_length = fmt::format_to_n(r_output_string,
444 *
format.integer_digit_count)
446 r_output_string[output_length] =
'\0';
450 case FormatSpecifierType::FLOAT: {
458 if (
format.integer_digit_count.has_value()) {
460 output_length = fmt::format_to_n(r_output_string,
464 *
format.integer_digit_count)
472 r_output_string[output_length] =
'.';
476 r_output_string[output_length] =
'0';
480 r_output_string[output_length] =
'\0';
485 case FormatSpecifierType::SYNTAX_ERROR: {
488 "Format specifiers with invalid syntax should have been rejected before getting here.");
493 return output_length;
505 const double float_value,
510 r_output_string[0] =
'\0';
511 int output_length = 0;
514 case FormatSpecifierType::NONE: {
521 r_output_string[output_length] =
'\0';
528 r_output_string[output_length] =
'.';
529 r_output_string[output_length + 1] =
'0';
530 r_output_string[output_length + 2] =
'\0';
536 case FormatSpecifierType::INTEGER: {
541 case FormatSpecifierType::FLOAT: {
545 if (
format.integer_digit_count.has_value()) {
548 output_length = fmt::format_to_n(r_output_string,
552 *
format.integer_digit_count +
553 *
format.fractional_digit_count + 1,
554 *
format.fractional_digit_count)
556 r_output_string[output_length] =
'\0';
560 output_length = fmt::format_to_n(r_output_string,
564 *
format.fractional_digit_count)
566 r_output_string[output_length] =
'\0';
572 case FormatSpecifierType::SYNTAX_ERROR: {
575 "Format specifiers with invalid syntax should have been rejected before getting here.");
580 return output_length;
592 FormatSpecifier
format = {};
596 format.type = FormatSpecifierType::SYNTAX_ERROR;
602 format.integer_digit_count = format_specifier.
size();
604 format.type = FormatSpecifierType::INTEGER;
611 const bool found_dot = dot_index != std::string::npos;
612 const bool only_one_dot = dot_index == dot_index_last;
613 if (format_specifier.
find_first_not_of(
".#") == std::string::npos && found_dot && only_one_dot) {
620 format.type = FormatSpecifierType::SYNTAX_ERROR;
624 if (!
left.is_empty()) {
630 format.type = FormatSpecifierType::FLOAT;
634 format.type = FormatSpecifierType::SYNTAX_ERROR;
656 int format_specifier_split = -1;
659 for (
int byte_index = from_char; byte_index < path.
size(); byte_index++) {
661 if (start == -1 && (byte_index + 1) < path.
size() && path[byte_index] ==
'{' &&
662 path[byte_index + 1] ==
'{')
665 token.type = TokenType::LEFT_CURLY_BRACE;
675 if (start == -1 && (byte_index + 1) < path.
size() && path[byte_index] ==
'}' &&
676 path[byte_index + 1] ==
'}')
678 token.type = TokenType::RIGHT_CURLY_BRACE;
685 if (start == -1 && path[byte_index] ==
'}') {
686 token.type = TokenType::UNESCAPED_CURLY_BRACE_ERROR;
692 if (path[byte_index] ==
'{') {
695 token.type = TokenType::VARIABLE_SYNTAX_ERROR;
700 format_specifier_split = -1;
711 if (path[byte_index] ==
':') {
712 if (format_specifier_split == -1) {
716 format_specifier_split = byte_index;
722 if (path[byte_index] ==
'}') {
723 end = byte_index + 1;
735 token.type = TokenType::VARIABLE_SYNTAX_ERROR;
742 if (format_specifier_split == -1) {
744 token.variable_name = path.
substr(start + 1, (end - 1) - (start + 1));
748 token.variable_name = path.
substr(start + 1, format_specifier_split - (start + 1));
750 path.
substr(format_specifier_split + 1, (end - 1) - (format_specifier_split + 1)));
751 if (token.format.type == FormatSpecifierType::SYNTAX_ERROR) {
752 token.type = TokenType::VARIABLE_SYNTAX_ERROR;
766 for (
int bytes_read = 0; bytes_read < path.
size();) {
767 const std::optional<Token> token =
next_token(path, bytes_read);
769 if (!token.has_value()) {
773 bytes_read = token->byte_range.one_after_last();
784 switch (token.type) {
785 case TokenType::VARIABLE_SYNTAX_ERROR: {
786 if (token.format.type == FormatSpecifierType::SYNTAX_ERROR) {
794 case TokenType::UNESCAPED_CURLY_BRACE_ERROR: {
799 case TokenType::VARIABLE_EXPRESSION:
800 case TokenType::LEFT_CURLY_BRACE:
801 case TokenType::RIGHT_CURLY_BRACE:
835 const int out_path_maxncpy,
858 for (
const Token &token : tokens) {
867 switch (token.type) {
869 case TokenType::VARIABLE_SYNTAX_ERROR:
870 case TokenType::UNESCAPED_CURLY_BRACE_ERROR: {
876 case TokenType::LEFT_CURLY_BRACE: {
877 strcpy(replacement_string,
"{");
880 case TokenType::RIGHT_CURLY_BRACE: {
881 strcpy(replacement_string,
"}");
886 case TokenType::VARIABLE_EXPRESSION: {
887 if (std::optional<blender::StringRefNull> string_value = template_variables.
get_string(
888 token.variable_name))
890 if (token.format.type != FormatSpecifierType::NONE) {
895 STRNCPY(replacement_string, string_value->c_str());
900 if (std::optional<blender::StringRefNull> path_value = template_variables.
get_filepath(
901 token.variable_name))
903 if (token.format.type != FormatSpecifierType::NONE) {
908 STRNCPY(replacement_string, path_value->c_str());
912 if (std::optional<int64_t> integer_value = template_variables.
get_integer(
913 token.variable_name))
920 if (std::optional<double> float_value = template_variables.
get_float(token.variable_name))
936 if (token.byte_range.start() + length_diff >= out_path_maxncpy) {
942 token.byte_range.start() + length_diff,
943 token.byte_range.one_after_last() + length_diff,
946 length_diff -= token.byte_range.size();
947 length_diff += strlen(replacement_string);
969 path_buffer.
data(), path_buffer.
size(), path, template_variables);
982 switch (
error.type) {
984 return std::string(
"Unescaped curly brace '") + subpath +
"'.";
988 return std::string(
"Invalid or incomplete template expression '") + subpath +
"'.";
992 return std::string(
"Invalid format specifier in template expression '") + subpath +
"'.";
996 return std::string(
"Unknown variable referenced in template expression '") + subpath +
"'.";
1001 return "Unknown error.";
1011 std::string error_message =
"Parse errors in path '" + path +
"':";
1016 BKE_report(reports, report_type, error_message.c_str());
1023 if (
format.type == FormatSpecifierType::SYNTAX_ERROR) {
1024 return std::nullopt;
1035 if (
format.type == FormatSpecifierType::SYNTAX_ERROR) {
1036 return std::nullopt;
Scene * CTX_data_scene(const bContext *C)
const char * BKE_main_blendfile_path_from_global()
Functions and classes for evaluating template expressions in filepaths.
void BKE_report_path_template_errors(ReportList *reports, eReportType report_type, blender::StringRef path, blender::Span< blender::bke::path_templates::Error > errors)
std::optional< blender::bke::path_templates::VariableMap > BKE_build_template_variables_for_prop(const bContext *C, PointerRNA *ptr, PropertyRNA *prop)
std::optional< std::string > BKE_path_template_format_int(blender::StringRef format_specifier, int64_t value)
std::optional< std::string > BKE_path_template_format_float(blender::StringRef format_specifier, double value)
std::string BKE_path_template_error_to_string(const blender::bke::path_templates::Error &error, blender::StringRef path)
void BKE_add_template_variables_for_render_path(blender::bke::path_templates::VariableMap &variables, const Scene &scene)
void BKE_add_template_variables_general(blender::bke::path_templates::VariableMap &variables, const ID *path_owner_id)
blender::Vector< blender::bke::path_templates::Error > BKE_path_validate_template(blender::StringRef path, const blender::bke::path_templates::VariableMap &template_variables)
void BKE_add_template_variables_for_node(blender::bke::path_templates::VariableMap &variables, const bNode &owning_node)
bool BKE_path_contains_template_syntax(blender::StringRef path)
blender::Vector< blender::bke::path_templates::Error > BKE_path_apply_template(char *path, int path_maxncpy, const blender::bke::path_templates::VariableMap &template_variables)
void BKE_report(ReportList *reports, eReportType type, const char *message)
void BKE_render_resolution(const RenderData *r, const bool use_crop, int *r_width, int *r_height)
#define BLI_assert_msg(a, msg)
bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
const char * BLI_path_extension_or_end(const char *filepath) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
bool BLI_path_make_safe_filename(char *filename) ATTR_NONNULL(1)
char * STRNCPY(char(&dst)[N], const char *src)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
size_t BLI_string_replace_range(char *string, size_t string_maxncpy, int src_beg, int src_end, const char *dst)
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
Enumerations for DNA_ID.h.
@ PROP_VARIABLES_RENDER_OUTPUT
@ PROP_PATH_SUPPORTS_TEMPLATES
static constexpr IndexRange from_begin_end(const int64_t begin, const int64_t end)
constexpr bool is_empty() const
void copy_unsafe(char *dst) const
constexpr int64_t find_last_of(StringRef chars, int64_t pos=INT64_MAX) const
constexpr const char * end() const
constexpr int64_t find_first_not_of(StringRef chars, int64_t pos=0) const
constexpr bool is_empty() const
constexpr StringRef substr(int64_t start, int64_t size) const
void copy_bytes_truncated(char *dst, int64_t dst_size) const
constexpr int64_t find_first_of(StringRef chars, int64_t pos=0) const
constexpr int64_t size() const
constexpr const char * c_str() const
void append(const T &value)
bool add_path_up_to_file(blender::StringRef var_name, blender::StringRefNull full_path, blender::StringRef fallback)
std::optional< blender::StringRefNull > get_string(blender::StringRef name) const
bool add_filepath(blender::StringRef name, blender::StringRef value)
bool add_filename_only(blender::StringRef var_name, blender::StringRefNull full_path, blender::StringRef fallback)
std::optional< int64_t > get_integer(blender::StringRef name) const
bool add_string(blender::StringRef name, blender::StringRef value)
std::optional< blender::StringRefNull > get_filepath(blender::StringRef name) const
bool contains(blender::StringRef name) const
bool add_float(blender::StringRef name, double value)
bool remove(blender::StringRef name)
bool add_integer(blender::StringRef name, int64_t value)
std::optional< double > get_float(blender::StringRef name) const
static void error(const char *str)
bool operator==(const Error &left, const Error &right)
static int format_float_to_string(const FormatSpecifier &format, const double float_value, char r_output_string[FORMAT_BUFFER_SIZE])
#define FORMAT_BUFFER_SIZE
static blender::Vector< Error > eval_template(char *out_path, const int out_path_maxncpy, blender::StringRef in_path, const VariableMap &template_variables)
static int format_int_to_string(const FormatSpecifier &format, const int64_t integer_value, char r_output_string[FORMAT_BUFFER_SIZE])
static blender::Vector< Token > parse_template(blender::StringRef path)
static std::optional< Token > next_token(blender::StringRef path, const int from_char)
static FormatSpecifier parse_format_specifier(blender::StringRef format_specifier)
static std::optional< Error > token_to_syntax_error(const Token &token)
std::optional< AncestorPointerRNA > RNA_struct_search_closest_ancestor_by_type(PointerRNA *ptr, const StructRNA *srna)
PropertyPathTemplateType RNA_property_path_template_type(PropertyRNA *prop)
int RNA_property_flag(PropertyRNA *prop)
blender::IndexRange byte_range