diff --git a/rcl/include/rcl/lexer_lookahead.h b/rcl/include/rcl/lexer_lookahead.h index d50166e3f..142c69b8b 100644 --- a/rcl/include/rcl/lexer_lookahead.h +++ b/rcl/include/rcl/lexer_lookahead.h @@ -170,6 +170,28 @@ rcl_lexer_lookahead2_peek2( rcl_lexeme_t * next_type1, rcl_lexeme_t * next_type2); +/// Look ahead to check if buffer contains colon prefix. +/** + *
+ * Attribute | Adherence + * ------------------ | ------------- + * Allocates Memory | Yes [1] + * Thread-Safe | No + * Uses Atomics | No + * Lock-Free | Yes + * [1] Only allocates if an argument is invalid or an internal bug is detected. + * + * \param[in] buffer the lookahead2 buffer being used to analyze a string. + * \return `RCL_RET_OK` if peeking was successfull, or + * \return `RCL_RET_INVALID_ARGUMENT` if any function arguments are invalid, or + * \return `RCL_RET_ERROR` if an unspecified error occurs. + */ +RCL_PUBLIC +RCL_WARN_UNUSED +rcl_ret_t +rcl_lexer_lookahead2_peek_colon_prefix( + rcl_lexer_lookahead2_t * buffer); + /// Accept a lexeme and advance analysis. /** * A token must have been peeked before it can be accepted. diff --git a/rcl/src/rcl/arguments.c b/rcl/src/rcl/arguments.c index 58a74f1e5..aaf0d5a76 100644 --- a/rcl/src/rcl/arguments.c +++ b/rcl/src/rcl/arguments.c @@ -1504,7 +1504,7 @@ _rcl_parse_remap_nodename_replacement( return RCL_RET_OK; } -/// Parse a nodename prefix including trailing colon (ex: `node_name:`). +/// Parse a nodename prefix including trailing colon (ex: `node_name:` or `/ns/node_name:`). RCL_LOCAL rcl_ret_t _rcl_parse_nodename_prefix( @@ -1512,28 +1512,66 @@ _rcl_parse_nodename_prefix( rcl_allocator_t allocator, char ** node_name) { - size_t length = 0; - const char * token = NULL; - // Check arguments sanity assert(NULL != lex_lookahead); assert(rcutils_allocator_is_valid(&allocator)); assert(NULL != node_name); assert(NULL == *node_name); - // Expect a token and a colon - rcl_ret_t ret = - rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_TOKEN, &token, &length); + rcl_ret_t ret; + const char * name_start = rcl_lexer_lookahead2_get_text(lex_lookahead); + if (NULL == name_start) { + RCL_SET_ERROR_MSG("failed to get start of node name"); + return RCL_RET_ERROR; + } + + rcl_lexeme_t next_type; + ret = rcl_lexer_lookahead2_peek(lex_lookahead, &next_type); if (RCL_RET_OK != ret) { return ret; } + + if (RCL_LEXEME_FORWARD_SLASH == next_type) { + // repeated slashes and tokens until a colon + do { + ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_FORWARD_SLASH, NULL, NULL); + if (RCL_RET_WRONG_LEXEME == ret) { + rcl_reset_error(); + break; + } + + ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_TOKEN, NULL, NULL); + if (RCL_RET_WRONG_LEXEME == ret) { + if (RCL_RET_OK == ret) { + rcl_reset_error(); + break; + } + } + + ret = rcl_lexer_lookahead2_peek(lex_lookahead, &next_type); + if (RCL_RET_OK != ret) { + return ret; + } + if (RCL_LEXEME_COLON == next_type) { + break; + } + } while (true); + } else { + ret = + rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_TOKEN, NULL, NULL); + if (RCL_RET_OK != ret) { + return ret; + } + } + + const char * name_end = rcl_lexer_lookahead2_get_text(lex_lookahead); ret = rcl_lexer_lookahead2_expect(lex_lookahead, RCL_LEXEME_COLON, NULL, NULL); if (RCL_RET_OK != ret) { return ret; } - + const size_t length = (size_t)(name_end - name_start); // Copy the node name - *node_name = rcutils_strndup(token, length, allocator); + *node_name = rcutils_strndup(name_start, length, allocator); if (NULL == *node_name) { RCL_SET_ERROR_MSG("failed to allocate node name"); return RCL_RET_BAD_ALLOC; @@ -1582,18 +1620,14 @@ _rcl_parse_remap_begin_remap_rule( { rcl_ret_t ret; rcl_lexeme_t lexeme1; - rcl_lexeme_t lexeme2; // Check arguments sanity assert(NULL != lex_lookahead); assert(NULL != rule); // Check for optional nodename prefix - ret = rcl_lexer_lookahead2_peek2(lex_lookahead, &lexeme1, &lexeme2); - if (RCL_RET_OK != ret) { - return ret; - } - if (RCL_LEXEME_TOKEN == lexeme1 && RCL_LEXEME_COLON == lexeme2) { + ret = rcl_lexer_lookahead2_peek_colon_prefix(lex_lookahead); + if (RCL_RET_OK == ret) { ret = _rcl_parse_remap_nodename_prefix(lex_lookahead, rule); if (RCL_RET_OK != ret) { return ret; diff --git a/rcl/src/rcl/lexer_lookahead.c b/rcl/src/rcl/lexer_lookahead.c index a5b258957..8324d5ed9 100644 --- a/rcl/src/rcl/lexer_lookahead.c +++ b/rcl/src/rcl/lexer_lookahead.c @@ -162,6 +162,42 @@ rcl_lexer_lookahead2_peek2( return RCL_RET_OK; } +rcl_ret_t +rcl_lexer_lookahead2_peek_colon_prefix( + rcl_lexer_lookahead2_t * buffer) +{ + RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT); + + RCL_CHECK_ARGUMENT_FOR_NULL(buffer, RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_FOR_NULL_WITH_MSG( + buffer->impl, "buffer not initialized", return RCL_RET_INVALID_ARGUMENT); + RCL_CHECK_FOR_NULL_WITH_MSG( + buffer->impl->text, "buffer text not initialized", return RCL_RET_INVALID_ARGUMENT); + + rcl_lexeme_t lexeme = RCL_LEXEME_NONE; + rcl_ret_t ret; + size_t length; + const char * ptext = buffer->impl->text; + while (ptext) { + if (*ptext == '\0') { + ret = RCL_RET_ERROR; + break; + } + ret = rcl_lexer_analyze(ptext, &lexeme, &length); + if (RCL_RET_OK != ret) { + break; + } + + if (lexeme == RCL_LEXEME_COLON) { + ret = RCL_RET_OK; + break; + } + + ptext += length; + } + return ret; +} + rcl_ret_t rcl_lexer_lookahead2_accept( rcl_lexer_lookahead2_t * buffer, diff --git a/rcl/src/rcl/remap.c b/rcl/src/rcl/remap.c index 5db08623c..a6ec2ccc8 100644 --- a/rcl/src/rcl/remap.c +++ b/rcl/src/rcl/remap.c @@ -19,6 +19,7 @@ #include "rcl/error_handling.h" #include "rcl/expand_topic_name.h" #include "rcutils/allocator.h" +#include "rcutils/format_string.h" #include "rcutils/macros.h" #include "rcutils/strdup.h" #include "rcutils/types/string_map.h" @@ -113,15 +114,34 @@ rcl_remap_first_match( rcl_remap_t ** output_rule) { *output_rule = NULL; + + char * full_qualified_name = NULL; + if (node_namespace && node_name) { + const char * fmt = (strlen(node_namespace) == 1) ? "%s%s" : "%s/%s"; + full_qualified_name = + rcutils_format_string(allocator, fmt, node_namespace, node_name); + if (NULL == full_qualified_name) { + RCL_SET_ERROR_MSG("failed to allocate memory for full node name"); + return RCL_RET_BAD_ALLOC; + } + } + for (int i = 0; i < num_rules; ++i) { rcl_remap_t * rule = &(remap_rules[i]); if (!(rule->impl->type & type_bitmask)) { // Not the type of remap rule we're looking fore continue; } - if (rule->impl->node_name != NULL && 0 != strcmp(rule->impl->node_name, node_name)) { - // Rule has a node name prefix and the supplied node name didn't match - continue; + if (rule->impl->node_name != NULL) { + if (rule->impl->node_name[0] == '/' && full_qualified_name) { + if (0 != strcmp(rule->impl->node_name, full_qualified_name)) { + // Rule has a full qualified name and the supplied node name didn't match + continue; + } + } else if (0 != strcmp(rule->impl->node_name, node_name)) { + // Rule has a node name prefix and the supplied node name didn't match + continue; + } } bool matched = false; if (rule->impl->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) { @@ -138,6 +158,7 @@ rcl_remap_first_match( RCL_RET_BAD_ALLOC == ret) { // these are probably going to happen again. Stop processing rules + allocator.deallocate(full_qualified_name, allocator.state); return ret; } continue; @@ -153,6 +174,7 @@ rcl_remap_first_match( break; } } + allocator.deallocate(full_qualified_name, allocator.state); return RCL_RET_OK; } diff --git a/rcl/test/rcl/test_arguments.cpp b/rcl/test/rcl/test_arguments.cpp index 9e7bcecf4..526f72822 100644 --- a/rcl/test/rcl/test_arguments.cpp +++ b/rcl/test/rcl/test_arguments.cpp @@ -143,6 +143,9 @@ TEST_F(CLASSNAME(TestArgumentsFixture, RMW_IMPLEMENTATION), check_known_vs_unkno EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "foo:=/bar"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "/foo123:=/bar123"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "node:/foo123:=/bar123"})); + EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "node:foo123:=/bar123"})); + EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "/node:foo123:=/bar123"})); + EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "/ns1/node:foo123:=/bar123"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "rostopic:=/foo/bar"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "rosservice:=baz"})); EXPECT_TRUE(are_known_ros_args({"--ros-args", "-r", "rostopic://rostopic:=rosservice"})); diff --git a/rcl/test/rcl/test_remap.cpp b/rcl/test/rcl/test_remap.cpp index 72016d867..e59613d4b 100644 --- a/rcl/test/rcl/test_remap.cpp +++ b/rcl/test/rcl/test_remap.cpp @@ -194,7 +194,9 @@ TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), nodename_prefix_topic_re "--ros-args", "-r", "Node1:/foo:=/foo/bar", "-r", "Node2:/foo:=/this_one", - "-r", "Node3:/foo:=/bar/foo"); + "-r", "Node3:/foo:=/bar/foo", + "-r", "/Node4:/foo:=/bar/foo", + "-r", "/ns1/Node5:/foo:=/bar/foo"); { char * output = NULL; @@ -220,6 +222,22 @@ TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), nodename_prefix_topic_re EXPECT_STREQ("/bar/foo", output); rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state); } + { + char * output = NULL; + ret = rcl_remap_topic_name( + NULL, &global_arguments, "/foo", "Node4", "/", rcl_get_default_allocator(), &output); + EXPECT_EQ(RCL_RET_OK, ret); + EXPECT_STREQ("/bar/foo", output); + rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state); + } + { + char * output = NULL; + ret = rcl_remap_topic_name( + NULL, &global_arguments, "/foo", "Node5", "/ns1", rcl_get_default_allocator(), &output); + EXPECT_EQ(RCL_RET_OK, ret); + EXPECT_STREQ("/bar/foo", output); + rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state); + } } TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_use_global_topic_name_replacement) { @@ -326,7 +344,9 @@ TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), nodename_prefix_service_ "--ros-args", "-r", "Node1:/foo:=/foo/bar", "-r", "Node2:/foo:=/this_one", - "-r", "Node3:/foo:=/bar/foo"); + "-r", "Node3:/foo:=/bar/foo", + "-r", "/Node4:/foo:=/bar/foo", + "-r", "/ns1/Node5:/foo:=/bar/foo"); { char * output = NULL; @@ -352,6 +372,22 @@ TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), nodename_prefix_service_ EXPECT_STREQ("/bar/foo", output); rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state); } + { + char * output = NULL; + ret = rcl_remap_service_name( + NULL, &global_arguments, "/foo", "Node4", "/", rcl_get_default_allocator(), &output); + EXPECT_EQ(RCL_RET_OK, ret); + EXPECT_STREQ("/bar/foo", output); + rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state); + } + { + char * output = NULL; + ret = rcl_remap_service_name( + NULL, &global_arguments, "/foo", "Node5", "/ns1", rcl_get_default_allocator(), &output); + EXPECT_EQ(RCL_RET_OK, ret); + EXPECT_STREQ("/bar/foo", output); + rcl_get_default_allocator().deallocate(output, rcl_get_default_allocator().state); + } } TEST_F(CLASSNAME(TestRemapFixture, RMW_IMPLEMENTATION), no_use_global_service_name_replacement) {