17#include "RNA_prototypes.hh"
26 if (this->strings_.contains(name)) {
29 if (this->integers_.contains(name)) {
32 if (this->floats_.contains(name)) {
40 if (this->strings_.remove(name)) {
43 if (this->integers_.remove(name)) {
46 if (this->floats_.remove(name)) {
57 this->strings_.add_new(name, value);
66 this->integers_.add_new(name, value);
75 this->floats_.add_new(name, value);
81 const std::string *value = this->strings_.lookup_ptr(name);
82 if (value ==
nullptr) {
90 const int64_t *value = this->integers_.lookup_ptr(name);
91 if (value ==
nullptr) {
99 const double *value = this->floats_.lookup_ptr(name);
100 if (value ==
nullptr) {
139 if (
ptr ==
nullptr || prop ==
nullptr ||
149 "Should never have `PROP_VARIABLES_NONE` for a path that supports path templates.");
157 render_data = &
reinterpret_cast<const Scene *
>(
ptr->owner_id)->r;
161 render_data = scene ? &scene->
r :
nullptr;
184 if (file_name[0] ==
'\0') {
189 else if (file_name_end == file_name) {
221#define FORMAT_BUFFER_SIZE 128
225enum class FormatSpecifierType {
244struct FormatSpecifier {
245 FormatSpecifierType type = FormatSpecifierType::NONE;
249 std::optional<uint8_t> integer_digit_count;
250 std::optional<uint8_t> fractional_digit_count;
253enum class TokenType {
264 VARIABLE_SYNTAX_ERROR,
267 UNESCAPED_CURLY_BRACE_ERROR,
274 TokenType type = TokenType::VARIABLE_EXPRESSION;
309 r_output_string[0] =
'\0';
310 int output_length = 0;
313 case FormatSpecifierType::NONE: {
316 r_output_string[output_length] =
'\0';
320 case FormatSpecifierType::INTEGER: {
323 output_length = fmt::format_to_n(r_output_string,
327 *
format.integer_digit_count)
329 r_output_string[output_length] =
'\0';
333 case FormatSpecifierType::FLOAT: {
341 if (
format.integer_digit_count.has_value()) {
343 output_length = fmt::format_to_n(r_output_string,
347 *
format.integer_digit_count)
355 r_output_string[output_length] =
'.';
359 r_output_string[output_length] =
'0';
363 r_output_string[output_length] =
'\0';
368 case FormatSpecifierType::SYNTAX_ERROR: {
371 "Format specifiers with invalid syntax should have been rejected before getting here.");
376 return output_length;
388 const double float_value,
393 r_output_string[0] =
'\0';
394 int output_length = 0;
397 case FormatSpecifierType::NONE: {
404 r_output_string[output_length] =
'\0';
411 r_output_string[output_length] =
'.';
412 r_output_string[output_length + 1] =
'0';
413 r_output_string[output_length + 2] =
'\0';
419 case FormatSpecifierType::INTEGER: {
424 case FormatSpecifierType::FLOAT: {
428 if (
format.integer_digit_count.has_value()) {
431 output_length = fmt::format_to_n(r_output_string,
435 *
format.integer_digit_count +
436 *
format.fractional_digit_count + 1,
437 *
format.fractional_digit_count)
439 r_output_string[output_length] =
'\0';
443 output_length = fmt::format_to_n(r_output_string,
447 *
format.fractional_digit_count)
449 r_output_string[output_length] =
'\0';
455 case FormatSpecifierType::SYNTAX_ERROR: {
458 "Format specifiers with invalid syntax should have been rejected before getting here.");
463 return output_length;
475 FormatSpecifier
format = {};
479 format.type = FormatSpecifierType::SYNTAX_ERROR;
485 format.integer_digit_count = format_specifier.
size();
487 format.type = FormatSpecifierType::INTEGER;
494 const bool found_dot = dot_index != std::string::npos;
495 const bool only_one_dot = dot_index == dot_index_last;
496 if (format_specifier.
find_first_not_of(
".#") == std::string::npos && found_dot && only_one_dot) {
503 format.type = FormatSpecifierType::SYNTAX_ERROR;
507 if (!
left.is_empty()) {
513 format.type = FormatSpecifierType::FLOAT;
517 format.type = FormatSpecifierType::SYNTAX_ERROR;
540 int format_specifier_split = -1;
543 for (
int byte_index = from_char; byte_index < path.
size(); byte_index++) {
545 if (start == -1 && (byte_index + 1) < path.
size() && path[byte_index] ==
'{' &&
546 path[byte_index + 1] ==
'{')
549 token.type = TokenType::LEFT_CURLY_BRACE;
559 if (start == -1 && (byte_index + 1) < path.
size() && path[byte_index] ==
'}' &&
560 path[byte_index + 1] ==
'}')
562 token.type = TokenType::RIGHT_CURLY_BRACE;
569 if (start == -1 && path[byte_index] ==
'}') {
570 token.type = TokenType::UNESCAPED_CURLY_BRACE_ERROR;
576 if (path[byte_index] ==
'{') {
579 token.type = TokenType::VARIABLE_SYNTAX_ERROR;
584 format_specifier_split = -1;
595 if (path[byte_index] ==
':') {
596 if (format_specifier_split == -1) {
600 format_specifier_split = byte_index;
606 if (path[byte_index] ==
'}') {
607 end = byte_index + 1;
619 token.type = TokenType::VARIABLE_SYNTAX_ERROR;
626 if (format_specifier_split == -1) {
628 token.variable_name = path.
substr(start + 1, (end - 1) - (start + 1));
632 token.variable_name = path.
substr(start + 1, format_specifier_split - (start + 1));
634 path.
substr(format_specifier_split + 1, (end - 1) - (format_specifier_split + 1)));
635 if (token.format.type == FormatSpecifierType::SYNTAX_ERROR) {
636 token.type = TokenType::VARIABLE_SYNTAX_ERROR;
650 for (
int bytes_read = 0; bytes_read < path.
size();) {
651 const std::optional<Token> token =
next_token(path, bytes_read);
653 if (!token.has_value()) {
657 bytes_read = token->byte_range.one_after_last();
668 switch (token.type) {
669 case TokenType::VARIABLE_SYNTAX_ERROR: {
670 if (token.format.type == FormatSpecifierType::SYNTAX_ERROR) {
678 case TokenType::UNESCAPED_CURLY_BRACE_ERROR: {
683 case TokenType::VARIABLE_EXPRESSION:
684 case TokenType::LEFT_CURLY_BRACE:
685 case TokenType::RIGHT_CURLY_BRACE:
719 const int out_path_max_length,
742 for (
const Token &token : tokens) {
751 switch (token.type) {
753 case TokenType::VARIABLE_SYNTAX_ERROR:
754 case TokenType::UNESCAPED_CURLY_BRACE_ERROR: {
760 case TokenType::LEFT_CURLY_BRACE: {
761 strcpy(replacement_string,
"{");
764 case TokenType::RIGHT_CURLY_BRACE: {
765 strcpy(replacement_string,
"}");
770 case TokenType::VARIABLE_EXPRESSION: {
771 if (std::optional<blender::StringRefNull> string_value = template_variables.
get_string(
772 token.variable_name))
776 if (token.format.type != FormatSpecifierType::NONE) {
781 BLI_strncpy(replacement_string, string_value->c_str(),
sizeof(replacement_string));
785 if (std::optional<int64_t> integer_value = template_variables.
get_integer(
786 token.variable_name))
793 if (std::optional<double> float_value = template_variables.
get_float(token.variable_name))
809 if (token.byte_range.start() + length_diff >= out_path_max_length) {
815 token.byte_range.start() + length_diff,
816 token.byte_range.one_after_last() + length_diff,
819 length_diff -= token.byte_range.size();
820 length_diff += strlen(replacement_string);
842 path_buffer.
data(), path_buffer.
size(), path, template_variables);
855 switch (
error.type) {
857 return std::string(
"Unescaped curly brace '") + subpath +
"'.";
861 return std::string(
"Invalid or incomplete template expression '") + subpath +
"'.";
865 return std::string(
"Invalid format specifier in template expression '") + subpath +
"'.";
869 return std::string(
"Unknown variable referenced in template expression '") + subpath +
"'.";
874 return "Unknown error.";
884 std::string error_message =
"Parse errors in path '" + path +
"':";
896 if (
format.type == FormatSpecifierType::SYNTAX_ERROR) {
908 if (
format.type == FormatSpecifierType::SYNTAX_ERROR) {
Scene * CTX_data_scene(const bContext *C)
const char * BKE_main_blendfile_path_from_global()
Functions and classes for applying templates with variable expressions to 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)
blender::bke::path_templates::VariableMap BKE_build_template_variables_for_render_path(const RenderData *render_data)
blender::Vector< blender::bke::path_templates::Error > BKE_path_validate_template(blender::StringRef path, const blender::bke::path_templates::VariableMap &template_variables)
bool BKE_path_contains_template_syntax(blender::StringRef path)
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_unreachable()
#define BLI_assert_msg(a, msg)
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
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)
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
constexpr int64_t find_last_of(StringRef chars, int64_t pos=INT64_MAX) 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
void append(const T &value)
std::optional< blender::StringRefNull > get_string(blender::StringRef name) const
std::optional< int64_t > get_integer(blender::StringRef name) const
bool add_string(blender::StringRef name, blender::StringRef value)
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 blender::Vector< Error > eval_template(char *out_path, const int out_path_max_length, blender::StringRef in_path, const VariableMap &template_variables)
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 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)
blender::Vector< Error > BKE_path_apply_template(char *path, int path_max_length, const VariableMap &template_variables)
static std::optional< Error > token_to_syntax_error(const Token &token)
PropertyPathTemplateType RNA_property_path_template_type(PropertyRNA *prop)
int RNA_property_flag(PropertyRNA *prop)
blender::IndexRange byte_range