Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Eisel <julian@blender.org>2022-07-29 17:56:48 +0300
committerJulian Eisel <julian@blender.org>2022-08-04 17:13:00 +0300
commit585dd63c6ef1d983558aac41e7e86a32a8649133 (patch)
treea5216dfa8965127772e52dec63f5cef4f250e841 /source/blender/makesrna
parent27d31c15e88914e87a0a881072a5a9ee0c99eb48 (diff)
Cleanup: Move RNA path functions into own C++ file
NOTE: This is committed to the 3.3 branch as part of D15606, which we decided should go to this release still (by Bastien, Dalai and me). That is because these are important usability fixes/improvements to have for the LTS release. Adds `rna_path.cc` and `RNA_path.h`. `rna_access.c` is a quite big file, which makes it rather hard and inconvenient to navigate. RNA path functions form a nicely coherent unit that can stand well on it's own, so it makes sense to split them off to mitigate the problem. Moreover, I was looking into refactoring the quite convoluted/overloaded `rna_path_parse()`, and found that some C++ features may help greatly with that. So having that code compile in C++ would be helpful to attempt that. Differential Revision: https://developer.blender.org/D15540 Reviewed by: Brecht Van Lommel, Campbell Barton, Bastien Montagne
Diffstat (limited to 'source/blender/makesrna')
-rw-r--r--source/blender/makesrna/RNA_access.h241
-rw-r--r--source/blender/makesrna/RNA_path.h259
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesrna/intern/rna_access.c1336
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c1
-rw-r--r--source/blender/makesrna/intern/rna_access_internal.h10
-rw-r--r--source/blender/makesrna/intern/rna_color.c1
-rw-r--r--source/blender/makesrna/intern/rna_internal.h8
-rw-r--r--source/blender/makesrna/intern/rna_path.cc1360
9 files changed, 1644 insertions, 1574 deletions
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 005228bea72..de9fa60aa5d 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -475,247 +475,6 @@ bool RNA_property_copy(
bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index);
bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop);
-/* Path
- *
- * Experimental method to refer to structs and properties with a string,
- * using a syntax like: scenes[0].objects["Cube"].data.verts[7].co
- *
- * This provides a way to refer to RNA data while being detached from any
- * particular pointers, which is useful in a number of applications, like
- * UI code or Actions, though efficiency is a concern. */
-
-char *RNA_path_append(
- const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey);
-#if 0 /* UNUSED. */
-char *RNA_path_back(const char *path);
-#endif
-
-/**
- * Search for the start of the 'rna array index' part of the given `rna_path`.
- *
- * Given the root RNA pointer and resolved RNA property, and the RNA path, return the first
- * character in `rna_path` that is part of the array index for the given property. Return NULL if
- * none can be found, e.g. because the property is not an RNA array.
- *
- * \param array_prop: if not NULL, the #PropertyRNA assumed to be the last one from the RNA path.
- * Only used to ensure it is a valid array property.
- */
-const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop);
-
-/* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */
-
-/**
- * Resolve the given RNA Path to find the pointer and/or property
- * indicated by fully resolving the path.
- *
- * \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers,
- * e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr,
- * and a NULL \a r_prop...
- *
- * \note Assumes all pointers provided are valid
- * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
- */
-bool RNA_path_resolve(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop);
-
-/**
- * Resolve the given RNA Path to find the pointer and/or property + array index
- * indicated by fully resolving the path.
- *
- * \note Assumes all pointers provided are valid.
- * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
- */
-bool RNA_path_resolve_full(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index);
-/**
- * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data.
- *
- * \note While it's correct to ignore the value of #PointerRNA.data
- * most callers need to know if the resulting pointer was found and not null.
- */
-bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index);
-
-/* RNA_path_resolve_property() variants ensure that pointer + property both exist. */
-
-/**
- * Resolve the given RNA Path to find both the pointer AND property
- * indicated by fully resolving the path.
- *
- * This is a convenience method to avoid logic errors and ugly syntax.
- * \note Assumes all pointers provided are valid
- * \return True only if both a valid pointer and property are found after resolving the path
- */
-bool RNA_path_resolve_property(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop);
-
-/**
- * Resolve the given RNA Path to find the pointer AND property (as well as the array index)
- * indicated by fully resolving the path.
- *
- * This is a convenience method to avoid logic errors and ugly syntax.
- * \note Assumes all pointers provided are valid
- * \return True only if both a valid pointer and property are found after resolving the path
- */
-bool RNA_path_resolve_property_full(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index);
-
-/* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist,
- * and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */
-
-/**
- * Resolve the given RNA Path to find both the pointer AND property
- * indicated by fully resolving the path, and get the value of the Pointer property
- * (or item of the collection).
- *
- * This is a convenience method to avoid logic errors and ugly syntax,
- * it combines both \a RNA_path_resolve and #RNA_path_resolve_property in a single call.
- * \note Assumes all pointers provided are valid.
- * \param r_item_ptr: The final Pointer or Collection item value.
- * You must check for its validity before use!
- * \return True only if both a valid pointer and property are found after resolving the path
- */
-bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- PointerRNA *r_item_ptr);
-
-/**
- * Resolve the given RNA Path to find both the pointer AND property (as well as the array index)
- * indicated by fully resolving the path,
- * and get the value of the Pointer property (or item of the collection).
- *
- * This is a convenience method to avoid logic errors and ugly syntax,
- * it combines both \a RNA_path_resolve_full and
- * \a RNA_path_resolve_property_full in a single call.
- * \note Assumes all pointers provided are valid.
- * \param r_item_ptr: The final Pointer or Collection item value.
- * You must check for its validity before use!
- * \return True only if both a valid pointer and property are found after resolving the path
- */
-bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index,
- PointerRNA *r_item_ptr);
-
-typedef struct PropertyElemRNA PropertyElemRNA;
-struct PropertyElemRNA {
- PropertyElemRNA *next, *prev;
- PointerRNA ptr;
- PropertyRNA *prop;
- int index;
-};
-/**
- * Resolve the given RNA Path into a linked list of #PropertyElemRNA's.
- *
- * To be used when complex operations over path are needed, like e.g. get relative paths,
- * to avoid too much string operations.
- *
- * \return True if there was no error while resolving the path
- * \note Assumes all pointers provided are valid
- */
-bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBase *r_elements);
-
-/**
- * Find the path from the structure referenced by the pointer to the runtime RNA-defined
- * #IDProperty object.
- *
- * \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties).
- *
- * \param ptr: Reference to the object owning the custom property storage.
- * \param needle: Custom property object to find.
- * \return Relative path or NULL.
- */
-char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle);
-
-/**
- * Find the actual ID pointer and path from it to the given ID.
- *
- * \param id: ID reference to search the global owner for.
- * \param[out] r_path: Path from the real ID to the initial ID.
- * \return The ID pointer, or NULL in case of failure.
- */
-struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path);
-
-char *RNA_path_from_ID_to_struct(const PointerRNA *ptr);
-
-char *RNA_path_from_real_ID_to_struct(struct Main *bmain,
- const PointerRNA *ptr,
- struct ID **r_real);
-
-char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop);
-/**
- * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc.
- * \param index: The *flattened* index to use when \a `index_dim > 0`,
- * this is expanded when used with multi-dimensional arrays.
- */
-char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index);
-
-char *RNA_path_from_real_ID_to_property_index(struct Main *bmain,
- const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index,
- struct ID **r_real_id);
-
-/**
- * \return the path to given ptr/prop from the closest ancestor of given type,
- * if any (else return NULL).
- */
-char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr,
- PropertyRNA *prop,
- const struct StructRNA *type);
-
-/**
- * Get the ID as a python representation, eg:
- * bpy.data.foo["bar"]
- */
-char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id);
-/**
- * Get the ID.struct as a python representation, eg:
- * bpy.data.foo["bar"].some_struct
- */
-char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr);
-/**
- * Get the ID.struct.property as a python representation, eg:
- * bpy.data.foo["bar"].some_struct.some_prop[10]
- */
-char *RNA_path_full_property_py_ex(
- struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback);
-char *RNA_path_full_property_py(struct Main *bmain,
- const PointerRNA *ptr,
- PropertyRNA *prop,
- int index);
-/**
- * Get the struct.property as a python representation, eg:
- * some_struct.some_prop[10]
- */
-char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index);
-/**
- * Get the struct.property as a python representation, eg:
- * some_prop[10]
- */
-char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index);
-
/* Quick name based property access
*
* These are just an easier way to access property values without having to
diff --git a/source/blender/makesrna/RNA_path.h b/source/blender/makesrna/RNA_path.h
new file mode 100644
index 00000000000..7ab8c6fa313
--- /dev/null
+++ b/source/blender/makesrna/RNA_path.h
@@ -0,0 +1,259 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup RNA
+ *
+ * RNA paths are a way to refer to pointers and properties with a string,
+ * using a syntax like: scenes[0].objects["Cube"].data.verts[7].co
+ *
+ * This provides a way to refer to RNA data while being detached from any
+ * particular pointers, which is useful in a number of applications, like
+ * UI code or Actions, though efficiency is a concern.
+ */
+
+#include "RNA_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ListBase;
+struct IDProperty;
+
+char *RNA_path_append(
+ const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey);
+#if 0 /* UNUSED. */
+char *RNA_path_back(const char *path);
+#endif
+
+/**
+ * Search for the start of the 'rna array index' part of the given `rna_path`.
+ *
+ * Given the root RNA pointer and resolved RNA property, and the RNA path, return the first
+ * character in `rna_path` that is part of the array index for the given property. Return NULL if
+ * none can be found, e.g. because the property is not an RNA array.
+ *
+ * \param array_prop: if not NULL, the #PropertyRNA assumed to be the last one from the RNA path.
+ * Only used to ensure it is a valid array property.
+ */
+const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop);
+
+/* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */
+
+/**
+ * Resolve the given RNA Path to find the pointer and/or property
+ * indicated by fully resolving the path.
+ *
+ * \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers,
+ * e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr,
+ * and a NULL \a r_prop...
+ *
+ * \note Assumes all pointers provided are valid
+ * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
+ */
+bool RNA_path_resolve(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop);
+
+/**
+ * Resolve the given RNA Path to find the pointer and/or property + array index
+ * indicated by fully resolving the path.
+ *
+ * \note Assumes all pointers provided are valid.
+ * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
+ */
+bool RNA_path_resolve_full(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index);
+/**
+ * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data.
+ *
+ * \note While it's correct to ignore the value of #PointerRNA.data
+ * most callers need to know if the resulting pointer was found and not null.
+ */
+bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index);
+
+/* RNA_path_resolve_property() variants ensure that pointer + property both exist. */
+
+/**
+ * Resolve the given RNA Path to find both the pointer AND property
+ * indicated by fully resolving the path.
+ *
+ * This is a convenience method to avoid logic errors and ugly syntax.
+ * \note Assumes all pointers provided are valid
+ * \return True only if both a valid pointer and property are found after resolving the path
+ */
+bool RNA_path_resolve_property(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop);
+
+/**
+ * Resolve the given RNA Path to find the pointer AND property (as well as the array index)
+ * indicated by fully resolving the path.
+ *
+ * This is a convenience method to avoid logic errors and ugly syntax.
+ * \note Assumes all pointers provided are valid
+ * \return True only if both a valid pointer and property are found after resolving the path
+ */
+bool RNA_path_resolve_property_full(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index);
+
+/* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist,
+ * and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */
+
+/**
+ * Resolve the given RNA Path to find both the pointer AND property
+ * indicated by fully resolving the path, and get the value of the Pointer property
+ * (or item of the collection).
+ *
+ * This is a convenience method to avoid logic errors and ugly syntax,
+ * it combines both \a RNA_path_resolve and #RNA_path_resolve_property in a single call.
+ * \note Assumes all pointers provided are valid.
+ * \param r_item_ptr: The final Pointer or Collection item value.
+ * You must check for its validity before use!
+ * \return True only if both a valid pointer and property are found after resolving the path
+ */
+bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ PointerRNA *r_item_ptr);
+
+/**
+ * Resolve the given RNA Path to find both the pointer AND property (as well as the array index)
+ * indicated by fully resolving the path,
+ * and get the value of the Pointer property (or item of the collection).
+ *
+ * This is a convenience method to avoid logic errors and ugly syntax,
+ * it combines both \a RNA_path_resolve_full and
+ * \a RNA_path_resolve_property_full in a single call.
+ * \note Assumes all pointers provided are valid.
+ * \param r_item_ptr: The final Pointer or Collection item value.
+ * You must check for its validity before use!
+ * \return True only if both a valid pointer and property are found after resolving the path
+ */
+bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index,
+ PointerRNA *r_item_ptr);
+
+typedef struct PropertyElemRNA PropertyElemRNA;
+struct PropertyElemRNA {
+ PropertyElemRNA *next, *prev;
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index;
+};
+/**
+ * Resolve the given RNA Path into a linked list of #PropertyElemRNA's.
+ *
+ * To be used when complex operations over path are needed, like e.g. get relative paths,
+ * to avoid too much string operations.
+ *
+ * \return True if there was no error while resolving the path
+ * \note Assumes all pointers provided are valid
+ */
+bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBase *r_elements);
+
+/**
+ * Find the path from the structure referenced by the pointer to the runtime RNA-defined
+ * #IDProperty object.
+ *
+ * \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties).
+ *
+ * \param ptr: Reference to the object owning the custom property storage.
+ * \param needle: Custom property object to find.
+ * \return Relative path or NULL.
+ */
+char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle);
+
+/**
+ * Find the actual ID pointer and path from it to the given ID.
+ *
+ * \param id: ID reference to search the global owner for.
+ * \param[out] r_path: Path from the real ID to the initial ID.
+ * \return The ID pointer, or NULL in case of failure.
+ */
+struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path);
+
+char *RNA_path_from_ID_to_struct(const PointerRNA *ptr);
+
+char *RNA_path_from_real_ID_to_struct(struct Main *bmain,
+ const PointerRNA *ptr,
+ struct ID **r_real);
+
+char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop);
+/**
+ * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc.
+ * \param index: The *flattened* index to use when \a `index_dim > 0`,
+ * this is expanded when used with multi-dimensional arrays.
+ */
+char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index);
+
+char *RNA_path_from_real_ID_to_property_index(struct Main *bmain,
+ const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index,
+ struct ID **r_real_id);
+
+/**
+ * \return the path to given ptr/prop from the closest ancestor of given type,
+ * if any (else return NULL).
+ */
+char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ const struct StructRNA *type);
+
+/**
+ * Get the ID as a python representation, eg:
+ * bpy.data.foo["bar"]
+ */
+char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id);
+/**
+ * Get the ID.struct as a python representation, eg:
+ * bpy.data.foo["bar"].some_struct
+ */
+char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr);
+/**
+ * Get the ID.struct.property as a python representation, eg:
+ * bpy.data.foo["bar"].some_struct.some_prop[10]
+ */
+char *RNA_path_full_property_py_ex(
+ struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback);
+char *RNA_path_full_property_py(struct Main *bmain,
+ const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index);
+/**
+ * Get the struct.property as a python representation, eg:
+ * some_struct.some_prop[10]
+ */
+char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index);
+/**
+ * Get the struct.property as a python representation, eg:
+ * some_prop[10]
+ */
+char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index aaadd36341f..8124804de2b 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -159,6 +159,7 @@ set(SRC_RNA_INC
../RNA_documentation.h
../RNA_enum_items.h
../RNA_enum_types.h
+ ../RNA_path.h
../RNA_types.h
)
@@ -416,6 +417,7 @@ add_custom_command(
set(SRC
rna_access.c
rna_access_compare_override.c
+ rna_path.cc
${GENSRC}
${SRC_RNA_INC}
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index cf4182243b9..04707c01d6b 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -46,6 +46,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "WM_api.h"
#include "WM_message.h"
@@ -738,7 +739,7 @@ PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
}
/* Find the property which uses the given nested struct */
-static PropertyRNA *RNA_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
+PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
{
PropertyRNA *prop = NULL;
@@ -1422,7 +1423,7 @@ StructRNA *RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
return cprop->item_type;
}
}
- /* ignore other types, RNA_struct_find_nested calls with unchecked props */
+ /* ignore other types, rna_struct_find_nested calls with unchecked props */
return &RNA_UnknownType;
}
@@ -4821,1337 +4822,6 @@ PointerRNA rna_array_lookup_int(
return rna_pointer_inherit_refine(ptr, type, ((char *)data) + index * itemsize);
}
-/* RNA Path - Experiment */
-
-/**
- * Extract the first token from `path`.
- *
- * \param path: Extract the token from path, step the pointer to the beginning of the next token
- * \return The nil terminated token.
- */
-static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen)
-{
- int len = 0;
-
- /* Get data until `.` or `[`. */
- const char *p = *path;
- while (*p && !ELEM(*p, '.', '[')) {
- len++;
- p++;
- }
-
- /* Empty, return. */
- if (UNLIKELY(len == 0)) {
- return NULL;
- }
-
- /* Try to use fixed buffer if possible. */
- char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__);
- memcpy(buf, *path, sizeof(char) * len);
- buf[len] = '\0';
-
- if (*p == '.') {
- p++;
- }
- *path = p;
-
- return buf;
-}
-
-/**
- * Extract the first token in brackets from `path` (with quoted text support).
- *
- * - `[0]` -> `0`
- * - `["Some\"Quote"]` -> `Some"Quote`
- *
- * \param path: Extract the token from path, step the pointer to the beginning of the next token
- * (past quoted text and brackets).
- * \return The nil terminated token.
- */
-static char *rna_path_token_in_brackets(const char **path,
- char *fixedbuf,
- int fixedlen,
- bool *r_quoted)
-{
- int len = 0;
- bool quoted = false;
-
- BLI_assert(r_quoted != NULL);
-
- /* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */
- if (UNLIKELY(**path != '[')) {
- return NULL;
- }
-
- (*path)++;
- const char *p = *path;
-
- /* 2 kinds of look-ups now, quoted or unquoted. */
- if (*p == '"') {
- /* Find the matching quote. */
- (*path)++;
- p = *path;
- const char *p_end = BLI_str_escape_find_quote(p);
- if (p_end == NULL) {
- /* No Matching quote. */
- return NULL;
- }
- /* Exclude the last quote from the length. */
- len += (p_end - p);
-
- /* Skip the last quoted char to get the `]`. */
- p_end += 1;
- p = p_end;
- quoted = true;
- }
- else {
- /* Find the matching bracket. */
- while (*p && (*p != ']')) {
- len++;
- p++;
- }
- }
-
- if (UNLIKELY(*p != ']')) {
- return NULL;
- }
-
- /* Empty, return. */
- if (UNLIKELY(len == 0)) {
- return NULL;
- }
-
- /* Try to use fixed buffer if possible. */
- char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__);
-
- /* Copy string, taking into account escaped ']' */
- if (quoted) {
- BLI_str_unescape(buf, *path, len);
- /* +1 to step over the last quote. */
- BLI_assert((*path)[len] == '"');
- p = (*path) + len + 1;
- }
- else {
- memcpy(buf, *path, sizeof(char) * len);
- buf[len] = '\0';
- }
- /* Set path to start of next token. */
- if (*p == ']') {
- p++;
- }
- if (*p == '.') {
- p++;
- }
- *path = p;
-
- *r_quoted = quoted;
-
- return buf;
-}
-
-/**
- * \return true when the key in the path is correctly parsed and found in the collection
- * or when the path is empty.
- */
-static bool rna_path_parse_collection_key(const char **path,
- PointerRNA *ptr,
- PropertyRNA *prop,
- PointerRNA *r_nextptr)
-{
- char fixedbuf[256];
- int intkey;
-
- *r_nextptr = *ptr;
-
- /* end of path, ok */
- if (!(**path)) {
- return true;
- }
-
- bool found = false;
- if (**path == '[') {
- bool quoted;
- char *token;
-
- /* resolve the lookup with [] brackets */
- token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), &quoted);
-
- if (!token) {
- return false;
- }
-
- /* check for "" to see if it is a string */
- if (quoted) {
- if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) {
- found = true;
- }
- else {
- r_nextptr->data = NULL;
- }
- }
- else {
- /* otherwise do int lookup */
- intkey = atoi(token);
- if (intkey == 0 && (token[0] != '0' || token[1] != '\0')) {
- return false; /* we can be sure the fixedbuf was used in this case */
- }
- if (RNA_property_collection_lookup_int(ptr, prop, intkey, r_nextptr)) {
- found = true;
- }
- else {
- r_nextptr->data = NULL;
- }
- }
-
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
- }
- else {
- if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) {
- found = true;
- }
- else {
- /* ensure we quit on invalid values */
- r_nextptr->data = NULL;
- }
- }
-
- return found;
-}
-
-static bool rna_path_parse_array_index(const char **path,
- PointerRNA *ptr,
- PropertyRNA *prop,
- int *r_index)
-{
- char fixedbuf[256];
- int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0};
- int len[RNA_MAX_ARRAY_DIMENSION];
- const int dim = RNA_property_array_dimension(ptr, prop, len);
- int i;
-
- *r_index = -1;
-
- /* end of path, ok */
- if (!(**path)) {
- return true;
- }
-
- for (i = 0; i < dim; i++) {
- int temp_index = -1;
- char *token;
-
- /* multi index resolve */
- if (**path == '[') {
- bool quoted;
- token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), &quoted);
-
- if (token == NULL) {
- /* invalid syntax blah[] */
- return false;
- }
- /* check for "" to see if it is a string */
- if (quoted) {
- temp_index = RNA_property_array_item_index(prop, *token);
- }
- else {
- /* otherwise do int lookup */
- temp_index = atoi(token);
-
- if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
-
- return false;
- }
- }
- }
- else if (dim == 1) {
- /* location.x || scale.X, single dimension arrays only */
- token = rna_path_token(path, fixedbuf, sizeof(fixedbuf));
- if (token == NULL) {
- /* invalid syntax blah. */
- return false;
- }
- temp_index = RNA_property_array_item_index(prop, *token);
- }
- else {
- /* just to avoid uninitialized pointer use */
- token = fixedbuf;
- }
-
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
-
- /* out of range */
- if (temp_index < 0 || temp_index >= len[i]) {
- return false;
- }
-
- index_arr[i] = temp_index;
- /* end multi index resolve */
- }
-
- /* arrays always contain numbers so further values are not valid */
- if (**path) {
- return false;
- }
-
- /* flatten index over all dimensions */
- {
- int totdim = 1;
- int flat_index = 0;
-
- for (i = dim - 1; i >= 0; i--) {
- flat_index += index_arr[i] * totdim;
- totdim *= len[i];
- }
-
- *r_index = flat_index;
- }
- return true;
-}
-
-/**
- * Generic rna path parser.
- *
- * \note All parameters besides \a ptr and \a path are optional.
- *
- * \param ptr: The root of given RNA path.
- * \param path: The RNA path.
- * \param r_ptr: The final RNA data holding the last property in \a path.
- * \param r_prop: The final property of \a r_ptr, from \a path.
- * \param r_index: The final index in the \a r_prop, if defined by \a path.
- * \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the
- * pointer, or of the collection item.
- * Mutually exclusive with \a eval_pointer option.
- * \param r_elements: A list of \a PropertyElemRNA items(pairs of \a PointerRNA, \a PropertyRNA
- * that represent the whole given \a path).
- * \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a
- * Collection property, \a r_ptr will be set to the value of that property,
- * and \a r_prop will be NULL.
- * Mutually exclusive with \a r_item_ptr.
- *
- * \return \a true on success, \a false if the path is somehow invalid.
- */
-static bool rna_path_parse(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index,
- PointerRNA *r_item_ptr,
- ListBase *r_elements,
- const bool eval_pointer)
-{
- BLI_assert(r_item_ptr == NULL || !eval_pointer);
- PropertyRNA *prop;
- PointerRNA curptr, nextptr;
- PropertyElemRNA *prop_elem = NULL;
- int index = -1;
- char fixedbuf[256];
- int type;
- const bool do_item_ptr = r_item_ptr != NULL && !eval_pointer;
-
- if (do_item_ptr) {
- RNA_POINTER_INVALIDATE(&nextptr);
- }
-
- prop = NULL;
- curptr = *ptr;
-
- if (path == NULL || *path == '\0') {
- return false;
- }
-
- while (*path) {
- if (do_item_ptr) {
- RNA_POINTER_INVALIDATE(&nextptr);
- }
-
- const bool use_id_prop = (*path == '[');
- /* custom property lookup ?
- * C.object["someprop"]
- */
-
- if (!curptr.data) {
- return false;
- }
-
- /* look up property name in current struct */
- bool quoted = false;
- char *token = use_id_prop ?
- rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), &quoted) :
- rna_path_token(&path, fixedbuf, sizeof(fixedbuf));
- if (!token) {
- return false;
- }
-
- prop = NULL;
- if (use_id_prop) { /* look up property name in current struct */
- IDProperty *group = RNA_struct_idprops(&curptr, 0);
- if (group && quoted) {
- prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token);
- }
- }
- else {
- prop = RNA_struct_find_property(&curptr, token);
- }
-
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
-
- if (!prop) {
- return false;
- }
-
- if (r_elements) {
- prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
- prop_elem->ptr = curptr;
- prop_elem->prop = prop;
- prop_elem->index = -1; /* index will be added later, if needed. */
- BLI_addtail(r_elements, prop_elem);
- }
-
- type = RNA_property_type(prop);
-
- /* now look up the value of this property if it is a pointer or
- * collection, otherwise return the property rna so that the
- * caller can read the value of the property itself */
- switch (type) {
- case PROP_POINTER: {
- /* resolve pointer if further path elements follow
- * or explicitly requested
- */
- if (do_item_ptr || eval_pointer || *path != '\0') {
- nextptr = RNA_property_pointer_get(&curptr, prop);
- }
-
- if (eval_pointer || *path != '\0') {
- curptr = nextptr;
- prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
- index = -1;
- }
- break;
- }
- case PROP_COLLECTION: {
- /* Resolve pointer if further path elements follow.
- * Note that if path is empty, rna_path_parse_collection_key will do nothing anyway,
- * so do_item_ptr is of no use in that case.
- */
- if (*path) {
- if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) {
- return false;
- }
-
- if (eval_pointer || *path != '\0') {
- curptr = nextptr;
- prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
- index = -1;
- }
- }
- break;
- }
- default:
- if (r_index || prop_elem) {
- if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) {
- return false;
- }
-
- if (prop_elem) {
- prop_elem->index = index;
- }
- }
- break;
- }
- }
-
- if (r_ptr) {
- *r_ptr = curptr;
- }
- if (r_prop) {
- *r_prop = prop;
- }
- if (r_index) {
- *r_index = index;
- }
- if (r_item_ptr && do_item_ptr) {
- *r_item_ptr = nextptr;
- }
-
- if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop ||
- prop_elem->index != index)) {
- prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
- prop_elem->ptr = curptr;
- prop_elem->prop = prop;
- prop_elem->index = index;
- BLI_addtail(r_elements, prop_elem);
- }
-
- return true;
-}
-
-bool RNA_path_resolve(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true)) {
- return false;
- }
-
- return r_ptr->data != NULL;
-}
-
-bool RNA_path_resolve_full(
- const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true)) {
- return false;
- }
-
- return r_ptr->data != NULL;
-}
-
-bool RNA_path_resolve_full_maybe_null(
- const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
-{
- return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true);
-}
-
-bool RNA_path_resolve_property(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, false)) {
- return false;
- }
-
- return r_ptr->data != NULL && *r_prop != NULL;
-}
-
-bool RNA_path_resolve_property_full(
- const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false)) {
- return false;
- }
-
- return r_ptr->data != NULL && *r_prop != NULL;
-}
-
-bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- PointerRNA *r_item_ptr)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, r_item_ptr, NULL, false)) {
- return false;
- }
-
- return r_ptr->data != NULL && *r_prop != NULL;
-}
-
-bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index,
- PointerRNA *r_item_ptr)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, r_item_ptr, NULL, false)) {
- return false;
- }
-
- return r_ptr->data != NULL && *r_prop != NULL;
-}
-bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
-{
- return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false);
-}
-
-char *RNA_path_append(const char *path,
- const PointerRNA *UNUSED(ptr),
- PropertyRNA *prop,
- int intkey,
- const char *strkey)
-{
- DynStr *dynstr;
- char *result;
-
- dynstr = BLI_dynstr_new();
-
- /* add .identifier */
- if (path) {
- BLI_dynstr_append(dynstr, path);
- if (*path) {
- BLI_dynstr_append(dynstr, ".");
- }
- }
-
- BLI_dynstr_append(dynstr, RNA_property_identifier(prop));
-
- if (RNA_property_type(prop) == PROP_COLLECTION) {
- /* add ["strkey"] or [intkey] */
- BLI_dynstr_append(dynstr, "[");
-
- if (strkey) {
- const int strkey_esc_max_size = (strlen(strkey) * 2) + 1;
- char *strkey_esc = BLI_array_alloca(strkey_esc, strkey_esc_max_size);
- BLI_str_escape(strkey_esc, strkey, strkey_esc_max_size);
- BLI_dynstr_append(dynstr, "\"");
- BLI_dynstr_append(dynstr, strkey_esc);
- BLI_dynstr_append(dynstr, "\"");
- }
- else {
- char appendstr[128];
- BLI_snprintf(appendstr, sizeof(appendstr), "%d", intkey);
- BLI_dynstr_append(dynstr, appendstr);
- }
-
- BLI_dynstr_append(dynstr, "]");
- }
-
- result = BLI_dynstr_get_cstring(dynstr);
- BLI_dynstr_free(dynstr);
-
- return result;
-}
-
-/* Having both path append & back seems like it could be useful,
- * this function isn't used at the moment. */
-static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path)
-{
- char fixedbuf[256];
- const char *previous, *current;
- char *result;
- int i;
-
- if (!path) {
- return NULL;
- }
-
- previous = NULL;
- current = path;
-
- /* parse token by token until the end, then we back up to the previous
- * position and strip of the next token to get the path one step back */
- while (*current) {
- char *token;
-
- token = rna_path_token(&current, fixedbuf, sizeof(fixedbuf));
-
- if (!token) {
- return NULL;
- }
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
-
- /* in case of collection we also need to strip off [] */
- bool quoted;
- token = rna_path_token_in_brackets(&current, fixedbuf, sizeof(fixedbuf), &quoted);
- if (token && token != fixedbuf) {
- MEM_freeN(token);
- }
-
- if (!*current) {
- break;
- }
-
- previous = current;
- }
-
- if (!previous) {
- return NULL;
- }
-
- /* copy and strip off last token */
- i = previous - path;
- result = BLI_strdup(path);
-
- if (i > 0 && result[i - 1] == '.') {
- i--;
- }
- result[i] = 0;
-
- return result;
-}
-
-const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop)
-{
- if (array_prop != NULL) {
- if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
- BLI_assert(array_prop->arraydimension == 0);
- return NULL;
- }
- if (array_prop->arraydimension == 0) {
- return NULL;
- }
- }
-
- /* Valid 'array part' of a rna path can only have '[', ']' and digit characters.
- * It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */
- off_t rna_path_len = (off_t)strlen(rna_path);
- if (rna_path[rna_path_len] != ']') {
- return NULL;
- }
- const char *last_valid_index_token_start = NULL;
- for (rna_path_len--; rna_path_len >= 0; rna_path_len--) {
- switch (rna_path[rna_path_len]) {
- case '[':
- if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') {
- return &rna_path[rna_path_len];
- }
- last_valid_index_token_start = &rna_path[rna_path_len];
- rna_path_len--;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- break;
- default:
- return last_valid_index_token_start;
- }
- }
- return last_valid_index_token_start;
-}
-
-/* generic path search func
- * if its needed this could also reference the IDProperty direct */
-typedef struct IDP_Chain {
- struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */
-
- const char *name;
- int index;
-
-} IDP_Chain;
-
-static char *rna_idp_path_create(IDP_Chain *child_link)
-{
- DynStr *dynstr = BLI_dynstr_new();
- char *path;
- bool is_first = true;
-
- int tot = 0;
- IDP_Chain *link = child_link;
-
- /* reverse the list */
- IDP_Chain *link_prev;
- link_prev = NULL;
- while (link) {
- IDP_Chain *link_next = link->up;
- link->up = link_prev;
- link_prev = link;
- link = link_next;
- tot++;
- }
-
- for (link = link_prev; link; link = link->up) {
- /* pass */
- if (link->index >= 0) {
- BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index);
- }
- else {
- BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name);
- }
-
- is_first = false;
- }
-
- path = BLI_dynstr_get_cstring(dynstr);
- BLI_dynstr_free(dynstr);
-
- if (*path == '\0') {
- MEM_freeN(path);
- path = NULL;
- }
-
- return path;
-}
-
-static char *rna_idp_path(PointerRNA *ptr,
- IDProperty *haystack,
- IDProperty *needle,
- IDP_Chain *parent_link)
-{
- char *path = NULL;
- IDP_Chain link;
-
- IDProperty *iter;
- int i;
-
- BLI_assert(haystack->type == IDP_GROUP);
-
- link.up = parent_link;
- /* Always set both name and index, else a stale value might get used. */
- link.name = NULL;
- link.index = -1;
-
- for (i = 0, iter = haystack->data.group.first; iter; iter = iter->next, i++) {
- if (needle == iter) { /* found! */
- link.name = iter->name;
- link.index = -1;
- path = rna_idp_path_create(&link);
- break;
- }
-
- /* Early out in case the IDProperty type cannot contain RNA properties. */
- if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) {
- continue;
- }
-
- /* Ensure this is RNA. */
- /* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name
- * collides with an actual fully static RNA property of the same struct (which would then not
- * be flagged with `PROP_IDPROPERTY`).
- *
- * That case must be ignored here, we only want to deal with runtime RNA properties stored in
- * IDProps.
- *
- * See T84091. */
- PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
- if (prop == NULL || (prop->flag & PROP_IDPROPERTY) == 0) {
- continue;
- }
-
- if (iter->type == IDP_GROUP) {
- if (prop->type == PROP_POINTER) {
- PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
- if (RNA_pointer_is_null(&child_ptr)) {
- /* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL
- * value is perfectly valid. Just means it won't match the searched needle. */
- continue;
- }
-
- link.name = iter->name;
- link.index = -1;
- if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
- break;
- }
- }
- }
- else if (iter->type == IDP_IDPARRAY) {
- if (prop->type == PROP_COLLECTION) {
- IDProperty *array = IDP_IDPArray(iter);
- if (needle >= array && needle < (iter->len + array)) { /* found! */
- link.name = iter->name;
- link.index = (int)(needle - array);
- path = rna_idp_path_create(&link);
- break;
- }
- int j;
- link.name = iter->name;
- for (j = 0; j < iter->len; j++, array++) {
- PointerRNA child_ptr;
- if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) {
- if (RNA_pointer_is_null(&child_ptr)) {
- /* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case
- * a NULL value is perfectly valid. Just means it won't match the searched needle. */
- continue;
- }
- link.index = j;
- if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
- break;
- }
- }
- }
- if (path) {
- break;
- }
- }
- }
- }
-
- return path;
-}
-
-char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
-{
- IDProperty *haystack = RNA_struct_idprops(ptr, false);
-
- if (haystack) { /* can fail when called on bones */
- return rna_idp_path(ptr, haystack, needle, NULL);
- }
- return NULL;
-}
-
-static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr)
-{
- PointerRNA id_ptr;
-
- BLI_assert(ptr->owner_id != NULL);
-
- /* TODO: Support Bones/PoseBones. no pointers stored to the bones from here, only the ID.
- * See example in T25746.
- * Unless this is added only way to find this is to also search
- * all bones and pose bones of an armature or object.
- */
- RNA_id_pointer_create(ptr->owner_id, &id_ptr);
-
- return RNA_path_from_struct_to_idproperty(&id_ptr, ptr->data);
-}
-
-ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path)
-{
- if (r_path) {
- *r_path = "";
- }
-
- if ((id == NULL) || (id->flag & LIB_EMBEDDED_DATA) == 0) {
- return id;
- }
-
- const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
- if (r_path) {
- switch (GS(id->name)) {
- case ID_NT:
- *r_path = "node_tree";
- break;
- case ID_GR:
- *r_path = "collection";
- break;
- default:
- BLI_assert_msg(0, "Missing handling of embedded id type.");
- }
- }
-
- if (id_type->owner_get == NULL) {
- BLI_assert_msg(0, "Missing handling of embedded id type.");
- return id;
- }
- return id_type->owner_get(bmain, id);
-}
-
-static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id)
-{
- if (r_real_id != NULL) {
- *r_real_id = NULL;
- }
-
- const char *prefix;
- ID *real_id = RNA_find_real_ID_and_path(bmain, id, &prefix);
-
- if (r_real_id != NULL) {
- *r_real_id = real_id;
- }
-
- if (path != NULL) {
- char *new_path = NULL;
-
- if (real_id) {
- if (prefix[0]) {
- new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path);
- }
- else {
- return path;
- }
- }
-
- MEM_freeN(path);
- return new_path;
- }
- return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL;
-}
-
-char *RNA_path_from_ID_to_struct(const PointerRNA *ptr)
-{
- char *ptrpath = NULL;
-
- if (!ptr->owner_id || !ptr->data) {
- return NULL;
- }
-
- if (!RNA_struct_is_ID(ptr->type)) {
- if (ptr->type->path) {
- /* if type has a path to some ID, use it */
- ptrpath = ptr->type->path((PointerRNA *)ptr);
- }
- else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
- PointerRNA parentptr;
- PropertyRNA *userprop;
-
- /* find the property in the struct we're nested in that references this struct, and
- * use its identifier as the first part of the path used...
- */
- RNA_id_pointer_create(ptr->owner_id, &parentptr);
- userprop = RNA_struct_find_nested(&parentptr, ptr->type);
-
- if (userprop) {
- ptrpath = BLI_strdup(RNA_property_identifier(userprop));
- }
- else {
- return NULL; /* can't do anything about this case yet... */
- }
- }
- else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
- /* special case, easier to deal with here than in ptr->type->path() */
- return rna_path_from_ID_to_idpgroup(ptr);
- }
- else {
- return NULL;
- }
- }
-
- return ptrpath;
-}
-
-char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real)
-{
- char *path = RNA_path_from_ID_to_struct(ptr);
-
- /* NULL path is valid in that case, when given struct is an ID one... */
- return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real);
-}
-
-static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY_LENGTH],
- const int totdims,
- const int index_dim,
- int index,
- int r_index_multi[RNA_MAX_ARRAY_LENGTH])
-{
- int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1];
- int i = totdims - 1;
- dimsize_step[i + 1] = 1;
- dimsize_step[i] = dimsize[i];
- while (--i != -1) {
- dimsize_step[i] = dimsize[i] * dimsize_step[i + 1];
- }
- while (++i != index_dim) {
- int index_round = index / dimsize_step[i + 1];
- r_index_multi[i] = index_round;
- index -= (index_round * dimsize_step[i + 1]);
- }
- BLI_assert(index == 0);
-}
-
-static void rna_path_array_multi_string_from_flat_index(const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index,
- char *index_str,
- int index_str_len)
-{
- int dimsize[RNA_MAX_ARRAY_LENGTH];
- int totdims = RNA_property_array_dimension(ptr, prop, dimsize);
- int index_multi[RNA_MAX_ARRAY_LENGTH];
-
- rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi);
-
- for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) {
- offset += BLI_snprintf_rlen(
- &index_str[offset], index_str_len - offset, "[%d]", index_multi[i]);
- }
-}
-
-char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index)
-{
- const bool is_rna = (prop->magic == RNA_MAGIC);
- const char *propname;
- char *ptrpath, *path;
-
- if (!ptr->owner_id || !ptr->data) {
- return NULL;
- }
-
- /* path from ID to the struct holding this property */
- ptrpath = RNA_path_from_ID_to_struct(ptr);
-
- propname = RNA_property_identifier(prop);
-
- /* support indexing w/ multi-dimensional arrays */
- char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1];
- if (index_dim == 0) {
- index_str[0] = '\0';
- }
- else {
- rna_path_array_multi_string_from_flat_index(
- ptr, prop, index_dim, index, index_str, sizeof(index_str));
- }
-
- if (ptrpath) {
- if (is_rna) {
- path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str);
- }
- else {
- char propname_esc[MAX_IDPROP_NAME * 2];
- BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
- path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str);
- }
- MEM_freeN(ptrpath);
- }
- else if (RNA_struct_is_ID(ptr->type)) {
- if (is_rna) {
- path = BLI_sprintfN("%s%s", propname, index_str);
- }
- else {
- char propname_esc[MAX_IDPROP_NAME * 2];
- BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
- path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str);
- }
- }
- else {
- path = NULL;
- }
-
- return path;
-}
-
-char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
-{
- return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1);
-}
-
-char *RNA_path_from_real_ID_to_property_index(Main *bmain,
- const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index,
- ID **r_real_id)
-{
- char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index);
-
- /* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part
- * of the path either. */
- return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL;
-}
-
-char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr,
- PropertyRNA *prop,
- const StructRNA *type)
-{
- /* Try to recursively find an "type"'d ancestor,
- * to handle situations where path from ID is not enough. */
- PointerRNA idptr;
- ListBase path_elems = {NULL};
- char *path = NULL;
- char *full_path = RNA_path_from_ID_to_property(ptr, prop);
-
- if (full_path == NULL) {
- return NULL;
- }
-
- RNA_id_pointer_create(ptr->owner_id, &idptr);
-
- if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) {
- PropertyElemRNA *prop_elem;
-
- for (prop_elem = path_elems.last; prop_elem; prop_elem = prop_elem->prev) {
- if (RNA_struct_is_a(prop_elem->ptr.type, type)) {
- char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr);
- if (ref_path) {
- path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */
- MEM_freeN(ref_path);
- }
- break;
- }
- }
-
- BLI_freelistN(&path_elems);
- }
-
- MEM_freeN(full_path);
- return path;
-}
-
-char *RNA_path_full_ID_py(Main *bmain, ID *id)
-{
- const char *path;
- ID *id_real = RNA_find_real_ID_and_path(bmain, id, &path);
-
- if (id_real) {
- id = id_real;
- }
- else {
- path = "";
- }
-
- char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4];
- if (ID_IS_LINKED(id)) {
- int ofs = 0;
- memcpy(lib_filepath_esc, ", \"", 3);
- ofs += 3;
- ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc));
- memcpy(lib_filepath_esc + ofs, "\"", 2);
- }
- else {
- lib_filepath_esc[0] = '\0';
- }
-
- char id_esc[(sizeof(id->name) - 2) * 2];
- BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc));
-
- return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s",
- BKE_idtype_idcode_to_name_plural(GS(id->name)),
- id_esc,
- lib_filepath_esc,
- path[0] ? "." : "",
- path);
-}
-
-char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr)
-{
- char *id_path;
- char *data_path;
-
- char *ret;
-
- if (!ptr->owner_id) {
- return NULL;
- }
-
- /* never fails */
- id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
-
- data_path = RNA_path_from_ID_to_struct(ptr);
-
- /* XXX data_path may be NULL (see T36788),
- * do we want to get the 'bpy.data.foo["bar"].(null)' stuff? */
- ret = BLI_sprintfN("%s.%s", id_path, data_path);
-
- if (data_path) {
- MEM_freeN(data_path);
- }
- MEM_freeN(id_path);
-
- return ret;
-}
-
-char *RNA_path_full_property_py_ex(
- Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback)
-{
- char *id_path;
- const char *data_delim;
- const char *data_path;
- bool data_path_free;
-
- char *ret;
-
- if (!ptr->owner_id) {
- return NULL;
- }
-
- /* never fails */
- id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
-
- data_path = RNA_path_from_ID_to_property(ptr, prop);
- if (data_path) {
- data_delim = (data_path[0] == '[') ? "" : ".";
- data_path_free = true;
- }
- else {
- if (use_fallback) {
- /* Fuzzy fallback. Be explicit in our ignorance. */
- data_path = RNA_property_identifier(prop);
- data_delim = " ... ";
- }
- else {
- data_delim = ".";
- }
- data_path_free = false;
- }
-
- if ((index == -1) || (RNA_property_array_check(prop) == false)) {
- ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path);
- }
- else {
- ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index);
- }
- MEM_freeN(id_path);
- if (data_path_free) {
- MEM_freeN((void *)data_path);
- }
-
- return ret;
-}
-
-char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index)
-{
- return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false);
-}
-
-char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
-{
- char *data_path;
-
- char *ret;
-
- if (!ptr->owner_id) {
- return NULL;
- }
-
- data_path = RNA_path_from_ID_to_property(ptr, prop);
-
- if (data_path == NULL) {
- /* This may not be an ID at all, check for simple when pointer owns property.
- * TODO: more complex nested case. */
- if (!RNA_struct_is_ID(ptr->type)) {
- const char *prop_identifier = RNA_property_identifier(prop);
- if (RNA_struct_find_property(ptr, prop_identifier) == prop) {
- data_path = BLI_strdup(prop_identifier);
- }
- }
- }
-
- if ((index == -1) || (RNA_property_array_check(prop) == false)) {
- ret = BLI_strdup(data_path);
- }
- else {
- ret = BLI_sprintfN("%s[%d]", data_path, index);
- }
-
- if (data_path) {
- MEM_freeN(data_path);
- }
-
- return ret;
-}
-
-char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
-{
- const bool is_rna = (prop->magic == RNA_MAGIC);
- const char *propname = RNA_property_identifier(prop);
- char *ret;
-
- if ((index == -1) || (RNA_property_array_check(prop) == false)) {
- if (is_rna) {
- ret = BLI_strdup(propname);
- }
- else {
- char propname_esc[MAX_IDPROP_NAME * 2];
- BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
- ret = BLI_sprintfN("[\"%s\"]", propname_esc);
- }
- }
- else {
- if (is_rna) {
- ret = BLI_sprintfN("%s[%d]", propname, index);
- }
- else {
- char propname_esc[MAX_IDPROP_NAME * 2];
- BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
- ret = BLI_sprintfN("[\"%s\"][%d]", propname_esc, index);
- }
- }
-
- return ret;
-}
-
/* Quick name based property access */
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c
index 17c00923efa..d1df54df3cd 100644
--- a/source/blender/makesrna/intern/rna_access_compare_override.c
+++ b/source/blender/makesrna/intern/rna_access_compare_override.c
@@ -38,6 +38,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "rna_access_internal.h"
diff --git a/source/blender/makesrna/intern/rna_access_internal.h b/source/blender/makesrna/intern/rna_access_internal.h
index a7dd35af670..384ff417fc9 100644
--- a/source/blender/makesrna/intern/rna_access_internal.h
+++ b/source/blender/makesrna/intern/rna_access_internal.h
@@ -10,6 +10,10 @@
#include "rna_internal_types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct IDProperty;
struct PropertyRNAOrID;
@@ -26,3 +30,9 @@ void rna_property_rna_or_id_get(PropertyRNA *prop,
void rna_idproperty_touch(struct IDProperty *idprop);
struct IDProperty *rna_idproperty_find(PointerRNA *ptr, const char *name);
+
+PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index 92cdcc6d781..2a85da42483 100644
--- a/source/blender/makesrna/intern/rna_color.c
+++ b/source/blender/makesrna/intern/rna_color.c
@@ -23,6 +23,7 @@
#ifdef RNA_RUNTIME
# include "RNA_access.h"
+# include "RNA_path.h"
# include "DNA_image_types.h"
# include "DNA_material_types.h"
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 833060e40f8..370455302b6 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -14,6 +14,10 @@
#include "UI_resources.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define RNA_MAGIC ((int)~0)
struct AssetLibraryReference;
@@ -691,3 +695,7 @@ void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values);
: -FLT_MAX, double \
: -DBL_MAX)
#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc
new file mode 100644
index 00000000000..3e0fc9add80
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_path.cc
@@ -0,0 +1,1360 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup RNA
+ */
+
+#include <cstdlib>
+#include <stdlib.h>
+#include <string.h>
+
+#include "BLI_alloca.h"
+#include "BLI_dynstr.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_idprop.h"
+#include "BKE_idtype.h"
+
+#include "DNA_ID.h" /* For ID properties. */
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_path.h"
+#include "RNA_prototypes.h"
+
+#include "rna_access_internal.h"
+#include "rna_internal.h"
+
+/**
+ * Extract the first token from `path`.
+ *
+ * \param path: Extract the token from path, step the pointer to the beginning of the next token
+ * \return The nil terminated token.
+ */
+static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen)
+{
+ int len = 0;
+
+ /* Get data until `.` or `[`. */
+ const char *p = *path;
+ while (*p && !ELEM(*p, '.', '[')) {
+ len++;
+ p++;
+ }
+
+ /* Empty, return. */
+ if (UNLIKELY(len == 0)) {
+ return NULL;
+ }
+
+ /* Try to use fixed buffer if possible. */
+ char *buf = (len + 1 < fixedlen) ? fixedbuf :
+ (char *)MEM_mallocN(sizeof(char) * (len + 1), __func__);
+ memcpy(buf, *path, sizeof(char) * len);
+ buf[len] = '\0';
+
+ if (*p == '.') {
+ p++;
+ }
+ *path = p;
+
+ return buf;
+}
+
+/**
+ * Extract the first token in brackets from `path` (with quoted text support).
+ *
+ * - `[0]` -> `0`
+ * - `["Some\"Quote"]` -> `Some"Quote`
+ *
+ * \param path: Extract the token from path, step the pointer to the beginning of the next token
+ * (past quoted text and brackets).
+ * \return The nil terminated token.
+ */
+static char *rna_path_token_in_brackets(const char **path,
+ char *fixedbuf,
+ int fixedlen,
+ bool *r_quoted)
+{
+ int len = 0;
+ bool quoted = false;
+
+ BLI_assert(r_quoted != NULL);
+
+ /* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */
+ if (UNLIKELY(**path != '[')) {
+ return NULL;
+ }
+
+ (*path)++;
+ const char *p = *path;
+
+ /* 2 kinds of look-ups now, quoted or unquoted. */
+ if (*p == '"') {
+ /* Find the matching quote. */
+ (*path)++;
+ p = *path;
+ const char *p_end = BLI_str_escape_find_quote(p);
+ if (p_end == NULL) {
+ /* No Matching quote. */
+ return NULL;
+ }
+ /* Exclude the last quote from the length. */
+ len += (p_end - p);
+
+ /* Skip the last quoted char to get the `]`. */
+ p_end += 1;
+ p = p_end;
+ quoted = true;
+ }
+ else {
+ /* Find the matching bracket. */
+ while (*p && (*p != ']')) {
+ len++;
+ p++;
+ }
+ }
+
+ if (UNLIKELY(*p != ']')) {
+ return NULL;
+ }
+
+ /* Empty, return. */
+ if (UNLIKELY(len == 0)) {
+ return NULL;
+ }
+
+ /* Try to use fixed buffer if possible. */
+ char *buf = (len + 1 < fixedlen) ? fixedbuf :
+ (char *)MEM_mallocN(sizeof(char) * (len + 1), __func__);
+
+ /* Copy string, taking into account escaped ']' */
+ if (quoted) {
+ BLI_str_unescape(buf, *path, len);
+ /* +1 to step over the last quote. */
+ BLI_assert((*path)[len] == '"');
+ p = (*path) + len + 1;
+ }
+ else {
+ memcpy(buf, *path, sizeof(char) * len);
+ buf[len] = '\0';
+ }
+ /* Set path to start of next token. */
+ if (*p == ']') {
+ p++;
+ }
+ if (*p == '.') {
+ p++;
+ }
+ *path = p;
+
+ *r_quoted = quoted;
+
+ return buf;
+}
+
+/**
+ * \return true when the key in the path is correctly parsed and found in the collection
+ * or when the path is empty.
+ */
+static bool rna_path_parse_collection_key(const char **path,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ PointerRNA *r_nextptr)
+{
+ char fixedbuf[256];
+ int intkey;
+
+ *r_nextptr = *ptr;
+
+ /* end of path, ok */
+ if (!(**path)) {
+ return true;
+ }
+
+ bool found = false;
+ if (**path == '[') {
+ bool quoted;
+ char *token;
+
+ /* resolve the lookup with [] brackets */
+ token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), &quoted);
+
+ if (!token) {
+ return false;
+ }
+
+ /* check for "" to see if it is a string */
+ if (quoted) {
+ if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) {
+ found = true;
+ }
+ else {
+ r_nextptr->data = NULL;
+ }
+ }
+ else {
+ /* otherwise do int lookup */
+ intkey = atoi(token);
+ if (intkey == 0 && (token[0] != '0' || token[1] != '\0')) {
+ return false; /* we can be sure the fixedbuf was used in this case */
+ }
+ if (RNA_property_collection_lookup_int(ptr, prop, intkey, r_nextptr)) {
+ found = true;
+ }
+ else {
+ r_nextptr->data = NULL;
+ }
+ }
+
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+ }
+ else {
+ if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) {
+ found = true;
+ }
+ else {
+ /* ensure we quit on invalid values */
+ r_nextptr->data = NULL;
+ }
+ }
+
+ return found;
+}
+
+static bool rna_path_parse_array_index(const char **path,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ int *r_index)
+{
+ char fixedbuf[256];
+ int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0};
+ int len[RNA_MAX_ARRAY_DIMENSION];
+ const int dim = RNA_property_array_dimension(ptr, prop, len);
+ int i;
+
+ *r_index = -1;
+
+ /* end of path, ok */
+ if (!(**path)) {
+ return true;
+ }
+
+ for (i = 0; i < dim; i++) {
+ int temp_index = -1;
+ char *token;
+
+ /* multi index resolve */
+ if (**path == '[') {
+ bool quoted;
+ token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), &quoted);
+
+ if (token == NULL) {
+ /* invalid syntax blah[] */
+ return false;
+ }
+ /* check for "" to see if it is a string */
+ if (quoted) {
+ temp_index = RNA_property_array_item_index(prop, *token);
+ }
+ else {
+ /* otherwise do int lookup */
+ temp_index = atoi(token);
+
+ if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ return false;
+ }
+ }
+ }
+ else if (dim == 1) {
+ /* location.x || scale.X, single dimension arrays only */
+ token = rna_path_token(path, fixedbuf, sizeof(fixedbuf));
+ if (token == NULL) {
+ /* invalid syntax blah. */
+ return false;
+ }
+ temp_index = RNA_property_array_item_index(prop, *token);
+ }
+ else {
+ /* just to avoid uninitialized pointer use */
+ token = fixedbuf;
+ }
+
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ /* out of range */
+ if (temp_index < 0 || temp_index >= len[i]) {
+ return false;
+ }
+
+ index_arr[i] = temp_index;
+ /* end multi index resolve */
+ }
+
+ /* arrays always contain numbers so further values are not valid */
+ if (**path) {
+ return false;
+ }
+
+ /* flatten index over all dimensions */
+ {
+ int totdim = 1;
+ int flat_index = 0;
+
+ for (i = dim - 1; i >= 0; i--) {
+ flat_index += index_arr[i] * totdim;
+ totdim *= len[i];
+ }
+
+ *r_index = flat_index;
+ }
+ return true;
+}
+
+/**
+ * Generic rna path parser.
+ *
+ * \note All parameters besides \a ptr and \a path are optional.
+ *
+ * \param ptr: The root of given RNA path.
+ * \param path: The RNA path.
+ * \param r_ptr: The final RNA data holding the last property in \a path.
+ * \param r_prop: The final property of \a r_ptr, from \a path.
+ * \param r_index: The final index in the \a r_prop, if defined by \a path.
+ * \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the
+ * pointer, or of the collection item.
+ * Mutually exclusive with \a eval_pointer option.
+ * \param r_elements: A list of \a PropertyElemRNA items(pairs of \a PointerRNA, \a PropertyRNA
+ * that represent the whole given \a path).
+ * \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a
+ * Collection property, \a r_ptr will be set to the value of that property,
+ * and \a r_prop will be NULL.
+ * Mutually exclusive with \a r_item_ptr.
+ *
+ * \return \a true on success, \a false if the path is somehow invalid.
+ */
+static bool rna_path_parse(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index,
+ PointerRNA *r_item_ptr,
+ ListBase *r_elements,
+ const bool eval_pointer)
+{
+ BLI_assert(r_item_ptr == NULL || !eval_pointer);
+ PropertyRNA *prop;
+ PointerRNA curptr, nextptr;
+ PropertyElemRNA *prop_elem = NULL;
+ int index = -1;
+ char fixedbuf[256];
+ int type;
+ const bool do_item_ptr = r_item_ptr != NULL && !eval_pointer;
+
+ if (do_item_ptr) {
+ RNA_POINTER_INVALIDATE(&nextptr);
+ }
+
+ prop = NULL;
+ curptr = *ptr;
+
+ if (path == NULL || *path == '\0') {
+ return false;
+ }
+
+ while (*path) {
+ if (do_item_ptr) {
+ RNA_POINTER_INVALIDATE(&nextptr);
+ }
+
+ const bool use_id_prop = (*path == '[');
+ /* custom property lookup ?
+ * C.object["someprop"]
+ */
+
+ if (!curptr.data) {
+ return false;
+ }
+
+ /* look up property name in current struct */
+ bool quoted = false;
+ char *token = use_id_prop ?
+ rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), &quoted) :
+ rna_path_token(&path, fixedbuf, sizeof(fixedbuf));
+ if (!token) {
+ return false;
+ }
+
+ prop = NULL;
+ if (use_id_prop) { /* look up property name in current struct */
+ IDProperty *group = RNA_struct_idprops(&curptr, 0);
+ if (group && quoted) {
+ prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token);
+ }
+ }
+ else {
+ prop = RNA_struct_find_property(&curptr, token);
+ }
+
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ if (!prop) {
+ return false;
+ }
+
+ if (r_elements) {
+ prop_elem = MEM_cnew<PropertyElemRNA>(__func__);
+ prop_elem->ptr = curptr;
+ prop_elem->prop = prop;
+ prop_elem->index = -1; /* index will be added later, if needed. */
+ BLI_addtail(r_elements, prop_elem);
+ }
+
+ type = RNA_property_type(prop);
+
+ /* now look up the value of this property if it is a pointer or
+ * collection, otherwise return the property rna so that the
+ * caller can read the value of the property itself */
+ switch (type) {
+ case PROP_POINTER: {
+ /* resolve pointer if further path elements follow
+ * or explicitly requested
+ */
+ if (do_item_ptr || eval_pointer || *path != '\0') {
+ nextptr = RNA_property_pointer_get(&curptr, prop);
+ }
+
+ if (eval_pointer || *path != '\0') {
+ curptr = nextptr;
+ prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
+ index = -1;
+ }
+ break;
+ }
+ case PROP_COLLECTION: {
+ /* Resolve pointer if further path elements follow.
+ * Note that if path is empty, rna_path_parse_collection_key will do nothing anyway,
+ * so do_item_ptr is of no use in that case.
+ */
+ if (*path) {
+ if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) {
+ return false;
+ }
+
+ if (eval_pointer || *path != '\0') {
+ curptr = nextptr;
+ prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
+ index = -1;
+ }
+ }
+ break;
+ }
+ default:
+ if (r_index || prop_elem) {
+ if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) {
+ return false;
+ }
+
+ if (prop_elem) {
+ prop_elem->index = index;
+ }
+ }
+ break;
+ }
+ }
+
+ if (r_ptr) {
+ *r_ptr = curptr;
+ }
+ if (r_prop) {
+ *r_prop = prop;
+ }
+ if (r_index) {
+ *r_index = index;
+ }
+ if (r_item_ptr && do_item_ptr) {
+ *r_item_ptr = nextptr;
+ }
+
+ if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop ||
+ prop_elem->index != index)) {
+ prop_elem = MEM_cnew<PropertyElemRNA>(__func__);
+ prop_elem->ptr = curptr;
+ prop_elem->prop = prop;
+ prop_elem->index = index;
+ BLI_addtail(r_elements, prop_elem);
+ }
+
+ return true;
+}
+
+bool RNA_path_resolve(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true)) {
+ return false;
+ }
+
+ return r_ptr->data != NULL;
+}
+
+bool RNA_path_resolve_full(
+ const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true)) {
+ return false;
+ }
+
+ return r_ptr->data != NULL;
+}
+
+bool RNA_path_resolve_full_maybe_null(
+ const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
+{
+ return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true);
+}
+
+bool RNA_path_resolve_property(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, false)) {
+ return false;
+ }
+
+ return r_ptr->data != NULL && *r_prop != NULL;
+}
+
+bool RNA_path_resolve_property_full(
+ const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false)) {
+ return false;
+ }
+
+ return r_ptr->data != NULL && *r_prop != NULL;
+}
+
+bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ PointerRNA *r_item_ptr)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, r_item_ptr, NULL, false)) {
+ return false;
+ }
+
+ return r_ptr->data != NULL && *r_prop != NULL;
+}
+
+bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index,
+ PointerRNA *r_item_ptr)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, r_item_ptr, NULL, false)) {
+ return false;
+ }
+
+ return r_ptr->data != NULL && *r_prop != NULL;
+}
+bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
+{
+ return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false);
+}
+
+char *RNA_path_append(const char *path,
+ const PointerRNA *UNUSED(ptr),
+ PropertyRNA *prop,
+ int intkey,
+ const char *strkey)
+{
+ DynStr *dynstr;
+ char *result;
+
+ dynstr = BLI_dynstr_new();
+
+ /* add .identifier */
+ if (path) {
+ BLI_dynstr_append(dynstr, path);
+ if (*path) {
+ BLI_dynstr_append(dynstr, ".");
+ }
+ }
+
+ BLI_dynstr_append(dynstr, RNA_property_identifier(prop));
+
+ if (RNA_property_type(prop) == PROP_COLLECTION) {
+ /* add ["strkey"] or [intkey] */
+ BLI_dynstr_append(dynstr, "[");
+
+ if (strkey) {
+ const int strkey_esc_max_size = (strlen(strkey) * 2) + 1;
+ char *strkey_esc = BLI_array_alloca(strkey_esc, strkey_esc_max_size);
+ BLI_str_escape(strkey_esc, strkey, strkey_esc_max_size);
+ BLI_dynstr_append(dynstr, "\"");
+ BLI_dynstr_append(dynstr, strkey_esc);
+ BLI_dynstr_append(dynstr, "\"");
+ }
+ else {
+ char appendstr[128];
+ BLI_snprintf(appendstr, sizeof(appendstr), "%d", intkey);
+ BLI_dynstr_append(dynstr, appendstr);
+ }
+
+ BLI_dynstr_append(dynstr, "]");
+ }
+
+ result = BLI_dynstr_get_cstring(dynstr);
+ BLI_dynstr_free(dynstr);
+
+ return result;
+}
+
+/* Having both path append & back seems like it could be useful,
+ * this function isn't used at the moment. */
+static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path)
+{
+ char fixedbuf[256];
+ const char *previous, *current;
+ char *result;
+ int i;
+
+ if (!path) {
+ return NULL;
+ }
+
+ previous = NULL;
+ current = path;
+
+ /* parse token by token until the end, then we back up to the previous
+ * position and strip of the next token to get the path one step back */
+ while (*current) {
+ char *token;
+
+ token = rna_path_token(&current, fixedbuf, sizeof(fixedbuf));
+
+ if (!token) {
+ return NULL;
+ }
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ /* in case of collection we also need to strip off [] */
+ bool quoted;
+ token = rna_path_token_in_brackets(&current, fixedbuf, sizeof(fixedbuf), &quoted);
+ if (token && token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ if (!*current) {
+ break;
+ }
+
+ previous = current;
+ }
+
+ if (!previous) {
+ return NULL;
+ }
+
+ /* copy and strip off last token */
+ i = previous - path;
+ result = BLI_strdup(path);
+
+ if (i > 0 && result[i - 1] == '.') {
+ i--;
+ }
+ result[i] = 0;
+
+ return result;
+}
+
+const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop)
+{
+ if (array_prop != NULL) {
+ if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
+ BLI_assert(array_prop->arraydimension == 0);
+ return NULL;
+ }
+ if (array_prop->arraydimension == 0) {
+ return NULL;
+ }
+ }
+
+ /* Valid 'array part' of a rna path can only have '[', ']' and digit characters.
+ * It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */
+ off_t rna_path_len = (off_t)strlen(rna_path);
+ if (rna_path[rna_path_len] != ']') {
+ return NULL;
+ }
+ const char *last_valid_index_token_start = NULL;
+ for (rna_path_len--; rna_path_len >= 0; rna_path_len--) {
+ switch (rna_path[rna_path_len]) {
+ case '[':
+ if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') {
+ return &rna_path[rna_path_len];
+ }
+ last_valid_index_token_start = &rna_path[rna_path_len];
+ rna_path_len--;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ default:
+ return last_valid_index_token_start;
+ }
+ }
+ return last_valid_index_token_start;
+}
+
+/* generic path search func
+ * if its needed this could also reference the IDProperty direct */
+typedef struct IDP_Chain {
+ struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */
+
+ const char *name;
+ int index;
+
+} IDP_Chain;
+
+static char *rna_idp_path_create(IDP_Chain *child_link)
+{
+ DynStr *dynstr = BLI_dynstr_new();
+ char *path;
+ bool is_first = true;
+
+ int tot = 0;
+ IDP_Chain *link = child_link;
+
+ /* reverse the list */
+ IDP_Chain *link_prev;
+ link_prev = NULL;
+ while (link) {
+ IDP_Chain *link_next = link->up;
+ link->up = link_prev;
+ link_prev = link;
+ link = link_next;
+ tot++;
+ }
+
+ for (link = link_prev; link; link = link->up) {
+ /* pass */
+ if (link->index >= 0) {
+ BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index);
+ }
+ else {
+ BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name);
+ }
+
+ is_first = false;
+ }
+
+ path = BLI_dynstr_get_cstring(dynstr);
+ BLI_dynstr_free(dynstr);
+
+ if (*path == '\0') {
+ MEM_freeN(path);
+ path = NULL;
+ }
+
+ return path;
+}
+
+static char *rna_idp_path(PointerRNA *ptr,
+ IDProperty *haystack,
+ IDProperty *needle,
+ IDP_Chain *parent_link)
+{
+ char *path = NULL;
+ IDP_Chain link;
+
+ IDProperty *iter;
+ int i;
+
+ BLI_assert(haystack->type == IDP_GROUP);
+
+ link.up = parent_link;
+ /* Always set both name and index, else a stale value might get used. */
+ link.name = NULL;
+ link.index = -1;
+
+ for (i = 0, iter = reinterpret_cast<IDProperty *>(haystack->data.group.first); iter;
+ iter = iter->next, i++) {
+ if (needle == iter) { /* found! */
+ link.name = iter->name;
+ link.index = -1;
+ path = rna_idp_path_create(&link);
+ break;
+ }
+
+ /* Early out in case the IDProperty type cannot contain RNA properties. */
+ if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) {
+ continue;
+ }
+
+ /* Ensure this is RNA. */
+ /* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name
+ * collides with an actual fully static RNA property of the same struct (which would then not
+ * be flagged with `PROP_IDPROPERTY`).
+ *
+ * That case must be ignored here, we only want to deal with runtime RNA properties stored in
+ * IDProps.
+ *
+ * See T84091. */
+ PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
+ if (prop == NULL || (prop->flag & PROP_IDPROPERTY) == 0) {
+ continue;
+ }
+
+ if (iter->type == IDP_GROUP) {
+ if (prop->type == PROP_POINTER) {
+ PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
+ if (RNA_pointer_is_null(&child_ptr)) {
+ /* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL
+ * value is perfectly valid. Just means it won't match the searched needle. */
+ continue;
+ }
+
+ link.name = iter->name;
+ link.index = -1;
+ if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
+ break;
+ }
+ }
+ }
+ else if (iter->type == IDP_IDPARRAY) {
+ if (prop->type == PROP_COLLECTION) {
+ IDProperty *array = IDP_IDPArray(iter);
+ if (needle >= array && needle < (iter->len + array)) { /* found! */
+ link.name = iter->name;
+ link.index = (int)(needle - array);
+ path = rna_idp_path_create(&link);
+ break;
+ }
+ int j;
+ link.name = iter->name;
+ for (j = 0; j < iter->len; j++, array++) {
+ PointerRNA child_ptr;
+ if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) {
+ if (RNA_pointer_is_null(&child_ptr)) {
+ /* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case
+ * a NULL value is perfectly valid. Just means it won't match the searched needle. */
+ continue;
+ }
+ link.index = j;
+ if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
+ break;
+ }
+ }
+ }
+ if (path) {
+ break;
+ }
+ }
+ }
+ }
+
+ return path;
+}
+
+char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
+{
+ IDProperty *haystack = RNA_struct_idprops(ptr, false);
+
+ if (haystack) { /* can fail when called on bones */
+ return rna_idp_path(ptr, haystack, needle, NULL);
+ }
+ return NULL;
+}
+
+static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr)
+{
+ PointerRNA id_ptr;
+
+ BLI_assert(ptr->owner_id != NULL);
+
+ /* TODO: Support Bones/PoseBones. no pointers stored to the bones from here, only the ID.
+ * See example in T25746.
+ * Unless this is added only way to find this is to also search
+ * all bones and pose bones of an armature or object.
+ */
+ RNA_id_pointer_create(ptr->owner_id, &id_ptr);
+
+ return RNA_path_from_struct_to_idproperty(&id_ptr, reinterpret_cast<IDProperty *>(ptr->data));
+}
+
+ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path)
+{
+ if (r_path) {
+ *r_path = "";
+ }
+
+ if ((id == NULL) || (id->flag & LIB_EMBEDDED_DATA) == 0) {
+ return id;
+ }
+
+ const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
+ if (r_path) {
+ switch (GS(id->name)) {
+ case ID_NT:
+ *r_path = "node_tree";
+ break;
+ case ID_GR:
+ *r_path = "collection";
+ break;
+ default:
+ BLI_assert_msg(0, "Missing handling of embedded id type.");
+ }
+ }
+
+ if (id_type->owner_get == NULL) {
+ BLI_assert_msg(0, "Missing handling of embedded id type.");
+ return id;
+ }
+ return id_type->owner_get(bmain, id);
+}
+
+static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id)
+{
+ if (r_real_id != NULL) {
+ *r_real_id = NULL;
+ }
+
+ const char *prefix;
+ ID *real_id = RNA_find_real_ID_and_path(bmain, id, &prefix);
+
+ if (r_real_id != NULL) {
+ *r_real_id = real_id;
+ }
+
+ if (path != NULL) {
+ char *new_path = NULL;
+
+ if (real_id) {
+ if (prefix[0]) {
+ new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path);
+ }
+ else {
+ return path;
+ }
+ }
+
+ MEM_freeN(path);
+ return new_path;
+ }
+ return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL;
+}
+
+char *RNA_path_from_ID_to_struct(const PointerRNA *ptr)
+{
+ char *ptrpath = NULL;
+
+ if (!ptr->owner_id || !ptr->data) {
+ return NULL;
+ }
+
+ if (!RNA_struct_is_ID(ptr->type)) {
+ if (ptr->type->path) {
+ /* if type has a path to some ID, use it */
+ ptrpath = ptr->type->path((PointerRNA *)ptr);
+ }
+ else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
+ PointerRNA parentptr;
+ PropertyRNA *userprop;
+
+ /* find the property in the struct we're nested in that references this struct, and
+ * use its identifier as the first part of the path used...
+ */
+ RNA_id_pointer_create(ptr->owner_id, &parentptr);
+ userprop = rna_struct_find_nested(&parentptr, ptr->type);
+
+ if (userprop) {
+ ptrpath = BLI_strdup(RNA_property_identifier(userprop));
+ }
+ else {
+ return NULL; /* can't do anything about this case yet... */
+ }
+ }
+ else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
+ /* special case, easier to deal with here than in ptr->type->path() */
+ return rna_path_from_ID_to_idpgroup(ptr);
+ }
+ else {
+ return NULL;
+ }
+ }
+
+ return ptrpath;
+}
+
+char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real)
+{
+ char *path = RNA_path_from_ID_to_struct(ptr);
+
+ /* NULL path is valid in that case, when given struct is an ID one... */
+ return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real);
+}
+
+static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY_LENGTH],
+ const int totdims,
+ const int index_dim,
+ int index,
+ int r_index_multi[RNA_MAX_ARRAY_LENGTH])
+{
+ int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1];
+ int i = totdims - 1;
+ dimsize_step[i + 1] = 1;
+ dimsize_step[i] = dimsize[i];
+ while (--i != -1) {
+ dimsize_step[i] = dimsize[i] * dimsize_step[i + 1];
+ }
+ while (++i != index_dim) {
+ int index_round = index / dimsize_step[i + 1];
+ r_index_multi[i] = index_round;
+ index -= (index_round * dimsize_step[i + 1]);
+ }
+ BLI_assert(index == 0);
+}
+
+static void rna_path_array_multi_string_from_flat_index(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index,
+ char *index_str,
+ int index_str_len)
+{
+ int dimsize[RNA_MAX_ARRAY_LENGTH];
+ int totdims = RNA_property_array_dimension(ptr, prop, dimsize);
+ int index_multi[RNA_MAX_ARRAY_LENGTH];
+
+ rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi);
+
+ for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) {
+ offset += BLI_snprintf_rlen(
+ &index_str[offset], index_str_len - offset, "[%d]", index_multi[i]);
+ }
+}
+
+char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index)
+{
+ const bool is_rna = (prop->magic == RNA_MAGIC);
+ const char *propname;
+ char *ptrpath, *path;
+
+ if (!ptr->owner_id || !ptr->data) {
+ return NULL;
+ }
+
+ /* path from ID to the struct holding this property */
+ ptrpath = RNA_path_from_ID_to_struct(ptr);
+
+ propname = RNA_property_identifier(prop);
+
+ /* support indexing w/ multi-dimensional arrays */
+ char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1];
+ if (index_dim == 0) {
+ index_str[0] = '\0';
+ }
+ else {
+ rna_path_array_multi_string_from_flat_index(
+ ptr, prop, index_dim, index, index_str, sizeof(index_str));
+ }
+
+ if (ptrpath) {
+ if (is_rna) {
+ path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str);
+ }
+ else {
+ char propname_esc[MAX_IDPROP_NAME * 2];
+ BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
+ path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str);
+ }
+ MEM_freeN(ptrpath);
+ }
+ else if (RNA_struct_is_ID(ptr->type)) {
+ if (is_rna) {
+ path = BLI_sprintfN("%s%s", propname, index_str);
+ }
+ else {
+ char propname_esc[MAX_IDPROP_NAME * 2];
+ BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
+ path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str);
+ }
+ }
+ else {
+ path = NULL;
+ }
+
+ return path;
+}
+
+char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
+{
+ return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1);
+}
+
+char *RNA_path_from_real_ID_to_property_index(Main *bmain,
+ const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index,
+ ID **r_real_id)
+{
+ char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index);
+
+ /* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part
+ * of the path either. */
+ return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL;
+}
+
+char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ const StructRNA *type)
+{
+ /* Try to recursively find an "type"'d ancestor,
+ * to handle situations where path from ID is not enough. */
+ PointerRNA idptr;
+ ListBase path_elems = {NULL};
+ char *path = NULL;
+ char *full_path = RNA_path_from_ID_to_property(ptr, prop);
+
+ if (full_path == NULL) {
+ return NULL;
+ }
+
+ RNA_id_pointer_create(ptr->owner_id, &idptr);
+
+ if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) {
+ LISTBASE_FOREACH_BACKWARD (PropertyElemRNA *, prop_elem, &path_elems) {
+ if (RNA_struct_is_a(prop_elem->ptr.type, type)) {
+ char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr);
+ if (ref_path) {
+ path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */
+ MEM_freeN(ref_path);
+ }
+ break;
+ }
+ }
+
+ BLI_freelistN(&path_elems);
+ }
+
+ MEM_freeN(full_path);
+ return path;
+}
+
+char *RNA_path_full_ID_py(Main *bmain, ID *id)
+{
+ const char *path;
+ ID *id_real = RNA_find_real_ID_and_path(bmain, id, &path);
+
+ if (id_real) {
+ id = id_real;
+ }
+ else {
+ path = "";
+ }
+
+ char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4];
+ if (ID_IS_LINKED(id)) {
+ int ofs = 0;
+ memcpy(lib_filepath_esc, ", \"", 3);
+ ofs += 3;
+ ofs += BLI_str_escape(lib_filepath_esc + ofs, id->lib->filepath, sizeof(lib_filepath_esc));
+ memcpy(lib_filepath_esc + ofs, "\"", 2);
+ }
+ else {
+ lib_filepath_esc[0] = '\0';
+ }
+
+ char id_esc[(sizeof(id->name) - 2) * 2];
+ BLI_str_escape(id_esc, id->name + 2, sizeof(id_esc));
+
+ return BLI_sprintfN("bpy.data.%s[\"%s\"%s]%s%s",
+ BKE_idtype_idcode_to_name_plural(GS(id->name)),
+ id_esc,
+ lib_filepath_esc,
+ path[0] ? "." : "",
+ path);
+}
+
+char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr)
+{
+ char *id_path;
+ char *data_path;
+
+ char *ret;
+
+ if (!ptr->owner_id) {
+ return NULL;
+ }
+
+ /* never fails */
+ id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
+
+ data_path = RNA_path_from_ID_to_struct(ptr);
+
+ /* XXX data_path may be NULL (see T36788),
+ * do we want to get the 'bpy.data.foo["bar"].(null)' stuff? */
+ ret = BLI_sprintfN("%s.%s", id_path, data_path);
+
+ if (data_path) {
+ MEM_freeN(data_path);
+ }
+ MEM_freeN(id_path);
+
+ return ret;
+}
+
+char *RNA_path_full_property_py_ex(
+ Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback)
+{
+ char *id_path;
+ const char *data_delim;
+ const char *data_path;
+ bool data_path_free;
+
+ char *ret;
+
+ if (!ptr->owner_id) {
+ return NULL;
+ }
+
+ /* never fails */
+ id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
+
+ data_path = RNA_path_from_ID_to_property(ptr, prop);
+ if (data_path) {
+ data_delim = (data_path[0] == '[') ? "" : ".";
+ data_path_free = true;
+ }
+ else {
+ if (use_fallback) {
+ /* Fuzzy fallback. Be explicit in our ignorance. */
+ data_path = RNA_property_identifier(prop);
+ data_delim = " ... ";
+ }
+ else {
+ data_delim = ".";
+ }
+ data_path_free = false;
+ }
+
+ if ((index == -1) || (RNA_property_array_check(prop) == false)) {
+ ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path);
+ }
+ else {
+ ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index);
+ }
+ MEM_freeN(id_path);
+ if (data_path_free) {
+ MEM_freeN((void *)data_path);
+ }
+
+ return ret;
+}
+
+char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index)
+{
+ return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false);
+}
+
+char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
+{
+ char *data_path;
+
+ char *ret;
+
+ if (!ptr->owner_id) {
+ return NULL;
+ }
+
+ data_path = RNA_path_from_ID_to_property(ptr, prop);
+
+ if (data_path == NULL) {
+ /* This may not be an ID at all, check for simple when pointer owns property.
+ * TODO: more complex nested case. */
+ if (!RNA_struct_is_ID(ptr->type)) {
+ const char *prop_identifier = RNA_property_identifier(prop);
+ if (RNA_struct_find_property(ptr, prop_identifier) == prop) {
+ data_path = BLI_strdup(prop_identifier);
+ }
+ }
+ }
+
+ if ((index == -1) || (RNA_property_array_check(prop) == false)) {
+ ret = BLI_strdup(data_path);
+ }
+ else {
+ ret = BLI_sprintfN("%s[%d]", data_path, index);
+ }
+
+ if (data_path) {
+ MEM_freeN(data_path);
+ }
+
+ return ret;
+}
+
+char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
+{
+ const bool is_rna = (prop->magic == RNA_MAGIC);
+ const char *propname = RNA_property_identifier(prop);
+ char *ret;
+
+ if ((index == -1) || (RNA_property_array_check(prop) == false)) {
+ if (is_rna) {
+ ret = BLI_strdup(propname);
+ }
+ else {
+ char propname_esc[MAX_IDPROP_NAME * 2];
+ BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
+ ret = BLI_sprintfN("[\"%s\"]", propname_esc);
+ }
+ }
+ else {
+ if (is_rna) {
+ ret = BLI_sprintfN("%s[%d]", propname, index);
+ }
+ else {
+ char propname_esc[MAX_IDPROP_NAME * 2];
+ BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
+ ret = BLI_sprintfN("[\"%s\"][%d]", propname_esc, index);
+ }
+ }
+
+ return ret;
+}