26 #include "rcl/error_handling.h"
29 #include "rcutils/error_handling.h"
30 #include "rcutils/format_string.h"
31 #include "rcutils/repl_str.h"
32 #include "rcutils/strdup.h"
33 #include "rmw/error_handling.h"
34 #include "rmw/types.h"
35 #include "rmw/validate_namespace.h"
36 #include "rmw/validate_node_name.h"
39 #define SUBSTITUION_NODE_NAME "{node}"
40 #define SUBSTITUION_NAMESPACE "{ns}"
41 #define SUBSTITUION_NAMESPACE2 "{namespace}"
45 const char * input_topic_name,
46 const char * node_name,
47 const char * node_namespace,
48 const rcutils_string_map_t * substitutions,
50 char ** output_topic_name)
66 int validation_result;
78 rmw_ret = rmw_validate_node_name(node_name, &validation_result, NULL);
79 if (rmw_ret != RMW_RET_OK) {
80 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
81 return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
83 if (validation_result != RMW_NODE_NAME_VALID) {
84 RCL_SET_ERROR_MSG(rmw_node_name_validation_result_string(validation_result));
88 rmw_ret = rmw_validate_namespace(node_namespace, &validation_result, NULL);
89 if (rmw_ret != RMW_RET_OK) {
90 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
91 return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
93 if (validation_result != RMW_NODE_NAME_VALID) {
94 RCL_SET_ERROR_MSG(rmw_namespace_validation_result_string(validation_result));
98 bool has_a_substitution = strchr(input_topic_name,
'{') != NULL;
99 bool has_a_namespace_tilde = input_topic_name[0] ==
'~';
100 bool is_absolute = input_topic_name[0] ==
'/';
102 if (is_absolute && !has_a_substitution) {
104 *output_topic_name = rcutils_strdup(input_topic_name, allocator);
105 if (!*output_topic_name) {
106 *output_topic_name = NULL;
107 RCL_SET_ERROR_MSG(
"failed to allocate memory for output topic");
112 char * local_output = NULL;
114 if (has_a_namespace_tilde) {
117 const char * fmt = (strlen(node_namespace) == 1) ?
"%s%s%s" :
"%s/%s%s";
119 rcutils_format_string(allocator, fmt, node_namespace, node_name, input_topic_name + 1);
121 *output_topic_name = NULL;
122 RCL_SET_ERROR_MSG(
"failed to allocate memory for output topic");
127 if (has_a_substitution) {
135 const char * current_output = (local_output) ? local_output : input_topic_name;
136 char * next_opening_brace = NULL;
138 while ((next_opening_brace = strchr(current_output,
'{')) != NULL) {
139 char * next_closing_brace = strchr(current_output,
'}');
141 size_t substitution_substr_len = next_closing_brace - next_opening_brace + 1;
143 const char * replacement = NULL;
144 if (strncmp(SUBSTITUION_NODE_NAME, next_opening_brace, substitution_substr_len) == 0) {
145 replacement = node_name;
147 strncmp(SUBSTITUION_NAMESPACE, next_opening_brace, substitution_substr_len) == 0 ||
148 strncmp(SUBSTITUION_NAMESPACE2, next_opening_brace, substitution_substr_len) == 0)
150 replacement = node_namespace;
152 replacement = rcutils_string_map_getn(
156 next_opening_brace + 1, substitution_substr_len - 2);
159 *output_topic_name = NULL;
160 char * unmatched_substitution =
161 rcutils_strndup(next_opening_brace, substitution_substr_len, allocator);
162 if (unmatched_substitution) {
163 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
164 "unknown substitution: %s", unmatched_substitution);
166 RCUTILS_SAFE_FWRITE_TO_STDERR(
"failed to allocate memory for unmatched substitution\n");
168 allocator.deallocate(unmatched_substitution, allocator.state);
169 allocator.deallocate(local_output, allocator.state);
175 char * next_substitution =
176 rcutils_strndup(next_opening_brace, substitution_substr_len, allocator);
177 if (!next_substitution) {
178 *output_topic_name = NULL;
179 RCL_SET_ERROR_MSG(
"failed to allocate memory for substitution");
180 allocator.deallocate(local_output, allocator.state);
183 char * original_local_output = local_output;
184 local_output = rcutils_repl_str(current_output, next_substitution, replacement, &allocator);
185 allocator.deallocate(next_substitution, allocator.state);
186 allocator.deallocate(original_local_output, allocator.state);
188 *output_topic_name = NULL;
189 RCL_SET_ERROR_MSG(
"failed to allocate memory for expanded topic");
192 current_output = local_output;
198 (local_output && local_output[0] !=
'/') ||
199 (!local_output && input_topic_name[0] !=
'/'))
201 char * original_local_output = local_output;
204 const char * fmt = (strlen(node_namespace) == 1) ?
"%s%s" :
"%s/%s";
205 local_output = rcutils_format_string(
206 allocator, fmt, node_namespace, (local_output) ? local_output : input_topic_name);
207 if (original_local_output) {
208 allocator.deallocate(original_local_output, allocator.state);
211 *output_topic_name = NULL;
212 RCL_SET_ERROR_MSG(
"failed to allocate memory for output topic");
217 *output_topic_name = local_output;
rcutils_allocator_t rcl_allocator_t
Encapsulation of an allocator.
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.
#define RCL_RET_NODE_INVALID_NAMESPACE
Invalid node namespace return code.
#define RCL_RET_UNKNOWN_SUBSTITUTION
Topic name substitution is unknown.
#define RCL_RET_OK
Success return code.
#define RCL_RET_BAD_ALLOC
Failed to allocate memory return code.
#define RCL_RET_INVALID_ARGUMENT
Invalid argument return code.
#define RCL_RET_TOPIC_NAME_INVALID
Topic name does not pass validation.
#define RCL_RET_NODE_INVALID_NAME
Invalid node name return code.
rmw_ret_t rcl_ret_t
The type that holds an rcl return code.
RCL_PUBLIC RCL_WARN_UNUSED rcl_ret_t rcl_validate_topic_name(const char *topic_name, int *validation_result, size_t *invalid_index)
Validate a given topic name.
#define RCL_TOPIC_NAME_VALID
The topic name is valid.
RCL_PUBLIC RCL_WARN_UNUSED const char * rcl_topic_name_validation_result_string(int validation_result)
Return a validation result description, or NULL if unknown or RCL_TOPIC_NAME_VALID.