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) {