Fix T101803: Max length Operator bl_idname is truncated 1 character.

There were quite a few issues here:
* Bad usage of nagic number leading to confusing code
* Forgetting to take into accoun final `NULL` char
* RNA code thinkin `bl_idname` is python version, when it is actually
  BL/C version.
This commit is contained in:
Bastien Montagne 2022-10-14 18:35:28 +02:00
parent 4112f0241b
commit 9e77f5f17f
Notes: blender-bot 2023-02-14 11:21:40 +01:00
Referenced by issue #101803, Max length Operator bl_idname is truncated 1 character
3 changed files with 40 additions and 31 deletions

View File

@ -364,7 +364,7 @@ typedef struct wmOperatorTypeMacro {
struct wmOperatorTypeMacro *next, *prev;
/* operator id */
char idname[64];
char idname[64]; /* OP_MAX_TYPENAME */
/* rna pointer to access properties, like keymap */
/** Operator properties, assigned to ptr->data and can be written to a file. */
struct IDProperty *properties;
@ -551,7 +551,7 @@ typedef struct wmOperator {
/* saved */
/** Used to retrieve type pointer. */
char idname[64];
char idname[64]; /* OP_MAX_TYPENAME */
/** Saved, user-settable properties. */
IDProperty *properties;

View File

@ -1644,12 +1644,7 @@ static StructRNA *rna_MacroOperator_register(Main *bmain,
return NULL;
}
if (strlen(identifier) >= sizeof(dummyop.idname)) {
BKE_reportf(reports,
RPT_ERROR,
"Registering operator class: '%s' is too long, maximum length is %d",
identifier,
(int)sizeof(dummyop.idname));
if (!WM_operator_py_idname_ok_or_report(reports, identifier, dummyot.idname)) {
return NULL;
}
@ -1661,10 +1656,6 @@ static StructRNA *rna_MacroOperator_register(Main *bmain,
}
}
if (!WM_operator_py_idname_ok_or_report(reports, identifier, dummyot.idname)) {
return NULL;
}
char idname_conv[sizeof(dummyop.idname)];
WM_operator_bl_idname(idname_conv, dummyot.idname); /* convert the idname from python */
@ -1867,8 +1858,9 @@ static void rna_def_operator_common(StructRNA *srna)
/* Registration */
prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "type->idname");
/* Without setting the length the pointer size would be used. -3 because `.` -> `_OT_`. */
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME - 3);
/* String stored here is the 'BL' identifier (`OPMODULE_OT_my_op`),
* not the 'python' identifier (`opmodule.my_op`). */
RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set");
// RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_flag(prop, PROP_REGISTER);

View File

@ -107,19 +107,32 @@
/** \name Operator API
* \{ */
#define OP_BL_SEP_STRING "_OT_"
#define OP_BL_SEP_LEN 4
#define OP_PY_SEP_CHAR '.'
#define OP_PY_SEP_LEN 1
/* Difference between python 'identifier' and BL/C code one ("." separator replaced by "_OT_"),
* and final `\0` char. */
#define OP_MAX_PY_IDNAME (OP_MAX_TYPENAME - OP_BL_SEP_LEN + OP_PY_SEP_LEN - 1)
size_t WM_operator_py_idname(char *dst, const char *src)
{
const char *sep = strstr(src, "_OT_");
const char *sep = strstr(src, OP_BL_SEP_STRING);
if (sep) {
int ofs = (sep - src);
const size_t sep_offset = (size_t)(sep - src);
/* NOTE: we use ascii `tolower` instead of system `tolower`, because the
* latter depends on the locale, and can lead to `idname` mismatch. */
memcpy(dst, src, sizeof(char) * ofs);
BLI_str_tolower_ascii(dst, ofs);
memcpy(dst, src, sep_offset);
BLI_str_tolower_ascii(dst, sep_offset);
dst[ofs] = '.';
return BLI_strncpy_rlen(dst + (ofs + 1), sep + 4, OP_MAX_TYPENAME - (ofs + 1)) + (ofs + 1);
dst[sep_offset] = OP_PY_SEP_CHAR;
return BLI_strncpy_rlen(dst + (sep_offset + OP_PY_SEP_LEN),
sep + OP_BL_SEP_LEN,
OP_MAX_TYPENAME - sep_offset - OP_PY_SEP_LEN) +
(sep_offset + OP_PY_SEP_LEN);
}
/* Should not happen but support just in case. */
return BLI_strncpy_rlen(dst, src, OP_MAX_TYPENAME);
@ -127,15 +140,19 @@ size_t WM_operator_py_idname(char *dst, const char *src)
size_t WM_operator_bl_idname(char *dst, const char *src)
{
const char *sep = strchr(src, '.');
int from_len;
if (sep && (from_len = strlen(src)) < OP_MAX_TYPENAME - 3) {
const int ofs = (sep - src);
memcpy(dst, src, sizeof(char) * ofs);
BLI_str_toupper_ascii(dst, ofs);
memcpy(dst + ofs, "_OT_", 4);
memcpy(dst + (ofs + 4), sep + 1, (from_len - ofs));
return (from_len - ofs) - 1;
const size_t from_len = (size_t)strlen(src);
const char *sep = strchr(src, OP_PY_SEP_CHAR);
if (sep && (from_len <= OP_MAX_PY_IDNAME)) {
const size_t sep_offset = (size_t)(sep - src);
memcpy(dst, src, sep_offset);
BLI_str_toupper_ascii(dst, sep_offset);
memcpy(dst + sep_offset, OP_BL_SEP_STRING, OP_BL_SEP_LEN);
BLI_strncpy(dst + sep_offset + OP_BL_SEP_LEN,
sep + OP_PY_SEP_LEN,
from_len - sep_offset - OP_PY_SEP_LEN + 1);
return from_len + OP_BL_SEP_LEN - OP_PY_SEP_LEN;
}
/* Should not happen but support just in case. */
return BLI_strncpy_rlen(dst, src, OP_MAX_TYPENAME);
@ -166,14 +183,14 @@ bool WM_operator_py_idname_ok_or_report(ReportList *reports,
}
}
if (i > (MAX_NAME - 3)) {
if (i > OP_MAX_PY_IDNAME) {
BKE_reportf(reports,
RPT_ERROR,
"Registering operator class: '%s', invalid bl_idname '%s', "
"is too long, maximum length is %d",
classname,
idname,
MAX_NAME - 3);
OP_MAX_PY_IDNAME);
return false;
}