Skip to content

Commit

Permalink
New Template of basic_escape_argv
Browse files Browse the repository at this point in the history
  • Loading branch information
fcharlie committed Jul 12, 2019
1 parent ef7110a commit f5b8806
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 108 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -53,6 +53,7 @@ bld/
*.pidb
*.svclog
*.scc
*.out


# Visual C++ cache files
Expand All @@ -70,4 +71,4 @@ ipch/
build/
dist/
.vscode/settings.json
.vscode/c_cpp_properties.json
.vscode/c_cpp_properties.json
311 changes: 204 additions & 107 deletions include/bela/escapeargv.hpp
@@ -1,107 +1,204 @@
/// EscapeArgv
#ifndef BELA_ESCAPE_ARGV_HPP
#define BELA_ESCAPE_ARGV_HPP
#pragma once
#include <string>
#include <string_view>

namespace bela {
namespace argv_internal {
inline std::wstring escape_argument(std::wstring_view ac) {
if (ac.empty()) {
return L"\"\"";
}
bool hasspace = false;
auto n = ac.size();
for (auto c : ac) {
switch (c) {
case L'"':
[[fallthrough]];
case L'\\':
n++;
break;
case ' ':
[[fallthrough]];
case '\t':
hasspace = true;
break;
default:
break;
}
}
if (hasspace) {
n += 2;
}
if (n == ac.size()) {
return std::wstring{ac};
}
std::wstring buf;
buf.reserve(n + 1);
if (hasspace) {
buf.push_back(L'"');
}
size_t slashes = 0;
for (auto c : ac) {
switch (c) {
case L'\\':
slashes++;
buf.push_back(L'\\');
break;
case L'"': {
for (; slashes > 0; slashes--) {
buf.push_back(L'\\');
}
buf.push_back(L'\\');
buf.push_back(c);
} break;
default:
slashes = 0;
buf.push_back(c);
break;
}
}
if (hasspace) {
for (; slashes > 0; slashes--) {
buf.push_back(L'\\');
}
buf.push_back(L'"');
}
return buf;
}
} // namespace argv_internal

class EscapeArgv {
public:
EscapeArgv() = default;
EscapeArgv(const EscapeArgv &) = delete;
EscapeArgv &operator=(const EscapeArgv &) = delete;
EscapeArgv &Assign(std::wstring_view arg0) {
saver_.assign(argv_internal::escape_argument(arg0));
return *this;
}
EscapeArgv &AssignNoEscape(std::wstring_view arg0) {
saver_.assign(arg0);
return *this;
}
EscapeArgv &Append(std::wstring_view aN) {
if (saver_.empty()) {
saver_.assign(argv_internal::escape_argument(aN));
return *this;
}
auto ea = argv_internal::escape_argument(aN);
saver_.reserve(saver_.size() + ea.size() + 1);
saver_.append(L" ").append(ea);
return *this;
}
std::wstring_view view() const { return std::wstring_view(saver_); }
wchar_t *data() { return saver_.data(); } // CreateProcessW require
const wchar_t *data() const { return saver_.data(); }
size_t size() const { return saver_.size(); }

private:
std::wstring saver_;
};

} // namespace bela

#endif
// Escape Argv
#ifndef BELA_ESCAPE_ARGV_HPP
#define BELA_ESCAPE_ARGV_HPP
#include <string_view>
#include "span.hpp"

