ROS 2 rclcpp + rcl - rolling  rolling-e7363f10
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 <stdbool.h>
18 #include <stddef.h>
19 #include <string.h>
20 
21 #include "./arguments_impl.h"
22 #include "./remap_impl.h"
23 #include "rcl/allocator.h"
24 #include "rcl/error_handling.h"
25 #include "rcl/expand_topic_name.h"
26 #include "rcl/types.h"
27 #include "rcl/visibility_control.h"
28 
29 #include "rcutils/allocator.h"
30 #include "rcutils/strdup.h"
31 #include "rcutils/types/rcutils_ret.h"
32 #include "rcutils/types/string_map.h"
33 
34 #ifdef __cplusplus
35 extern "C"
36 {
37 #endif
38 
41 {
42  // All members are initialized to 0 or NULL by C99 6.7.8/10.
43  static rcl_remap_t default_rule;
44  return default_rule;
45 }
46 
49  const rcl_remap_t * rule,
50  rcl_remap_t * rule_out)
51 {
52  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
53  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
54 
55  RCL_CHECK_ARGUMENT_FOR_NULL(rule, RCL_RET_INVALID_ARGUMENT);
56  RCL_CHECK_ARGUMENT_FOR_NULL(rule_out, RCL_RET_INVALID_ARGUMENT);
57  RCL_CHECK_ARGUMENT_FOR_NULL(rule->impl, RCL_RET_INVALID_ARGUMENT);
58 
59  if (NULL != rule_out->impl) {
60  RCL_SET_ERROR_MSG("rule_out must be zero initialized");
62  }
63 
64  rcl_allocator_t allocator = rule->impl->allocator;
65 
66  rule_out->impl = allocator.allocate(sizeof(rcl_remap_impl_t), allocator.state);
67  if (NULL == rule_out->impl) {
68  return RCL_RET_BAD_ALLOC;
69  }
70 
71  rule_out->impl->allocator = allocator;
72 
73  // Zero so it's safe to call rcl_remap_fini() if an error occurs while copying.
74  rule_out->impl->type = RCL_UNKNOWN_REMAP;
75  rule_out->impl->node_name = NULL;
76  rule_out->impl->match = NULL;
77  rule_out->impl->replacement = NULL;
78 
79  rule_out->impl->type = rule->impl->type;
80  if (NULL != rule->impl->node_name) {
81  rule_out->impl->node_name = rcutils_strdup(rule->impl->node_name, allocator);
82  if (NULL == rule_out->impl->node_name) {
83  goto fail;
84  }
85  }
86  if (NULL != rule->impl->match) {
87  rule_out->impl->match = rcutils_strdup(rule->impl->match, allocator);
88  if (NULL == rule_out->impl->match) {
89  goto fail;
90  }
91  }
92  if (NULL != rule->impl->replacement) {
93  rule_out->impl->replacement = rcutils_strdup(
94  rule->impl->replacement, allocator);
95  if (NULL == rule_out->impl->replacement) {
96  goto fail;
97  }
98  }
99  return RCL_RET_OK;
100 fail:
101  if (RCL_RET_OK != rcl_remap_fini(rule_out)) {
102  RCL_SET_ERROR_MSG("Error while finalizing remap rule due to another error");
103  }
104  return RCL_RET_BAD_ALLOC;
105 }
106 
109 static
110 rcl_ret_t
111 rcl_remap_first_match(
112  rcl_remap_t * remap_rules,
113  int num_rules,
114  rcl_remap_type_t type_bitmask,
115  const char * name,
116  const char * node_name,
117  const char * node_namespace,
118  const rcutils_string_map_t * substitutions,
119  rcutils_allocator_t allocator,
120  rcl_remap_t ** output_rule)
121 {
122  *output_rule = NULL;
123  for (int i = 0; i < num_rules; ++i) {
124  rcl_remap_t * rule = &(remap_rules[i]);
125  if (!(rule->impl->type & type_bitmask)) {
126  // Not the type of remap rule we're looking fore
127  continue;
128  }
129  if (rule->impl->node_name != NULL && 0 != strcmp(rule->impl->node_name, node_name)) {
130  // Rule has a node name prefix and the supplied node name didn't match
131  continue;
132  }
133  bool matched = false;
134  if (rule->impl->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
135  // topic and service rules need the match side to be expanded to a FQN
136  char * expanded_match = NULL;
138  rule->impl->match, node_name, node_namespace,
139  substitutions, allocator, &expanded_match);
140  if (RCL_RET_OK != ret) {
141  rcl_reset_error();
142  if (
144  RCL_RET_NODE_INVALID_NAME == ret ||
145  RCL_RET_BAD_ALLOC == ret)
146  {
147  // these are probably going to happen again. Stop processing rules
148  return ret;
149  }
150  continue;
151  }
152  if (NULL != name) {
153  // this check is to satisfy clang-tidy – name is always not null when type_bitmask is
154  // RCL_TOPIC_REMAP or RCL_SERVICE_REMAP. That is guaranteed because rcl_remap_first_match
155  // and rcl_remap_name are not public.
156  matched = (0 == strcmp(expanded_match, name));
157  }
158  allocator.deallocate(expanded_match, allocator.state);
159  } else {
160  // nodename and namespace replacement apply if the type and node name prefix checks passed
161  matched = true;
162  }
163  if (matched) {
164  *output_rule = rule;
165  break;
166  }
167  }
168  return RCL_RET_OK;
169 }
170 
172 RCL_LOCAL
173 rcl_ret_t
174 rcl_remap_name(
175  const rcl_arguments_t * local_arguments,
176  const rcl_arguments_t * global_arguments,
177  rcl_remap_type_t type_bitmask,
178  const char * name,
179  const char * node_name,
180  const char * node_namespace,
181  const rcutils_string_map_t * substitutions,
182  rcl_allocator_t allocator,
183  char ** output_name)
184 {
185  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
186  RCL_CHECK_ARGUMENT_FOR_NULL(output_name, RCL_RET_INVALID_ARGUMENT);
187  if (NULL != local_arguments && NULL == local_arguments->impl) {
188  local_arguments = NULL;
189  }
190  if (NULL != global_arguments && NULL == global_arguments->impl) {
191  global_arguments = NULL;
192  }
193  if (NULL == local_arguments && NULL == global_arguments) {
194  RCL_SET_ERROR_MSG("local_arguments invalid and not using global arguments");
196  }
197 
198  *output_name = NULL;
199  rcl_remap_t * rule = NULL;
200 
201  // Look at local rules first
202  if (NULL != local_arguments) {
203  rcl_ret_t ret = rcl_remap_first_match(
204  local_arguments->impl->remap_rules, local_arguments->impl->num_remap_rules, type_bitmask,
205  name, node_name, node_namespace, substitutions, allocator, &rule);
206  if (ret != RCL_RET_OK) {
207  return ret;
208  }
209  }
210  // Check global rules if no local rule matched
211  if (NULL == rule && NULL != global_arguments) {
212  rcl_ret_t ret = rcl_remap_first_match(
213  global_arguments->impl->remap_rules, global_arguments->impl->num_remap_rules, type_bitmask,
214  name, node_name, node_namespace, substitutions, allocator, &rule);
215  if (ret != RCL_RET_OK) {
216  return ret;
217  }
218  }
219  // Do the remapping
220  if (NULL != rule) {
221  if (rule->impl->type & (RCL_TOPIC_REMAP | RCL_SERVICE_REMAP)) {
222  // topic and service rules need the replacement to be expanded to a FQN
224  rule->impl->replacement, node_name, node_namespace, substitutions, allocator, output_name);
225  if (RCL_RET_OK != ret) {
226  return ret;
227  }
228  } else {
229  // nodename and namespace rules don't need replacment expanded
230  *output_name = rcutils_strdup(rule->impl->replacement, allocator);
231  }
232  if (NULL == *output_name) {
233  RCL_SET_ERROR_MSG("Failed to set output");
234  return RCL_RET_ERROR;
235  }
236  }
237  return RCL_RET_OK;
238 }
239 
240 rcl_ret_t
242  const rcl_arguments_t * local_arguments,
243  const rcl_arguments_t * global_arguments,
244  const char * topic_name,
245  const char * node_name,
246  const char * node_namespace,
247  rcl_allocator_t allocator,
248  char ** output_name)
249 {
250  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
251  RCL_CHECK_ARGUMENT_FOR_NULL(topic_name, RCL_RET_INVALID_ARGUMENT);
252 
253  rcutils_string_map_t substitutions = rcutils_get_zero_initialized_string_map();
254  rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions, 0, allocator);
255  rcl_ret_t ret = RCL_RET_ERROR;
256  if (RCUTILS_RET_OK == rcutils_ret) {
257  ret = rcl_get_default_topic_name_substitutions(&substitutions);
258  if (RCL_RET_OK == ret) {
259  ret = rcl_remap_name(
260  local_arguments, global_arguments, RCL_TOPIC_REMAP, topic_name, node_name,
261  node_namespace, &substitutions, allocator, output_name);
262  }
263  }
264  if (RCUTILS_RET_OK != rcutils_string_map_fini(&substitutions)) {
265  return RCL_RET_ERROR;
266  }
267  return ret;
268 }
269 
270 rcl_ret_t
272  const rcl_arguments_t * local_arguments,
273  const rcl_arguments_t * global_arguments,
274  const char * service_name,
275  const char * node_name,
276  const char * node_namespace,
277  rcl_allocator_t allocator,
278  char ** output_name)
279 {
280  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
281  RCL_CHECK_ARGUMENT_FOR_NULL(service_name, RCL_RET_INVALID_ARGUMENT);
282 
283  rcutils_string_map_t substitutions = rcutils_get_zero_initialized_string_map();
284  rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions, 0, allocator);
285  rcl_ret_t ret = RCL_RET_ERROR;
286  if (rcutils_ret == RCUTILS_RET_OK) {
287  ret = rcl_get_default_topic_name_substitutions(&substitutions);
288  if (ret == RCL_RET_OK) {
289  ret = rcl_remap_name(
290  local_arguments, global_arguments, RCL_SERVICE_REMAP, service_name, node_name,
291  node_namespace, &substitutions, allocator, output_name);
292  }
293  }
294  if (RCUTILS_RET_OK != rcutils_string_map_fini(&substitutions)) {
295  return RCL_RET_ERROR;
296  }
297  return ret;
298 }
299 
300 rcl_ret_t
302  const rcl_arguments_t * local_arguments,
303  const rcl_arguments_t * global_arguments,
304  const char * node_name,
305  rcl_allocator_t allocator,
306  char ** output_name)
307 {
308  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
309  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID_NAME);
310  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
311  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
312 
313  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
314  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
315  return rcl_remap_name(
316  local_arguments, global_arguments, RCL_NODENAME_REMAP, NULL, node_name, NULL, NULL,
317  allocator, output_name);
318 }
319 
320 rcl_ret_t
322  const rcl_arguments_t * local_arguments,
323  const rcl_arguments_t * global_arguments,
324  const char * node_name,
325  rcl_allocator_t allocator,
326  char ** output_namespace)
327 {
328  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_INVALID_ARGUMENT);
329  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_NODE_INVALID_NAMESPACE);
330  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_BAD_ALLOC);
331  RCUTILS_CAN_SET_MSG_AND_RETURN_WITH_ERROR_OF(RCL_RET_ERROR);
332 
333  RCL_CHECK_ARGUMENT_FOR_NULL(node_name, RCL_RET_INVALID_ARGUMENT);
334  RCL_CHECK_ALLOCATOR_WITH_MSG(&allocator, "allocator is invalid", return RCL_RET_INVALID_ARGUMENT);
335  return rcl_remap_name(
336  local_arguments, global_arguments, RCL_NAMESPACE_REMAP, NULL, node_name, NULL, NULL,
337  allocator, output_namespace);
338 }
339 
340 rcl_ret_t
342  rcl_remap_t * rule)
343 {
344  RCL_CHECK_ARGUMENT_FOR_NULL(rule, RCL_RET_INVALID_ARGUMENT);
345  if (rule->impl) {
346  rcl_ret_t ret = RCL_RET_OK;
347  if (NULL != rule->impl->node_name) {
348  rule->impl->allocator.deallocate(
349  rule->impl->node_name, rule->impl->allocator.state);
350  rule->impl->node_name = NULL;
351  }
352  if (NULL != rule->impl->match) {
353  rule->impl->allocator.deallocate(
354  rule->impl->match, rule->impl->allocator.state);
355  rule->impl->match = NULL;
356  }
357  if (NULL != rule->impl->replacement) {
358  rule->impl->allocator.deallocate(
359  rule->impl->replacement, rule->impl->allocator.state);
360  rule->impl->replacement = NULL;
361  }
362  rule->impl->allocator.deallocate(rule->impl, rule->impl->allocator.state);
363  rule->impl = NULL;
364  return ret;
365  }
366  RCL_SET_ERROR_MSG("rcl_remap_t finalized twice");
367  return RCL_RET_ERROR;
368 }
369 
370 #ifdef __cplusplus
371 }
372 #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:321
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:341
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:40
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:271
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:48
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:241
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:301
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:49
char * node_name
A node name that this rule is limited to, or NULL if it applies to any node.
Definition: remap_impl.h:45
rcl_allocator_t allocator
Allocator used to allocate objects in this struct.
Definition: remap_impl.h:52
char * match
Match portion of a rule, or NULL if node name or namespace replacement.
Definition: remap_impl.h:47
rcl_remap_type_t type
Bitmask indicating what type of rule this is.
Definition: remap_impl.h:43
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:64
#define RCL_RET_OK
Success return code.
Definition: types.h:28
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
Definition: types.h:34
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
Definition: types.h:36
#define RCL_RET_ERROR
Unspecified error return code.
Definition: types.h:30
#define RCL_RET_NODE_INVALID_NAME
Invalid node name return code.
Definition: types.h:62
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
Definition: types.h:25