1 // Copyright (c) 2016 Klemens D. Morgenstern
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 
7 #ifndef BOOST_PROCESS_DETAIL_POSIX_BASIC_CMD_HPP_
8 #define BOOST_PROCESS_DETAIL_POSIX_BASIC_CMD_HPP_
9 
10 #include <boost/process/detail/posix/handler.hpp>
11 #include <boost/process/detail/posix/cmd.hpp>
12 #include <boost/algorithm/string/replace.hpp>
13 #include <boost/process/shell.hpp>
14 #include <boost/algorithm/string/trim.hpp>
15 #include <boost/algorithm/string/join.hpp>
16 #include <string>
17 #include <vector>
18 
19 namespace boost
20 {
21 namespace process
22 {
23 namespace detail
24 {
25 namespace posix
26 {
27 
28 
build_cmd_shell(const std::string & exe,std::vector<std::string> && data)29 inline std::string build_cmd_shell(const std::string & exe, std::vector<std::string> && data)
30 {
31     std::string st = exe;
32     for (auto & arg : data)
33     {
34         boost::replace_all(arg, "\"", "\\\"");
35 
36         auto it = std::find(arg.begin(), arg.end(), ' ');//contains space?
37         if (it != arg.end())//ok, contains spaces.
38         {
39             //the first one is put directly onto the output,
40             //because then I don't have to copy the whole string
41             arg.insert(arg.begin(), '"' );
42             arg += '"'; //thats the post one.
43         }
44 
45         if (!st.empty())//first one does not need a preceeding space
46             st += ' ';
47 
48         st += arg;
49     }
50     return  st ;
51 }
52 
build_args(const std::string & data)53 inline std::vector<std::string>  build_args(const std::string & data)
54 {
55     std::vector<std::string>  st;
56 
57     typedef std::string::const_iterator itr_t;
58 
59     //normal quotes outside can be stripped, inside ones marked as \" will be replaced.
60     auto make_entry = [](const itr_t & begin, const itr_t & end)
61     {
62         std::string data;
63         if ((*begin == '"') && (*(end-1) == '"'))
64             data.assign(begin+1, end-1);
65         else
66             data.assign(begin, end);
67 
68         boost::replace_all(data, "\\\"", "\"");
69         return data;
70 
71     };
72 
73     bool in_quote = false;
74 
75     auto part_beg = data.cbegin();
76     auto itr = data.cbegin();
77 
78     for (; itr != data.cend(); itr++)
79     {
80         if (*itr == '"')
81             in_quote ^= true;
82 
83         if (!in_quote && (*itr == ' '))
84         {
85             //alright, got a space
86 
87             if ((itr != data.cbegin()) && (*(itr -1) != ' ' ))
88                 st.push_back(make_entry(part_beg, itr));
89 
90             part_beg = itr+1;
91         }
92     }
93     if (part_beg != itr)
94         st.emplace_back(make_entry(part_beg, itr));
95 
96 
97     return st;
98 }
99 
100 template<typename Char>
101 struct exe_cmd_init;
102 
103 template<>
104 struct exe_cmd_init<char> : boost::process::detail::api::handler_base_ext
105 {
106     exe_cmd_init(const exe_cmd_init & ) = delete;
107     exe_cmd_init(exe_cmd_init && ) = default;
exe_cmd_initboost::process::detail::posix::exe_cmd_init108     exe_cmd_init(std::string && exe, std::vector<std::string> && args)
109             : exe(std::move(exe)), args(std::move(args)) {};
110     template <class Executor>
on_setupboost::process::detail::posix::exe_cmd_init111     void on_setup(Executor& exec)
112     {
113         if (exe.empty()) //cmd style
114         {
115             exec.exe = args.front().c_str();
116             exec.cmd_style = true;
117         }
118         else
119             exec.exe = &exe.front();
120 
121         cmd_impl = make_cmd();
122         exec.cmd_line = cmd_impl.data();
123     }
exe_argsboost::process::detail::posix::exe_cmd_init124     static exe_cmd_init exe_args(std::string && exe, std::vector<std::string> && args) {return exe_cmd_init(std::move(exe), std::move(args));}
cmdboost::process::detail::posix::exe_cmd_init125     static exe_cmd_init cmd     (std::string && cmd)
126     {
127         auto args = build_args(cmd);
128         return exe_cmd_init({}, std::move(args));
129     }
130 
exe_args_shellboost::process::detail::posix::exe_cmd_init131     static exe_cmd_init exe_args_shell(std::string&& exe, std::vector<std::string> && args)
132     {
133         auto cmd = build_cmd_shell(std::move(exe), std::move(args));
134 
135         std::vector<std::string> args_ = {"-c", std::move(cmd)};
136         std::string sh = shell().string();
137 
138         return exe_cmd_init(std::move(sh), std::move(args_));
139     }
cmd_shellboost::process::detail::posix::exe_cmd_init140     static exe_cmd_init cmd_shell(std::string&& cmd)
141     {
142         std::vector<std::string> args = {"-c", "\"" + cmd + "\""};
143         std::string sh = shell().string();
144 
145         return exe_cmd_init(
146                 std::move(sh),
147                 {std::move(args)});
148     }
149 private:
150     inline std::vector<char*> make_cmd();
151     std::string exe;
152     std::vector<std::string> args;
153     std::vector<char*> cmd_impl;
154 };
155 
make_cmd()156 std::vector<char*> exe_cmd_init<char>::make_cmd()
157 {
158     std::vector<char*> vec;
159     if (!exe.empty())
160         vec.push_back(&exe.front());
161 
162     if (!args.empty()) {
163         for (auto & v : args)
164             vec.push_back(&v.front());
165     }
166 
167     vec.push_back(nullptr);
168 
169     return vec;
170 }
171 
172 
173 }}}}
174 
175 #endif
176