| 55 | // Based on blog post https://learn.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way |
| 56 | template <class Char> |
| 57 | static std::basic_string<Char> join_arguments(const std::vector<std::basic_string<Char>> &args) { |
| 58 | using charset = CharSet<Char>; |
| 59 | |
| 60 | std::basic_string<Char> ret; |
| 61 | |
| 62 | for(const auto &arg : args) { |
| 63 | if(!ret.empty()) |
| 64 | ret.push_back(charset::space); |
| 65 | |
| 66 | if(!arg.empty() && arg.find_first_of(charset::whitespace) == arg.npos) { |
| 67 | ret.append(arg); |
| 68 | continue; |
| 69 | } |
| 70 | |
| 71 | ret.push_back(charset::doublequote); |
| 72 | |
| 73 | for(auto it = arg.begin();; ++it) { |
| 74 | size_t n_backslashes = 0; |
| 75 | |
| 76 | while(it != arg.end() && *it == charset::backslash) { |
| 77 | ++it; |
| 78 | ++n_backslashes; |
| 79 | } |
| 80 | |
| 81 | if(it == arg.end()) { |
| 82 | // Escape all backslashes, but let the terminating double quotation mark |
| 83 | // we add below be interpreted as a metacharacter. |
| 84 | ret.append(n_backslashes * 2, charset::backslash); |
| 85 | break; |
| 86 | } |
| 87 | else if(*it == charset::doublequote) { |
| 88 | // Escape all backslashes and the following double quotation mark. |
| 89 | ret.append(n_backslashes * 2 + 1, charset::backslash); |
| 90 | ret.push_back(*it); |
| 91 | } |
| 92 | else { |
| 93 | // Backslashes aren't special here. |
| 94 | ret.append(n_backslashes, charset::backslash); |
| 95 | ret.push_back(*it); |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | ret.push_back(charset::doublequote); |
| 100 | } |
| 101 | |
| 102 | return ret; |
| 103 | } |
| 104 | |
| 105 | // Based on the discussion thread: https://www.reddit.com/r/cpp/comments/3vpjqg/a_new_platform_independent_process_library_for_c11/cxq1wsj |
| 106 | std::mutex create_process_mutex; |