Add a bash-based implementation of replace_bash_vars().
You can choose at configure time if you want the old c++ variable string replacement implementation or the new one that execs bash -c 'echo blah blah'. It supports using both EnvReal and EnvFake.
This commit is contained in:
parent
a1da8d1053
commit
c3cab484b6
5 changed files with 132 additions and 15 deletions
|
@ -12,5 +12,14 @@ conf.set('PROJECT_NAME', meson.project_name())
|
|||
conf.set('PROJECT_VERSION', meson.project_version())
|
||||
conf.set('COPYRIGHT_YEAR', '2020')
|
||||
conf.set('PROJECT_REPO_URL', 'https://alarmpi.no-ip.org/gitan/King_DuckZ/user-gcc')
|
||||
if get_option('bash_method') == 'naive'
|
||||
conf.set('USE_NAIVE_BASH_METHOD', true)
|
||||
else
|
||||
conf.set('USE_NATIVE_BASH_METHOD', true)
|
||||
bash_found = find_program('bash', required: false)
|
||||
if not bash_found.found()
|
||||
warning('bash was not found on your system, you will still be able to build ' + meson.project_name() + ' but you won\'t be able to run it correctly')
|
||||
endif
|
||||
endif
|
||||
|
||||
subdir('src')
|
||||
|
|
1
meson_options.txt
Normal file
1
meson_options.txt
Normal file
|
@ -0,0 +1 @@
|
|||
option('bash_method', type: 'combo', description: 'Select the implementation of the Bash script parsing method', choices: ['naive', 'native'], value: 'native')
|
|
@ -21,3 +21,5 @@
|
|||
#define PROJECT_VERSION "@PROJECT_VERSION@"
|
||||
#define COPYRIGHT_YEAR "@COPYRIGHT_YEAR@"
|
||||
#define PROJECT_REPO_URL "@PROJECT_REPO_URL@"
|
||||
#mesondefine USE_NATIVE_BASH_METHOD
|
||||
#mesondefine USE_NAIVE_BASH_METHOD
|
||||
|
|
|
@ -59,6 +59,7 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
duck::RunContext syspaths;
|
||||
syspaths.add_to_env(g_simple_colours);
|
||||
syspaths.env().set("argv0", PROJECT_NAME);
|
||||
#if !defined(NDEBUG)
|
||||
std::cout << syspaths << '\n';
|
||||
#endif
|
||||
|
|
|
@ -19,25 +19,30 @@
|
|||
#include "config.h"
|
||||
#include "run_context.hpp"
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#if defined(USE_NAIVE_BASH_METHOD)
|
||||
# include <cctype>
|
||||
#else
|
||||
# include "run_cmd.hpp"
|
||||
# include "env_real.hpp"
|
||||
# include "env_fake.hpp"
|
||||
# include <spawn.h>
|
||||
# include <unistd.h>
|
||||
# include <sys/wait.h>
|
||||
# include <stdexcept>
|
||||
# include <cstdlib>
|
||||
# include <poll.h>
|
||||
#endif
|
||||
|
||||
namespace duck {
|
||||
#if defined(USE_NAIVE_BASH_METHOD)
|
||||
|
||||
namespace {
|
||||
struct VarInfo {
|
||||
std::string_view name;
|
||||
std::size_t orig_len{};
|
||||
};
|
||||
|
||||
//std::string replaced_all (std::string text, std::string_view search, std::string_view repl) {
|
||||
// std::size_t pos = 0;
|
||||
// while ((pos = text.find(search, pos)) != std::string::npos) {
|
||||
// text.replace(pos, search.size(), repl);
|
||||
// pos += repl.size();
|
||||
// }
|
||||
// return text;
|
||||
//}
|
||||
|
||||
VarInfo extract_var_name (const RunContext& sp, std::string_view text) {
|
||||
VarInfo extract_var_name (const RunContext& rc, std::string_view text) {
|
||||
assert(not text.empty());
|
||||
assert(text.front() == '$');
|
||||
if (text.size() == 1) {
|
||||
|
@ -59,7 +64,7 @@ namespace duck {
|
|||
std::string_view token(text.substr(2, end - 2));
|
||||
std::string_view retval(token.empty() || token.front() != '!' ? token : token.substr(1));
|
||||
if (not token.empty() and token.front() == '!') {
|
||||
return {sp.env()[retval], end + 1};
|
||||
return {rc.env()[retval], end + 1};
|
||||
}
|
||||
else {
|
||||
return {retval, end + 1};
|
||||
|
@ -76,7 +81,7 @@ namespace duck {
|
|||
}
|
||||
} //unnamed namespace
|
||||
|
||||
std::string replace_bash_vars (const RunContext& sp, std::string_view text) {
|
||||
std::string replace_bash_vars (const RunContext& rc, std::string_view text) {
|
||||
std::size_t pos = 0;
|
||||
std::size_t prev_start = 0;
|
||||
std::string rtext;
|
||||
|
@ -84,7 +89,7 @@ namespace duck {
|
|||
|
||||
while ((pos = text.find('$', pos)) != std::string::npos) {
|
||||
if (0 == pos or rtext[pos-1] != '\\') {
|
||||
auto var = extract_var_name(sp, text.substr(pos));
|
||||
auto var = extract_var_name(rc, text.substr(pos));
|
||||
if (not var.orig_len) {
|
||||
++pos;
|
||||
continue;
|
||||
|
@ -95,7 +100,7 @@ namespace duck {
|
|||
if (var.name == "argv0")
|
||||
rtext += PROJECT_NAME;
|
||||
else
|
||||
rtext += sp.env()[var.name];
|
||||
rtext += rc.env()[var.name];
|
||||
pos += var.orig_len;
|
||||
prev_start = pos;
|
||||
}
|
||||
|
@ -107,4 +112,103 @@ namespace duck {
|
|||
rtext.append(text.begin() + prev_start, text.end());
|
||||
return rtext;
|
||||
}
|
||||
|
||||
#elif defined(USE_NATIVE_BASH_METHOD)
|
||||
|
||||
namespace {
|
||||
std::string replaced_all (std::string text, std::string_view search, std::string_view repl) {
|
||||
std::size_t pos = 0;
|
||||
while ((pos = text.find(search, pos)) != std::string::npos) {
|
||||
text.replace(pos, search.size(), repl);
|
||||
pos += repl.size();
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
struct Pipes {
|
||||
Pipes() {
|
||||
if(pipe(cout_pipe) or pipe(cerr_pipe))
|
||||
throw std::runtime_error("Error in pipe() in replace_bash_vars()");
|
||||
}
|
||||
|
||||
~Pipes() noexcept {
|
||||
close(cout_pipe[0]), close(cerr_pipe[0]);
|
||||
close(cout_pipe[1]), close(cerr_pipe[1]);
|
||||
}
|
||||
|
||||
void close_child_side() noexcept {
|
||||
close(cout_pipe[1]), close(cerr_pipe[1]);
|
||||
cout_pipe[1] = cerr_pipe[1] = 0;
|
||||
}
|
||||
|
||||
int cout_pipe[2];
|
||||
int cerr_pipe[2];
|
||||
};
|
||||
|
||||
struct FileAction {
|
||||
FileAction() { posix_spawn_file_actions_init(&action); }
|
||||
~FileAction() noexcept { posix_spawn_file_actions_destroy(&action); }
|
||||
|
||||
posix_spawn_file_actions_t action;
|
||||
};
|
||||
} //unnamed namespace
|
||||
|
||||
std::string replace_bash_vars (const RunContext& rc, std::string_view text) {
|
||||
Pipes p;
|
||||
FileAction fa;
|
||||
|
||||
//see: https://stackoverflow.com/questions/13893085/posix-spawnp-and-piping-child-output-to-a-string
|
||||
posix_spawn_file_actions_addclose(&fa.action, p.cout_pipe[0]);
|
||||
posix_spawn_file_actions_addclose(&fa.action, p.cerr_pipe[0]);
|
||||
posix_spawn_file_actions_adddup2(&fa.action, p.cout_pipe[1], 1);
|
||||
posix_spawn_file_actions_adddup2(&fa.action, p.cerr_pipe[1], 2);
|
||||
posix_spawn_file_actions_addclose(&fa.action, p.cout_pipe[1]);
|
||||
posix_spawn_file_actions_addclose(&fa.action, p.cerr_pipe[1]);
|
||||
|
||||
char shell_cpy[] = {'b', 'a', 's', 'h', '\0'};
|
||||
char c_arg_cpy[] = {'-', 'c', '\0'};
|
||||
std::vector<char> command;
|
||||
{
|
||||
std::string san_text = "\"" + replaced_all(to_string(text), "\"", "\\\"") + "\"";
|
||||
command.reserve(5 + san_text.size() + 1);
|
||||
command.insert(command.begin(), "echo ", "echo " + 5);
|
||||
std::copy(san_text.begin(), san_text.end(), std::back_inserter(command));
|
||||
command.push_back('\0');
|
||||
}
|
||||
char* const argv[] = {shell_cpy, c_arg_cpy, command.data(), nullptr};
|
||||
|
||||
const auto new_env = rc.env().to_c_array();
|
||||
pid_t pid;
|
||||
const int spawn_ret = posix_spawnp(&pid, argv[0], &fa.action, nullptr, argv, new_env.get());
|
||||
p.close_child_side();
|
||||
|
||||
if (spawn_ret)
|
||||
throw std::runtime_error("posix_spawnp() failed with code " + std::to_string(spawn_ret));
|
||||
|
||||
// Read from pipes
|
||||
std::string retval;
|
||||
std::string buffer(1024,' ');
|
||||
std::vector<pollfd> plist = { {p.cout_pipe[0], POLLIN}, {p.cerr_pipe[0], POLLIN} };
|
||||
constexpr const int timeout = -1;
|
||||
for (int rval; (rval = poll(&plist[0], plist.size(), timeout)) > 0;) {
|
||||
if (plist[0].revents & POLLIN) {
|
||||
const int bytes_read = read(p.cout_pipe[0], &buffer[0], buffer.length());
|
||||
retval.append(buffer.begin(), buffer.begin() + bytes_read);
|
||||
}
|
||||
else if (plist[1].revents & POLLIN) {
|
||||
/*const int bytes_read =*/ read(p.cerr_pipe[0], &buffer[0], buffer.length());
|
||||
//cout << buffer.substr(0, static_cast<size_t>(bytes_read)) << "\n";
|
||||
}
|
||||
else break; // nothing left to read
|
||||
}
|
||||
|
||||
int exit_code;
|
||||
waitpid(pid, &exit_code, 0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
# error "None of the known bash parsing method has been selected by the user"
|
||||
#endif
|
||||
} //namespace duck
|
||||
|
|
Loading…
Reference in a new issue