27 #include "rcl/error_handling.h"
30 #include "rcutils/error_handling.h"
31 #include "rcutils/format_string.h"
32 #include "rcutils/repl_str.h"
33 #include "rcutils/strdup.h"
34 #include "rcutils/types/string_map.h"
35 #include "rmw/error_handling.h"
36 #include "rmw/ret_types.h"
37 #include "rmw/validate_namespace.h"
38 #include "rmw/validate_node_name.h"
41 #define SUBSTITUION_NODE_NAME "{node}"
42 #define SUBSTITUION_NAMESPACE "{ns}"
43 #define SUBSTITUION_NAMESPACE2 "{namespace}"
47 const char * input_topic_name,
48 const char * node_name,
49 const char * node_namespace,
50 const rcutils_string_map_t * substitutions,
52 char ** output_topic_name)
68 int validation_result;
80 rmw_ret = rmw_validate_node_name(node_name, &validation_result, NULL);
81 if (rmw_ret != RMW_RET_OK) {
82 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
83 return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
85 if (validation_result != RMW_NODE_NAME_VALID) {
86 RCL_SET_ERROR_MSG(rmw_node_name_validation_result_string(validation_result));
90 rmw_ret = rmw_validate_namespace(node_namespace, &validation_result, NULL);
91 if (rmw_ret != RMW_RET_OK) {
92 RCL_SET_ERROR_MSG(rmw_get_error_string().str);
93 return rcl_convert_rmw_ret_to_rcl_ret(rmw_ret);
95 if (validation_result != RMW_NODE_NAME_VALID) {
96 RCL_SET_ERROR_MSG(rmw_namespace_validation_result_string(validation_result));
100 bool has_a_substitution = strchr(input_topic_name,
'{') != NULL;
101 bool has_a_namespace_tilde = input_topic_name[0] ==
'~';
102 bool is_absolute = input_topic_name[0] ==
'/';
104 if (is_absolute && !has_a_substitution) {
106 *output_topic_name = rcutils_strdup(input_topic_name, allocator);
107 if (!*output_topic_name) {
108 *output_topic_name = NULL;
110 RCL_SET_ERROR_MSG(
"failed to allocate memory for output topic");
115 char * local_output = NULL;
117 if (has_a_namespace_tilde) {
120 const char * fmt = (strlen(node_namespace) == 1) ?
"%s%s%s" :
"%s/%s%s";
122 rcutils_format_string(allocator, fmt, node_namespace, node_name, input_topic_name + 1);
124 *output_topic_name = NULL;
125 RCL_SET_ERROR_MSG(
"failed to allocate memory for output topic");
130 if (has_a_substitution) {
138 const char * current_output = (local_output) ? local_output : input_topic_name;
139 const char * next_opening_brace = NULL;
141 while ((next_opening_brace = strchr(current_output,
'{')) != NULL) {
142 const char * next_closing_brace = strchr(current_output,
'}');
144 size_t substitution_substr_len = next_closing_brace - next_opening_brace + 1;
146 const char * replacement = NULL;
147 if (strncmp(SUBSTITUION_NODE_NAME, next_opening_brace, substitution_substr_len) == 0) {
148 replacement = node_name;
150 strncmp(SUBSTITUION_NAMESPACE, next_opening_brace, substitution_substr_len) == 0 ||
151 strncmp(SUBSTITUION_NAMESPACE2, next_opening_brace, substitution_substr_len) == 0)
153 replacement = node_namespace;
155 replacement = rcutils_string_map_getn(
159 next_opening_brace + 1, substitution_substr_len - 2);
162 *output_topic_name = NULL;
163 char * unmatched_substitution =
164 rcutils_strndup(next_opening_brace, substitution_substr_len, allocator);
165 if (unmatched_substitution) {
166 RCL_SET_ERROR_MSG_WITH_FORMAT_STRING(
167 "unknown substitution: %s", unmatched_substitution);
169 RCUTILS_SAFE_FWRITE_TO_STDERR(
"failed to allocate memory for unmatched substitution\n");
171 allocator.deallocate(unmatched_substitution, allocator.state);
172 allocator.deallocate(local_output, allocator.state);
178 char * next_substitution =
179 rcutils_strndup(next_opening_brace, substitution_substr_len, allocator);
180 if (!next_substitution) {
181 *output_topic_name = NULL;
183 RCL_SET_ERROR_MSG(
"failed to allocate memory for substitution");
184 allocator.deallocate(local_output, allocator.state);
187 char * original_local_output = local_output;
188 local_output = rcutils_repl_str(current_output, next_substitution, replacement, &allocator);
189 allocator.deallocate(next_substitution, allocator.state);
190 allocator.deallocate(original_local_output, allocator.state);
192 *output_topic_name = NULL;
194 RCL_SET_ERROR_MSG(
"failed to allocate memory for expanded topic");
197 current_output = local_output;
203 (local_output && local_output[0] !=
'/') ||
204 (!local_output && input_topic_name[0] !=
'/'))
206 char * original_local_output = local_output;
209 const char * fmt = (strlen(node_namespace) == 1) ?
"%s%s" :
"%s/%s";
210 local_output = rcutils_format_string(
211 allocator, fmt, node_namespace, (local_output) ? local_output : input_topic_name);
212 if (original_local_output) {
213 allocator.deallocate(original_local_output, allocator.state);
216 *output_topic_name = NULL;
217 RCL_SET_ERROR_MSG(
"failed to allocate memory for output topic");
222 *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.