namespace bela {

namespace argv_internal {
template <typename charT> std::basic_string_view<charT> empty_arg();
template <> std::string_view empty_arg() { return "\"\""; }
template <> std::wstring_view empty_arg() { return L"\"\""; }
} // namespace argv_internal

// basic escape argv
template <typename charT, typename Allocator = std::allocator<charT>>
class basic_escape_argv {
public:
typedef std::basic_string_view<charT> string_view_t;
typedef std::basic_string<charT, std::char_traits<charT>, Allocator> string_t;

template <typename... Args>
basic_escape_argv(string_view_t arg0, Args... arg) {
string_view_t svv[] = {arg0, arg...};
AssignFull(svv);
}
basic_escape_argv() = default;
basic_escape_argv(const basic_escape_argv &) = delete;
basic_escape_argv &operator=(const basic_escape_argv &) = delete;
// AssignFull
basic_escape_argv &AssignFull(bela::Span<string_view_t> args) {
struct arg_status {
unsigned len{0};
bool hasspace{false};
};
size_t totalsize = 0;
std::vector<arg_status> avs(args.size());
for (size_t i = 0; i < args.size(); i++) {
auto ac = args[i];
if (ac.empty()) {
avs[i].len = 2; // "\"\""
totalsize += 2;
continue;
}
auto n = static_cast<unsigned>(ac.size());
for (auto c : ac) {
switch (c) {
case L'"':
[[fallthrough]];
case L'\\':
n++;
break;
case ' ':
[[fallthrough]];
case '\t':
avs[i].hasspace = true;
break;
default:
break;
}
}
if (avs[i].hasspace) {
n += 2;
}
avs[i].len = n;
totalsize += n;
}
saver.reserve(args.size() + totalsize);
for (size_t i = 0; i < args.size(); i++) {
auto ac = args[i];
if (!saver.empty()) {
saver.push_back(' ');
}
if (ac.empty()) {
saver.append(argv_internal::empty_arg<charT>());
continue;
}
if (ac.size() == avs[i].len) {
saver.append(ac);
continue;
}
if (avs[i].hasspace) {
saver += '"';
}
size_t slashes = 0;
for (auto c : ac) {
switch (c) {
case '\\':
slashes++;
saver += '\\';
break;
case L'"': {
for (; slashes > 0; slashes--) {
saver += '\\';
}
saver += '\\';
saver += c;
} break;
default:
slashes = 0;
saver += c;
break;
}
}
if (avs[i].hasspace) {
for (; slashes > 0; slashes--) {
saver += '\\';
}
saver += '"';
}
}
return *this;
}
basic_escape_argv &AssignNoEscape(string_view_t a0) {
saver.assign(a0);
return *this;
}
basic_escape_argv &Assign(string_view_t arg0) {
saver.clear();
argv_escape_internal(arg0, saver);
return *this;
}
basic_escape_argv &Append(string_view_t aN) {
argv_escape_internal(aN, saver);
return *this;
}
const charT *data() const { return saver.data(); }
charT *data() { return saver.data(); }
string_view_t sv() const { return saver; }
size_t size() const { return saver.size(); }

private:
void argv_escape_internal(string_view_t sv, string_t &s) {
if (!s.empty()) {
s.push_back(' ');
}
if (sv.empty()) {
s.append(argv_internal::empty_arg<charT>());
return;
}
bool hasspace = false;
auto n = sv.size();
for (auto c : sv) {
switch (c) {
case '"':
[[fallthrough]];
case '\\':
n++;
break;
case ' ':
[[fallthrough]];
case '\t':
hasspace = true;
break;
default:
break;
}
}
if (hasspace) {
n += 2;
}
if (n == sv.size()) {
s.append(sv);
return;
}
s.reserve(s.size() + sv.size() + 1);
if (hasspace) {
s += '"';
}
size_t slashes = 0;
for (auto c : sv) {
switch (c) {
case '\\':
slashes++;
s += '\\';
break;
case L'"': {
for (; slashes > 0; slashes--) {
s += '\\';
}
s += '\\';
s += c;
} break;
default:
slashes = 0;
s += c;
break;
}
}
if (hasspace) {
for (; slashes > 0; slashes--) {
s += '\\';
}
s += '"';
}
}

string_t saver;
};

using EscapeArgv = basic_escape_argv<wchar_t>;
} // namespace bela

#endif

0 comments on commit f5b8806

Please sign in to comment.