diff --git a/distr/flecs.c b/distr/flecs.c index 398d8ce99..4100f0c23 100644 --- a/distr/flecs.c +++ b/distr/flecs.c @@ -10343,7 +10343,28 @@ bool flecs_path_append( } if (name) { - ecs_strbuf_appendstrn(buf, name, name_len); + /* Check if we need to escape separator character */ + const char *sep_in_name = NULL; + if (!sep[1]) { + sep_in_name = strchr(name, sep[0]); + } + + if (sep_in_name) { + const char *name_ptr = name; + while (sep_in_name) { + ecs_size_t len = sep_in_name - name_ptr; + ecs_strbuf_appendstrn(buf, name_ptr, len); + ecs_strbuf_appendch(buf, '\\'); + ecs_strbuf_appendch(buf, sep[0]); + + name_ptr = sep_in_name + 1; + sep_in_name = strchr(name_ptr, sep[0]); + } + + ecs_strbuf_appendstr(buf, name_ptr); + } else { + ecs_strbuf_appendstrn(buf, name, name_len); + } } else { ecs_strbuf_appendch(buf, '#'); ecs_strbuf_appendint(buf, flecs_uto(int64_t, (uint32_t)child)); diff --git a/src/entity_name.c b/src/entity_name.c index 2a451175a..5879ff03d 100644 --- a/src/entity_name.c +++ b/src/entity_name.c @@ -61,7 +61,28 @@ bool flecs_path_append( } if (name) { - ecs_strbuf_appendstrn(buf, name, name_len); + /* Check if we need to escape separator character */ + const char *sep_in_name = NULL; + if (!sep[1]) { + sep_in_name = strchr(name, sep[0]); + } + + if (sep_in_name) { + const char *name_ptr = name; + while (sep_in_name) { + ecs_size_t len = sep_in_name - name_ptr; + ecs_strbuf_appendstrn(buf, name_ptr, len); + ecs_strbuf_appendch(buf, '\\'); + ecs_strbuf_appendch(buf, sep[0]); + + name_ptr = sep_in_name + 1; + sep_in_name = strchr(name_ptr, sep[0]); + } + + ecs_strbuf_appendstr(buf, name_ptr); + } else { + ecs_strbuf_appendstrn(buf, name, name_len); + } } else { ecs_strbuf_appendch(buf, '#'); ecs_strbuf_appendint(buf, flecs_uto(int64_t, (uint32_t)child)); diff --git a/test/core/project.json b/test/core/project.json index 0408970b9..13a28b824 100644 --- a/test/core/project.json +++ b/test/core/project.json @@ -627,6 +627,16 @@ "path_custom_prefix", "path_prefix_rel_match", "path_prefix_rel_no_match", + "path_escaped_sep", + "path_escaped_two_sep", + "path_escaped_two_consecutive_sep", + "path_escaped_sep_at_begin", + "path_escaped_sep_at_end", + "path_escaped_sep_w_parent", + "path_only_escaped_sep", + "path_only_escaped_sep_w_parent", + "path_only_escaped_two_sep", + "path_only_escaped_two_sep_w_parent", "fullpath_for_core", "path_w_number", "path_w_entity_id", diff --git a/test/core/src/Hierarchies.c b/test/core/src/Hierarchies.c index 83ec74d3a..7f7cb3924 100644 --- a/test/core/src/Hierarchies.c +++ b/test/core/src/Hierarchies.c @@ -381,6 +381,177 @@ void Hierarchies_path_w_entity_id(void) { ecs_fini(world); } +void Hierarchies_path_escaped_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "foo\\.bar"}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "foo.bar"); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "foo\\.bar") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "foo\\.bar"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_two_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "foo\\.bar\\.woo"}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "foo.bar.woo"); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "foo\\.bar\\.woo") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "foo\\.bar\\.woo"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_two_consecutive_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "foo\\.\\.bar\\.woo"}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "foo..bar.woo"); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "foo\\.\\.bar\\.woo") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "foo\\.\\.bar\\.woo"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_sep_at_begin(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "\\.foo\\.bar"}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), ".foo.bar"); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "\\.foo\\.bar") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "\\.foo\\.bar"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_sep_at_end(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "foo\\.bar\\."}); + test_assert(e != 0); + test_str(ecs_get_name(world, e), "foo.bar."); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "foo\\.bar\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "foo\\.bar\\."); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_escaped_sep_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "parent.foo\\.bar"}); + test_assert(e != 0); + ecs_entity_t p = ecs_lookup(world, "parent"); + test_assert(p != 0); + + test_str(ecs_get_name(world, e), "foo.bar"); + test_assert(ecs_get_parent(world, e) == p); + test_assert(ecs_lookup(world, "parent.foo\\.bar") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "parent.foo\\.bar"); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_only_escaped_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "\\."}); + test_assert(e != 0); + + test_str(ecs_get_name(world, e), "."); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "\\."); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_only_escaped_sep_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "parent.\\."}); + test_assert(e != 0); + ecs_entity_t p = ecs_lookup(world, "parent"); + test_assert(p != 0); + + test_str(ecs_get_name(world, e), "."); + test_assert(ecs_get_parent(world, e) == p); + test_assert(ecs_lookup(world, "parent.\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "parent.\\."); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_only_escaped_two_sep(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "\\.\\."}); + test_assert(e != 0); + + test_str(ecs_get_name(world, e), ".."); + test_assert(ecs_get_parent(world, e) == 0); + test_assert(ecs_lookup(world, "\\.\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "\\.\\."); + ecs_os_free(path); + + ecs_fini(world); +} + +void Hierarchies_path_only_escaped_two_sep_w_parent(void) { + ecs_world_t *world = ecs_mini(); + + ecs_entity_t e = ecs_entity(world, { .name = "parent.\\.\\."}); + test_assert(e != 0); + ecs_entity_t p = ecs_lookup(world, "parent"); + test_assert(p != 0); + + test_str(ecs_get_name(world, e), ".."); + test_assert(ecs_get_parent(world, e) == p); + test_assert(ecs_lookup(world, "parent.\\.\\.") == e); + + char *path = ecs_get_path(world, e); + test_str(path, "parent.\\.\\."); + ecs_os_free(path); + + ecs_fini(world); +} + void Hierarchies_lookup_depth_0(void) { ecs_world_t *world = ecs_mini(); diff --git a/test/core/src/main.c b/test/core/src/main.c index 36e7ac222..4984b6bc9 100644 --- a/test/core/src/main.c +++ b/test/core/src/main.c @@ -596,6 +596,16 @@ void Hierarchies_path_custom_sep(void); void Hierarchies_path_custom_prefix(void); void Hierarchies_path_prefix_rel_match(void); void Hierarchies_path_prefix_rel_no_match(void); +void Hierarchies_path_escaped_sep(void); +void Hierarchies_path_escaped_two_sep(void); +void Hierarchies_path_escaped_two_consecutive_sep(void); +void Hierarchies_path_escaped_sep_at_begin(void); +void Hierarchies_path_escaped_sep_at_end(void); +void Hierarchies_path_escaped_sep_w_parent(void); +void Hierarchies_path_only_escaped_sep(void); +void Hierarchies_path_only_escaped_sep_w_parent(void); +void Hierarchies_path_only_escaped_two_sep(void); +void Hierarchies_path_only_escaped_two_sep_w_parent(void); void Hierarchies_fullpath_for_core(void); void Hierarchies_path_w_number(void); void Hierarchies_path_w_entity_id(void); @@ -4550,6 +4560,46 @@ bake_test_case Hierarchies_testcases[] = { "path_prefix_rel_no_match", Hierarchies_path_prefix_rel_no_match }, + { + "path_escaped_sep", + Hierarchies_path_escaped_sep + }, + { + "path_escaped_two_sep", + Hierarchies_path_escaped_two_sep + }, + { + "path_escaped_two_consecutive_sep", + Hierarchies_path_escaped_two_consecutive_sep + }, + { + "path_escaped_sep_at_begin", + Hierarchies_path_escaped_sep_at_begin + }, + { + "path_escaped_sep_at_end", + Hierarchies_path_escaped_sep_at_end + }, + { + "path_escaped_sep_w_parent", + Hierarchies_path_escaped_sep_w_parent + }, + { + "path_only_escaped_sep", + Hierarchies_path_only_escaped_sep + }, + { + "path_only_escaped_sep_w_parent", + Hierarchies_path_only_escaped_sep_w_parent + }, + { + "path_only_escaped_two_sep", + Hierarchies_path_only_escaped_two_sep + }, + { + "path_only_escaped_two_sep_w_parent", + Hierarchies_path_only_escaped_two_sep_w_parent + }, { "fullpath_for_core", Hierarchies_fullpath_for_core @@ -11145,7 +11195,7 @@ static bake_test_suite suites[] = { "Hierarchies", Hierarchies_setup, NULL, - 95, + 105, Hierarchies_testcases }, {