ROS 2 rclcpp + rcl - rolling  rolling-a919a6e5
ROS 2 C++ Client Library with ROS Client Library
remap.c
1 // Copyright 2018 Open Source Robotics Foundation, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "rcl/remap.h"
16 
17 #include "./arguments_impl.h"
18 #include "./remap_impl.h"
19 #include "rcl/error_handling.h"
20 #include "rcl/expand_topic_name.h"
21 #include "rcutils/allocator.h"
22 #include "rcutils/macros.h"
23 #include "rcutils/strdup.h"
24 #include "rcutils/types/string_map.h"
25 
26 #ifdef __cplusplus
27 extern "C"
28 {
29 #endif
30 
33 {
34  // All members are initialized to 0 or NULL by C99 6.7.8/10.
35  static rcl_remap_t default_rule;
36  return default_rule;
37 }
38 
41  const rcl_remap_t * rule,
42  rcl_remap_t * rule_out)
43 {
44  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
45  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
46 
47  RCL_CHECK_ARGUMENT_FOR_NULL(rule, RCL_RET_INVALID_ARGUMENT);
48  RCL_CHECK_ARGUMENT_FOR_NULL(rule_out, RCL_RET_INVALID_ARGUMENT);
49  RCL_CHECK_ARGUMENT_FOR_NULL(rule->impl, RCL_RET_INVALID_ARGUMENT);
50 
51  if (NULL != rule_out->impl) {
52  RCL_SET_ERROR_MSG("rule_out must be zero initialized");
54  }
55 
56  rcl_allocator_t allocator = rule->impl->allocator;
57 
58  rule_out->impl = allocator.allocate(sizeof(rcl_remap_impl_t), allocator.state);
59  if (NULL == rule_out->impl) {
60  return RCL_RET_BAD_ALLOC;
61  }
62 
63  rule_out->impl->allocator = allocator;
64 
65  // Zero so it's safe to call rcl_remap_fini() if an error occurs while copying.
66  rule_out->impl->type = RCL_UNKNOWN_REMAP;
67  rule_out->impl->node_name = NULL;
68  rule_out->impl->match = NULL;
69  rule_out->impl->replacement = NULL;
70 
71  rule_out->impl->type = rule->impl->type;
72  if (NULL != rule->impl->node_name) {
73  rule_out->impl->node_name = rcutils_strdup(rule->impl->node_name, allocator);
74  if (NULL == rule_out->impl->node_name) {
75  goto fail;
76  }
77  }
78  if (NULL != rule->impl->match) {
79  rule_out->impl->match = rcutils_strdup(rule->impl->match, allocator);
80  if (NULL == rule_out->impl->match) {
81  goto fail;
82  }
83  }
84  if (NULL != rule->impl->replacement) {
85  rule_out->impl->replacement = rcutils_strdup(
86  rule->impl->replacement, allocator);
87  if (NULL == rule_out->impl->replacement) {
88  goto fail;
89  }
90  }
91  return RCL_RET_OK;
92 fail:
93  if (RCL_RET_OK != rcl_remap_fini(rule_out)) {
94  RCL_SET_ERROR_MSG("Error while finalizing remap rule due to another error");
95  }
96  return RCL_RET_BAD_ALLOC;
97 }
98 
101 static
102 rcl_ret_t
103 rcl_remap_first_match(
104  rcl_remap_t * remap_rules,
105  int num_rules,
106  rcl_remap_type_t type_bitmask,
107  const char * name,
108  const char * node_name,
109  const char * node_namespace,
110  const rcutils_string_map_t * substitutions,
111  rcutils_allocator_t allocator,
112  rcl_remap_t ** output_rule)
113 {
114  *output_rule = NULL;
115  for (int i = 0; i < num_rules; ++i) {
116  rcl_remap_t * rule = &(remap_rules[i]);
117  if (!(rule->impl->type & type_bitmask)) {
118  // Not the type of remap rule we're looking fore
119  continue;
120  }
121  if (rule->impl->node_name != NULL && 0 != strcmp(rule->impl->node_name, node_name)) {
122  // Rule has a node name prefix and the supplied node name didn't match
123  continue;
124  }
125  bool matched = false;
126  if (rule->impl->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
127  // topic and service rules need the match side to be expanded to a FQN
128  char * expanded_match = NULL;
130  rule->impl->match, node_name, node_namespace,
131  substitutions, allocator, &expanded_match);
132  if (RCL_RET_OK != ret) {
133  rcl_reset_error();
134  if (
136  RCL_RET_NODE_INVALID_NAME == ret ||
137  RCL_RET_BAD_ALLOC == ret)
138  {
139  // these are probably going to happen again. Stop processing rules
140  return ret;
141  }
142  continue;
143  }
144  if (NULL != name) {
145  // this check is to satisfy clang-tidy – name is always not null when type_bitmask is
146  // RCL_TOPIC_REMAP or RCL_SERVICE_REMAP. That is guaranteed because rcl_remap_first_match
147  // and rcl_remap_name are not public.
148  matched = (0 == strcmp(expanded_match, name));
149  }
150  allocator.deallocate(expanded_match, allocator.state);
151  } else {
152  // nodename and namespace replacement apply if the type and node name prefix checks passed
153  matched = true;
154  }
155  if (matched) {
156  *output_rule = rule;
157  break;
158  }
159  }
160  return RCL_RET_OK;
161 }
162 
164 RCL_LOCAL
165 rcl_ret_t
166 rcl_remap_name(
167  const rcl_arguments_t * local_arguments,
168  const rcl_arguments_t * global_arguments,
169  rcl_remap_type_t type_bitmask,
170  const char * name,
171  const char * node_name,
172  const char * node_namespace,
173  const rcutils_string_map_t * substitutions,
174  rcl_allocator_t allocator,
175  char ** output_name)
176 {
177  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
178  RCL_CHECK_ARGUMENT_FOR_NULL(output_name, RCL_RET_INVALID_ARGUMENT);
179  if (NULL != local_arguments && NULL == local_arguments->impl) {
180  local_arguments = NULL;
181  }
182  if (NULL != global_arguments && NULL == global_arguments->impl) {
183  global_arguments = NULL;
184  }
185  if (NULL == local_arguments && NULL == global_arguments) {
186  RCL_SET_ERROR_MSG("local_arguments invalid and not using global arguments");
188  }
189 
190  *output_name = NULL;
191  rcl_remap_t * rule = NULL;
192 
193  // Look at local rules first
194  if (NULL != local_arguments) {
195  rcl_ret_t ret = rcl_remap_first_match(
196  local_arguments->impl->remap_rules, local_arguments->impl->num_remap_rules, type_bitmask,
197  name, node_name, node_namespace, substitutions, allocator, &rule);
198  if (ret != RCL_RET_OK) {
199  return ret;
200  }
201  }
202  // Check global rules if no local rule matched
203  if (NULL == rule && NULL != global_arguments) {
204  rcl_ret_t ret = rcl_remap_first_match(
205  global_arguments->impl->remap_rules, global_arguments->impl->num_remap_rules, type_bitmask,
206  name, node_name, node_namespace, substitutions, allocator, &rule);
207  if (ret != RCL_RET_OK) {
208  return ret;
209  }
210  }
211  // Do the remapping
212  if (NULL != rule) {
213  if (rule->impl->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
214  // topic and service rules need the replacement to be expanded to a FQN
216  rule->impl->replacement, node_name, node_namespace, substitutions, allocator, output_name);
217  if (RCL_RET_OK != ret) {
218  return ret;
219  }
220  } else {
221  // nodename and namespace rules don't need replacment expanded
222  *output_name = rcutils_strdup(rule->impl->replacement, allocator);
223  }
224  if (NULL == *output_name) {
225  RCL_SET_ERROR_MSG("Failed to set output");
226  return RCL_RET_ERROR;
227  }
228  }
229  return RCL_RET_OK;
230 }
231 
232 rcl_ret_t
234  const rcl_arguments_t * local_arguments,
235  const rcl_arguments_t * global_arguments,
236  const char * topic_name,
237  const char * node_name,
238  const char * node_namespace,
239  rcl_allocator_t allocator,
240  char ** output_name)
241 {
242  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
243  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
244 
245  rcutils_string_map_t substitutions = rcutils_get_zero_initialized_string_map();
246  rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions, 0, allocator);
247  rcl_ret_t ret = RCL_RET_ERROR;
248  if (RCUTILS_RET_OK == rcutils_ret) {
249  ret = rcl_get_default_topic_name_substitutions(&substitutions);
250  if (RCL_RET_OK == ret) {
251  ret = rcl_remap_name(
252  local_arguments, global_arguments, RCL_TOPIC_REMAP, topic_name, node_name,
253  node_namespace, &substitutions, allocator, output_name);
254  }
255  }
256  if (RCUTILS_RET_OK != rcutils_string_map_fini(&substitutions)) {
257  return RCL_RET_ERROR;
258  }
259  return ret;
260 }
261 
262 rcl_ret_t
264  const rcl_arguments_t * local_arguments,
265  const rcl_arguments_t * global_arguments,
266  const char * service_name,
267  const char * node_name,
268  const char * node_namespace,
269  rcl_allocator_t allocator,
270  char ** output_name)
271 {
272  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
273  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
274 
275  rcutils_string_map_t substitutions = rcutils_get_zero_initialized_string_map();
276  rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions, 0, allocator);
277  rcl_ret_t ret = RCL_RET_ERROR;
278  if (rcutils_ret == RCUTILS_RET_OK) {
279  ret = rcl_get_default_topic_name_substitutions(&substitutions);
280  if (ret == RCL_RET_OK) {
281  ret = rcl_remap_name(
282  local_arguments, global_arguments, RCL_SERVICE_REMAP, service_name, node_name,
283  node_namespace, &substitutions, allocator, output_name);
284  }
285  }
286  if (RCUTILS_RET_OK != rcutils_string_map_fini(&substitutions)) {
287  return RCL_RET_ERROR;
288  }
289  return ret;
290 }
291 
292 rcl_ret_t
294  const rcl_arguments_t * local_arguments,
295  const rcl_arguments_t * global_arguments,
296  const char * node_name,
297  rcl_allocator_t allocator,
298  char ** output_name)
299 {
300  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
301  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID_NAME);
302  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
303  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
304 
305  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
306  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
307  return rcl_remap_name(
308  local_arguments, global_arguments, RCL_NODENAME_REMAP, NULL, node_name, NULL, NULL,
309  allocator, output_name);
310 }
311 
312 rcl_ret_t
314  const rcl_arguments_t * local_arguments,
315  const rcl_arguments_t * global_arguments,
316  const char * node_name,
317  rcl_allocator_t allocator,
318  char ** output_namespace)
319 {
320  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
321  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID_NAMESPACE);
322  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
323  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
324 
325  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
326  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
327  return rcl_remap_name(
328  local_arguments, global_arguments, RCL_NAMESPACE_REMAP, NULL, node_name, NULL, NULL,
329  allocator, output_namespace);
330 }
331 
332 rcl_ret_t
334  rcl_remap_t * rule)
335 {
336  RCL_CHECK_ARGUMENT_FOR_NULL(rule, RCL_RET_INVALID_ARGUMENT);
337  if (rule->impl) {
338  rcl_ret_t ret = RCL_RET_OK;
339  if (NULL != rule->impl->node_name) {
340  rule->impl->allocator.deallocate(
341  rule->impl->node_name, rule->impl->allocator.state);
342  rule->impl->node_name = NULL;
343  }
344  if (NULL != rule->impl->match) {
345  rule->impl->allocator.deallocate(
346  rule->impl->match, rule->impl->allocator.state);
347  rule->impl->match = NULL;
348  }
349  if (NULL != rule->impl->replacement) {
350  rule->impl->allocator.deallocate(
351  rule->impl->replacement, rule->impl->allocator.state);
352  rule->impl->replacement = NULL;
353  }
354  rule->impl->allocator.deallocate(rule->impl, rule->impl->allocator.state);
355  rule->impl = NULL;
356  return ret;
357  }
358  RCL_SET_ERROR_MSG("rcl_remap_t finalized twice");
359  return RCL_RET_ERROR;
360 }
361 
362 #ifdef __cplusplus
363 }
364 #endif
#define RCL_CHECK_ALLOCATOR_WITH_MSG(allocator, msg, fail_statement)
Check that the given allocator is initialized, or fail with a message.
Definition: allocator.h:56
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
Definition: allocator.h:31
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_expand_topic_name(const char *input_topic_name, const char *node_name, const char *node_namespace, const rcutils_string_map_t *substitutions, rcl_allocator_t allocator, char **output_topic_name)
Expand a given topic name into a fully-qualified topic name.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_get_default_topic_name_substitutions(rcutils_string_map_t *string_map)
Fill a given string map with the default substitution pairs.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_node_namespace(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *node_name, rcl_allocator_t allocator, char **output_namespace)
Remap a namespace based on given rules.
Definition: remap.c:313
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_fini(rcl_remap_t *remap)
Reclaim resources held inside rcl_remap_t structure.
Definition: remap.c:333
RCL_PUBLIC RCL_WARN_UNUSED rcl_remap_t rcl_get_zero_initialized_remap(void)
Return a rcl_remap_t struct with members initialized to NULL.
Definition: remap.c:32
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_service_name(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *service_name, const char *node_name, const char *node_namespace, rcl_allocator_t allocator, char **output_name)
Remap a service name based on given rules.
Definition: remap.c:263
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_copy(const rcl_remap_t *rule, rcl_remap_t *rule_out)
Copy one remap structure into another.
Definition: remap.c:40
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_topic_name(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *topic_name, const char *node_name, const char *node_namespace, rcl_allocator_t allocator, char **output_name)
Remap a topic name based on given rules.
Definition: remap.c:233
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_remap_node_name(const rcl_arguments_t *local_arguments, const rcl_arguments_t *global_arguments, const char *node_name, rcl_allocator_t allocator, char **output_name)
Remap a node name based on given rules.
Definition: remap.c:293
rcl_remap_t * remap_rules
Array of rules for name remapping.
int num_remap_rules
Length of remap_rules.
Hold output of parsing command line arguments.
Definition: arguments.h:36
rcl_arguments_impl_t * impl
Private implementation pointer.
Definition: arguments.h:38
char * replacement
Replacement portion of a rule.
Definition: remap_impl.h:48
char * node_name
A node name that this rule is limited to, or NULL if it applies to any node.
Definition: remap_impl.h:44
rcl_allocator_t allocator
Allocator used to allocate objects in this struct.
Definition: remap_impl.h:51
char * match
Match portion of a rule, or NULL if node name or namespace replacement.
Definition: remap_impl.h:46
rcl_remap_type_t type
Bitmask indicating what type of rule this is.
Definition: remap_impl.h:42
Hold remapping rules.
Definition: remap.h:35
rcl_remap_impl_t * impl
Private implementation pointer.
Definition: remap.h:37
#define RCL_RET_NODE_INVALID_NAMESPACE
Invalid node namespace return code.
Definition: types.h:63
#define RCL_RET_OK
Success return code.
Definition: types.h:27
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
Definition: types.h:33
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:35
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:29
#define RCL_RET_NODE_INVALID_NAME
Invalid node name return code.
Definition: types.h:61
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:24