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('PROJECT_VERSION', meson.project_version())
|
||||||
conf.set('COPYRIGHT_YEAR', '2020')
|
conf.set('COPYRIGHT_YEAR', '2020')
|
||||||
conf.set('PROJECT_REPO_URL', 'https://alarmpi.no-ip.org/gitan/King_DuckZ/user-gcc')
|
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')
|
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 PROJECT_VERSION "@PROJECT_VERSION@"
|
||||||
#define COPYRIGHT_YEAR "@COPYRIGHT_YEAR@"
|
#define COPYRIGHT_YEAR "@COPYRIGHT_YEAR@"
|
||||||
#define PROJECT_REPO_URL "@PROJECT_REPO_URL@"
|
#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;
|
duck::RunContext syspaths;
|
||||||
syspaths.add_to_env(g_simple_colours);
|
syspaths.add_to_env(g_simple_colours);
|
||||||
|
syspaths.env().set("argv0", PROJECT_NAME);
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
std::cout << syspaths << '\n';
|
std::cout << syspaths << '\n';
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -19,25 +19,30 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "run_context.hpp"
|
#include "run_context.hpp"
|
||||||
#include <cassert>
|
#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 {
|
namespace duck {
|
||||||
|
#if defined(USE_NAIVE_BASH_METHOD)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct VarInfo {
|
struct VarInfo {
|
||||||
std::string_view name;
|
std::string_view name;
|
||||||
std::size_t orig_len{};
|
std::size_t orig_len{};
|
||||||
};
|
};
|
||||||
|
|
||||||
//std::string replaced_all (std::string text, std::string_view search, std::string_view repl) {
|
VarInfo extract_var_name (const RunContext& rc, std::string_view text) {
|
||||||
// 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) {
|
|
||||||
assert(not text.empty());
|
assert(not text.empty());
|
||||||
assert(text.front() == '$');
|
assert(text.front() == '$');
|
||||||
if (text.size() == 1) {
|
if (text.size() == 1) {
|
||||||
|
@ -59,7 +64,7 @@ namespace duck {
|
||||||
std::string_view token(text.substr(2, end - 2));
|
std::string_view token(text.substr(2, end - 2));
|
||||||
std::string_view retval(token.empty() || token.front() != '!' ? token : token.substr(1));
|
std::string_view retval(token.empty() || token.front() != '!' ? token : token.substr(1));
|
||||||
if (not token.empty() and token.front() == '!') {
|
if (not token.empty() and token.front() == '!') {
|
||||||
return {sp.env()[retval], end + 1};
|
return {rc.env()[retval], end + 1};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return {retval, end + 1};
|
return {retval, end + 1};
|
||||||
|
@ -76,7 +81,7 @@ namespace duck {
|
||||||
}
|
}
|
||||||
} //unnamed namespace
|
} //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 pos = 0;
|
||||||
std::size_t prev_start = 0;
|
std::size_t prev_start = 0;
|
||||||
std::string rtext;
|
std::string rtext;
|
||||||
|
@ -84,7 +89,7 @@ namespace duck {
|
||||||
|
|
||||||
while ((pos = text.find('$', pos)) != std::string::npos) {
|
while ((pos = text.find('$', pos)) != std::string::npos) {
|
||||||
if (0 == pos or rtext[pos-1] != '\\') {
|
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) {
|
if (not var.orig_len) {
|
||||||
++pos;
|
++pos;
|
||||||
continue;
|
continue;
|
||||||
|
@ -95,7 +100,7 @@ namespace duck {
|
||||||
if (var.name == "argv0")
|
if (var.name == "argv0")
|
||||||
rtext += PROJECT_NAME;
|
rtext += PROJECT_NAME;
|
||||||
else
|
else
|
||||||
rtext += sp.env()[var.name];
|
rtext += rc.env()[var.name];
|
||||||
pos += var.orig_len;
|
pos += var.orig_len;
|
||||||
prev_start = pos;
|
prev_start = pos;
|
||||||
}
|
}
|
||||||
|
@ -107,4 +112,103 @@ namespace duck {
|
||||||
rtext.append(text.begin() + prev_start, text.end());
|
rtext.append(text.begin() + prev_start, text.end());
|
||||||
return rtext;
|
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
|
} //namespace duck
|
||||||
|
|
Loading…
Reference in a new issue