Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 62 additions & 45 deletions src/cpp/xmlparser/XMLElementParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,65 +48,82 @@ static std::string process_environment(
* Environment variable names used ... consist solely of uppercase letters, digits, and the '_' (underscore)
* from the characters defined in Portable Character Set and do not begin with a digit.
*/
std::regex expression("\\$\\{([A-Z_][A-Z0-9_]*)\\}");

// Algorithm inspired by https://stackoverflow.com/a/37516316/559350
enum class ParseStage : uint8_t {
PLAIN_CHAR,
DOLLAR,
OPEN_CURLY,
ENV_CHAR,
};

// This will hold the accumulated substitution result
std::string ret_val{};

// Iterators to first and last character of the input string
auto first = input.cbegin();
auto last = input.cend();

// Position of last match in the input string
std::smatch::difference_type last_match_position = 0;
bool all_expanded = true;
const char* cursor = input.c_str();

// Iterator to the character after the last match in the input string
auto last_match_end = first;
ParseStage parse_stage = ParseStage::PLAIN_CHAR;
const char* match_start = cursor;
const char* last_match_end = cursor;
std::string var_name;

// Functor called to process each match
auto match_cb = [&](const std::smatch& match)
{
// Compute substitution value
std::string var_name = match[1];
std::string value = "";
if (var_name == "_")
{
// Silently ignore ${_} since it might expose sensitive information (full path to executable).
EPROSIMA_LOG_WARNING(XMLPARSER, "Ignoring environment variable ${_}");
for (; *cursor != '\0'; ++cursor) {
switch (parse_stage) {
case ParseStage::PLAIN_CHAR:
if ('$' == *cursor) {
parse_stage = ParseStage::DOLLAR;
}
else if (dds::RETCODE_OK != SystemInfo::get_env(var_name, value))
{
EPROSIMA_LOG_ERROR(XMLPARSER, "Could not find a value for environment variable " << var_name);
break;
case ParseStage::DOLLAR:
if ('{' == *cursor) {
match_start = cursor + 1;
parse_stage = ParseStage::OPEN_CURLY;
} else {
parse_stage = ParseStage::PLAIN_CHAR;
}
break;
case ParseStage::OPEN_CURLY:
if ((0 != std::isupper(*cursor)) || ('_' == *cursor)) {
// Must start with A-Z or '_'
parse_stage = ParseStage::ENV_CHAR;
} else {
parse_stage = ParseStage::PLAIN_CHAR;
}
break;
case ParseStage::ENV_CHAR:
if ('}' == *cursor) {
// Append everything after last '}' (or beginning of input) up until '$'
ret_val.append(last_match_end, match_start - 2);

// Compute number of non-matching characters between this match and the last one
auto this_match_position = match.position();
auto diff = this_match_position - last_match_position;
// Extract the part between curly braces to simplify lookup
var_name.assign(match_start, cursor);

// Append non-matching characters to return value
auto this_match_start = last_match_end;
std::advance(this_match_start, diff);
ret_val.append(last_match_end, this_match_start);
std::string value = "";

// Append substitution value to return value
ret_val.append(value);
if (var_name == "_")
{
// Silently ignore ${_} since it might expose sensitive information (full path to executable).
EPROSIMA_LOG_WARNING(XMLPARSER, "Ignoring environment variable ${_}");
}
else if (dds::RETCODE_OK != SystemInfo::get_env(var_name, value))
{
EPROSIMA_LOG_ERROR(XMLPARSER, "Could not find a value for environment variable " << var_name);
}

// Prepare for next iteration
auto match_length = match.length();
last_match_position = this_match_position + match_length;
last_match_end = this_match_start;
std::advance(last_match_end, match_length);
};
ret_val.append(value);

// Substitute all matches
std::sregex_iterator begin(first, last, expression);
std::sregex_iterator end;
std::for_each(begin, end, match_cb);
last_match_end = cursor + 1;
parse_stage = ParseStage::PLAIN_CHAR;
} else if ((0 == std::isupper(*cursor)) && (0 == std::isdigit(*cursor)) &&
('_' != *cursor)) {
// Can only be 0-9, A-Z, or '_'
parse_stage = ParseStage::PLAIN_CHAR;
}
break;
}
}

// Append characters after last match
ret_val.append(last_match_end, last);
// Append any remaining characters (cursor at '\0')
ret_val.append(last_match_end, cursor);

return ret_val;
}
Expand Down