John Weiss wrote:
> I just ran a quick test. Any Unix shell treats "--option='foo bar'"
> as a single element of "*argv[]". The single-quotes protected the
> space from the shell. (I tested with bash, ash, csh, tcsh, ksh, and
> bsh.)
>
> For Windows, however I wouldn't be surprised if whitespace chars
> weren't escaped. Just another one of those platform differences
> that'll need special handling.
I understand, really I do. See here for a sophisticated way of splitting a
string into an argv array:
http://www.devel.lyx.org/~leeming/libs/child/doc/html/parse_pseudo_command_line.html
Equivalent URL: http://tinyurl.com/4c4v9
I attach the code to recombine an argv array following the wsh quoting
rules.
--
Angus
/**
* \file make_command_line.cpp
* \author Angus Leeming
*/
#include "make_command_line.h"
#include "process_data.h"
#include <sstream>
using std::ostream;
using std::ostringstream;
using std::string;
using std::vector;
namespace child {
namespace {
string const protect_wsh_arg(string const & old_arg)
{
if (old_arg.empty())
return old_arg;
ostringstream new_arg;
string::const_iterator it = old_arg.begin();
string::const_iterator const end = old_arg.end();
// Inside a "-protected block, any " character must be escaped.
// Note also that all adjacent \ characters in a block must be escaped
// only if the next character following them is a " (which must
// also be escaped).
// Thus, using <...> to delimit the string,
// <foo \ bar> becomes <"foo \ bar">,
// <foo " bar> becomes <"foo \" bar"> and
// <foo \\" bar> becomes <"foo \\\\\" bar">.
// See http://article.gmane.org/gmane.comp.lib.boost.user/3005
// or http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
new_arg << '"';
for (; it != end; ++it) {
char const c = *it;
if (c == '"') {
new_arg << "\\\"";
} else if (c == '\\') {
string::const_iterator it2 = it + 1;
while (it2 != end && *it2 == '\\')
++it2;
string::size_type nbackslashes = std::distance(it, it2);
if (it2 != end && *it2 == '"')
nbackslashes *= 2;
new_arg << string(nbackslashes, '\\');
it = it2 - 1;
} else {
new_arg << c;
}
}
new_arg << '"';
return new_arg.str();
}
void build_wsh_command_line(ostream & os,
vector<string> const & argv)
{
vector<string>::const_iterator it = argv.begin();
vector<string>::const_iterator const end = argv.end();
for (; it != end; ++it) {
string const arg = protect_wsh_arg(*it);
if (!arg.empty()) {
if (os.tellp() > 0)
os << ' ';
os << arg;
}
}
}
} // namespace anon
string const make_wsh_command_line(process_data const & data)
{
if (data.argv().empty())
return string();
ostringstream line;
build_wsh_command_line(line, data.argv());
// standard stream stuff to go here.
return line.str();
}
string const make_wsh_command_line(vector<string> const & argv)
{
if (argv.empty())
return string();
ostringstream line;
build_wsh_command_line(line, argv);
return line.str();
}
} // namespace child