diff --git a/src/cpp/xmlparser/XMLElementParser.cpp b/src/cpp/xmlparser/XMLElementParser.cpp index 98ca5c9d0fb..5e194f6db8f 100644 --- a/src/cpp/xmlparser/XMLElementParser.cpp +++ b/src/cpp/xmlparser/XMLElementParser.cpp @@ -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; }