/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ #include #include #include "BLI_dynstr.h" #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_utildefines.h" #include "DNA_ID.h" #include "BKE_idprop.h" #include "BKE_idtype.h" #include "MEM_guardedalloc.h" #include "BLI_strict_flags.h" /* -------------------------------------------------------------------- */ /** \name IDProp Repr * * Convert an IDProperty to a string. * * Output should be a valid Python literal * (with minor exceptions - float nan for eg). * \{ */ struct ReprState { void (*str_append_fn)(void *user_data, const char *str, uint str_len); void *user_data; /* Big enough to format any primitive type. */ char buf[128]; }; static void idp_str_append_escape(struct ReprState *state, const char *str, const uint str_len, bool quote) { if (quote) { state->str_append_fn(state->user_data, "\"", 1); } uint i_prev = 0, i = 0; while (i < str_len) { const char c = str[i]; if (c == '"') { if (i_prev != i) { state->str_append_fn(state->user_data, str + i_prev, i - i_prev); } state->str_append_fn(state->user_data, "\\\"", 2); i_prev = i + 1; } else if (c == '\\') { if (i_prev != i) { state->str_append_fn(state->user_data, str + i_prev, i - i_prev); } state->str_append_fn(state->user_data, "\\\\", 2); i_prev = i + 1; } else if (c < 32) { if (i_prev != i) { state->str_append_fn(state->user_data, str + i_prev, i - i_prev); } char buf[5]; uint len = (uint)BLI_snprintf_rlen(buf, sizeof(buf), "\\x%02x", c); BLI_assert(len == 4); state->str_append_fn(state->user_data, buf, len); i_prev = i + 1; } i++; } state->str_append_fn(state->user_data, str + i_prev, i - i_prev); if (quote) { state->str_append_fn(state->user_data, "\"", 1); } } static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *prop) { /* NOTE: 'strlen' will be calculated at compile time for literals. */ #define STR_APPEND_STR(str) state->str_append_fn(state->user_data, str, (uint)strlen(str)) #define STR_APPEND_STR_QUOTE(str) idp_str_append_escape(state, str, (uint)strlen(str), true) #define STR_APPEND_STR_LEN_QUOTE(str, str_len) idp_str_append_escape(state, str, str_len, true) #define STR_APPEND_FMT(format, ...) \ state->str_append_fn( \ state->user_data, \ state->buf, \ (uint)BLI_snprintf_rlen(state->buf, sizeof(state->buf), format, __VA_ARGS__)) switch (prop->type) { case IDP_STRING: { STR_APPEND_STR_LEN_QUOTE(IDP_String(prop), (uint)MAX2(0, prop->len - 1)); break; } case IDP_INT: { STR_APPEND_FMT("%d", IDP_Int(prop)); break; } case IDP_FLOAT: { STR_APPEND_FMT("%g", (double)IDP_Float(prop)); break; } case IDP_DOUBLE: { STR_APPEND_FMT("%g", IDP_Double(prop)); break; } case IDP_ARRAY: { STR_APPEND_STR("["); switch (prop->subtype) { case IDP_INT: for (const int *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) { if (v != prop->data.pointer) { STR_APPEND_STR(", "); } STR_APPEND_FMT("%d", *v); } break; case IDP_FLOAT: for (const float *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) { if (v != prop->data.pointer) { STR_APPEND_STR(", "); } STR_APPEND_FMT("%g", (double)*v); } break; case IDP_DOUBLE: for (const double *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) { if (v != prop->data.pointer) { STR_APPEND_STR(", "); } STR_APPEND_FMT("%g", *v); } break; } STR_APPEND_STR("]"); break; } case IDP_IDPARRAY: { STR_APPEND_STR("["); for (const IDProperty *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) { if (v != prop->data.pointer) { STR_APPEND_STR(", "); } idp_repr_fn_recursive(state, v); } STR_APPEND_STR("]"); break; } case IDP_GROUP: { STR_APPEND_STR("{"); LISTBASE_FOREACH (const IDProperty *, subprop, &prop->data.group) { if (subprop != prop->data.group.first) { STR_APPEND_STR(", "); } STR_APPEND_STR_QUOTE(subprop->name); STR_APPEND_STR(": "); idp_repr_fn_recursive(state, subprop); } STR_APPEND_STR("}"); break; } case IDP_ID: { const ID *id = prop->data.pointer; if (id != NULL) { STR_APPEND_STR("bpy.data."); STR_APPEND_STR(BKE_idtype_idcode_to_name_plural(GS(id->name))); STR_APPEND_STR("["); STR_APPEND_STR_QUOTE(id->name + 2); STR_APPEND_STR("]"); } else { STR_APPEND_STR("None"); } break; } default: { BLI_assert_unreachable(); break; } } #undef STR_APPEND_STR #undef STR_APPEND_STR_QUOTE #undef STR_APPEND_STR_LEN_QUOTE #undef STR_APPEND_FMT } void IDP_repr_fn(const IDProperty *prop, void (*str_append_fn)(void *user_data, const char *str, uint str_len), void *user_data) { struct ReprState state = { .str_append_fn = str_append_fn, .user_data = user_data, }; idp_repr_fn_recursive(&state, prop); } static void repr_str(void *user_data, const char *str, uint len) { BLI_dynstr_nappend(user_data, str, (int)len); } char *IDP_reprN(const IDProperty *prop, uint *r_len) { DynStr *ds = BLI_dynstr_new(); IDP_repr_fn(prop, repr_str, ds); char *cstring = BLI_dynstr_get_cstring(ds); if (r_len != NULL) { *r_len = (uint)BLI_dynstr_get_len(ds); } BLI_dynstr_free(ds); return cstring; } void IDP_print(const IDProperty *prop) { char *repr = IDP_reprN(prop, NULL); printf("IDProperty(%p): ", prop); puts(repr); MEM_freeN(repr); } /** \} */