diff --git a/CMakeLists.txt b/CMakeLists.txt index b92db5c..f79f41f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,17 +85,17 @@ RAGEL_TARGET(value value.rl value.cpp COMPILE_FLAGS "-p -G2") # need to copy all OUTPUT file to the build dir add_custom_command( - OUTPUT phase2-parser.cpp phase2-parser.h phase2-parser.out - COMMAND lemon++ phase2-parser.lemon - COMMAND mv -f phase2-parser.cpp phase2-parser.h phase2-parser.out ${CMAKE_CURRENT_BINARY_DIR}/ - MAIN_DEPENDENCY phase2-parser.lemon + OUTPUT phase3.cpp phase3.h phase3.out + COMMAND lemon++ phase3.lemon + COMMAND mv -f phase3.cpp phase3.h phase3.out ${CMAKE_CURRENT_BINARY_DIR}/ + MAIN_DEPENDENCY phase3.lemon WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp - mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp - phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp + mpw-shell-parser.cpp mpw_parser.cpp value.cpp mpw-shell-quote.cpp + phase1.cpp phase2.cpp phase3.cpp command.cpp environment.cpp builtins.cpp pathnames.cpp macroman.cpp cxx/mapped_file.cpp diff --git a/builtins.cpp b/builtins.cpp index ccc8fd8..80b8ee2 100644 --- a/builtins.cpp +++ b/builtins.cpp @@ -855,12 +855,15 @@ int builtin_which(Environment &env, const std::vector &tokens, cons "catenate", "directory", "echo", + "execute", "exists", "export", + "false", // not in MPW "parameters", "quote", "set", "shift", + "true", // not in MPW "unalias", "unexport", "unset", @@ -1034,3 +1037,23 @@ int builtin_true(Environment &, const std::vector &, const fdmask & int builtin_false(Environment &, const std::vector &, const fdmask &) { return 1; } + + +int builtin_execute(Environment &e, const std::vector &tokens, const fdmask &fds) { + + // runs argv[1] in the current environment. unlike MPW, argv[1] must be a script. + // special enhancement -- '-' means execute stdin. + + if (tokens.size() < 2) return 0; + std::string filename = tokens[1]; + if (filename == "-") { + // since we're parsing stdin, don't let any children read it. [???] + fdset new_fds; + int fd = open("/dev/null", O_RDONLY); + new_fds.set(0, fd); + + return read_fd(e, fds[0], new_fds | fds); + } + + return read_file(e, filename, fds); +} diff --git a/builtins.h b/builtins.h index 302aefa..0b3cb6b 100644 --- a/builtins.h +++ b/builtins.h @@ -25,6 +25,12 @@ int builtin_which(Environment &e, const std::vector &, const fdmask int builtin_alias(Environment &e, const std::vector &, const fdmask &); int builtin_unalias(Environment &e, const std::vector &, const fdmask &); +int builtin_execute(Environment &e, const std::vector &, const fdmask &); +int builtin_true(Environment &e, const std::vector &, const fdmask &); +int builtin_false(Environment &e, const std::vector &, const fdmask &); +int builtin_execute(Environment &e, const std::vector &, const fdmask &); + + int builtin_evaluate(Environment &e, std::vector &&, const fdmask &); #endif \ No newline at end of file diff --git a/command.cpp b/command.cpp index 116135f..94e87aa 100644 --- a/command.cpp +++ b/command.cpp @@ -1,5 +1,5 @@ #include "command.h" -#include "phase2-parser.h" +#include "phase3.h" #include "environment.h" #include "fdset.h" #include "builtins.h" @@ -198,6 +198,7 @@ namespace { {"catenate", builtin_catenate}, {"directory", builtin_directory}, {"echo", builtin_echo}, + {"execute", builtin_execute}, {"exists", builtin_exists}, {"export", builtin_export}, {"parameters", builtin_parameters}, @@ -209,6 +210,10 @@ namespace { {"unset", builtin_unset}, {"version", builtin_version}, {"which", builtin_which}, + + // not in MPW. + {"true", builtin_true}, + {"false", builtin_false}, }; @@ -278,9 +283,6 @@ namespace { } - -//std::string expand_vars(const std::string &s, const class Environment &); - command::~command() {} @@ -293,7 +295,7 @@ command::~command() template -int exec(std::string command, Environment &env, bool throwup, F &&fx) { +int exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx) { bool echo = true; int rv = 0; @@ -302,7 +304,7 @@ int exec(std::string command, Environment &env, bool throwup, F &&fx) { try { process p; - command = expand_vars(command, env); + command = expand_vars(command, env, fds); auto tokens = tokenize(command, false); if (tokens.empty()) return 0; parse_tokens(std::move(tokens), p); @@ -330,7 +332,7 @@ int exec(std::string command, Environment &env, bool throwup, F &&fx) { int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) { - return exec(text, env, throwup, [&](process &p){ + return exec(text, env, fds, throwup, [&](process &p){ fdmask newfds = p.fds | fds; @@ -367,7 +369,7 @@ int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) { } template -int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){ +int eval_exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx){ std::string name; bool echo = true; @@ -376,7 +378,7 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){ if (control_c) throw execution_of_input_terminated(); try { - command = expand_vars(command, env); + command = expand_vars(command, env, fds); auto tokens = tokenize(command, true); if (tokens.empty()) return 0; @@ -406,29 +408,8 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){ int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) { -#if 0 - if (control_c) throw execution_of_input_terminated(); - std::string s = expand_vars(text, env); - - env.set("command", "evaluate"); - - env.echo("%s", s.c_str()); - - try { - auto tokens = tokenize(s, true); - if (tokens.empty()) return 0; - - int status = builtin_evaluate(env, std::move(tokens), fds); - - return env.status(status, throwup); - } catch (std::exception &e) { - fprintf(stderr, "%s\n", e.what()); - return env.status(1, throwup); - } -#endif - - return eval_exec(text, env, throwup, [&](token_vector &tokens){ + return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){ env.set("command", "evaluate"); return builtin_evaluate(env, std::move(tokens), fds); }); @@ -439,7 +420,7 @@ int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) int break_command::execute(Environment &env, const fdmask &fds, bool throwup) { - return eval_exec(text, env, throwup, [&](token_vector &tokens){ + return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){ env.set("command", "break"); if (!env.loop()) throw break_error(); int status = evaluate(BREAK, std::move(tokens), env); @@ -447,30 +428,11 @@ int break_command::execute(Environment &env, const fdmask &fds, bool throwup) { return status; }); -#if 0 - if (control_c) throw execution_of_input_terminated(); - - std::string s = expand_vars(text, env); - - env.set("command", "break"); - - env.echo("%s", s.c_str()); - if (!env.loop()) { - fputs("### MPW Shell - Break must be within for or loop.\n", stderr); - return env.status(-3, throwup); - } - - // check/evaluate if clause. - int status = evaluate(BREAK, s, env); - if (status > 0) - throw break_command_t(); - return env.status(status, throwup); -#endif } int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) { - return eval_exec(text, env, throwup, [&](token_vector &tokens){ + return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){ env.set("command", "continue"); if (!env.loop()) throw continue_error(); int status = evaluate(CONTINUE, std::move(tokens), env); @@ -478,26 +440,6 @@ int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) return status; }); -#if 0 - if (control_c) throw execution_of_input_terminated(); - - std::string s = expand_vars(text, env); - - env.set("command", "continue"); - - env.echo("%s", s.c_str()); - if (!env.loop()) { - fputs("### MPW Shell - Continue must be within for or loop.\n", stderr); - return env.status(-3, throwup); - } - - - // check/evaluate if clause. - int status = evaluate(CONTINUE, s, env); - if (status > 0) - throw continue_command_t(); - return env.status(status, throwup); -#endif } int or_command::execute(Environment &e, const fdmask &fds, bool throwup) { @@ -577,7 +519,7 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) { return e.status(-3); } - std::string s = expand_vars(text, e); + std::string s = expand_vars(text, e, fds); e.echo("%s", s.c_str()); @@ -601,7 +543,7 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) { } template -int begin_end_exec(std::string begin, std::string end, Environment &env, bool throwup, F &&fx) { +int begin_end_exec(std::string begin, std::string end, Environment &env, const fdmask &fds, bool throwup, F &&fx) { bool echo = true; @@ -611,8 +553,8 @@ int begin_end_exec(std::string begin, std::string end, Environment &env, bool th try { process p; - begin = expand_vars(begin, env); - end = expand_vars(end, env); + begin = expand_vars(begin, env, fds); + end = expand_vars(end, env, fds); auto b = tokenize(begin, true); auto e = tokenize(end, false); @@ -639,12 +581,11 @@ int begin_end_exec(std::string begin, std::string end, Environment &env, bool th } return env.status(rv, throwup); - } int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) { - return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){ + return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){ env.set("command", type == BEGIN ? "end" : ")"); if (b.size() != 1) { @@ -664,53 +605,12 @@ int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) { return rv; }); - -#if 0 - - std::string b = expand_vars(begin, env); - std::string e = expand_vars(end, env); - - - env.set("command", type == BEGIN ? "end" : ")"); - - // echo! - env.echo("%s ... %s", - b.c_str(), - e.c_str() - ); - - - - // tokenize the begin and end commands. - // begin may not have extra arguments. end may have redirection. - - auto bt = tokenize(b, true); - if (bt.size() != 1) { - fprintf(stderr, "### Begin - Too many parameters were specified.\n"); - fprintf(stderr, "Usage - Begin\n"); - return env.status(-3); - } - - fdmask newfds; - int status = check_ends(e, newfds); - newfds |= fds; - if (status) return env.status(status); - - int rv; - env.indent_and([&]{ - rv = vector_command::execute(env, newfds); - }); - - env.echo("%s", type == BEGIN ? "end" : ")"); - - return env.status(rv); -#endif } int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) { - return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){ + return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){ env.set("command", "end"); if (b.size() != 1) { @@ -742,58 +642,11 @@ int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) { return rv; }); - - -#if 0 - std::string b = expand_vars(begin, env); - std::string e = expand_vars(end, env); - - - env.set("command", "end"); - - // echo! - env.echo("%s ... %s", - b.c_str(), - e.c_str() - ); - - // check for extra tokens... - auto bt = tokenize(b, true); - if (bt.size() != 1) { - fprintf(stderr, "### Loop - Too many parameters were specified.\n"); - fprintf(stderr, "Usage - Loop\n"); - return env.status(-3); - } - - fdmask newfds; - int status = check_ends(e, newfds); - newfds |= fds; - if (status) return env.status(status); - - int rv = 0; - for(;;) { - - if (control_c) throw execution_of_input_terminated(); - - try { - env.loop_indent_and([&]{ - rv = vector_command::execute(env, newfds); - }); - } - catch (break_command_t &ex) { - env.echo("end"); - break; - } - catch (continue_command_t &ex) {} - env.echo("end"); - } - return env.status(rv); -#endif } int for_command::execute(Environment &env, const fdmask &fds, bool throwup) { - return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){ + return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){ env.set("command", "end"); @@ -827,56 +680,6 @@ int for_command::execute(Environment &env, const fdmask &fds, bool throwup) { return rv; }); - -#if 0 - std::string b = expand_vars(begin, env); - std::string e = expand_vars(end, env); - - - env.set("command", "end"); - - // echo! - env.echo("%s ... %s", - b.c_str(), - e.c_str() - ); - - // check for extra tokens... - auto bt = tokenize(b, true); - if (bt.size() < 3 || strcasecmp(bt[2].string.c_str(), "in")) { - fprintf(stderr, "### For - Missing in keyword.\n"); - fprintf(stderr, "Usage - For name in [word...]\n"); - return env.status(-3); - } - - fdmask newfds; - int status = check_ends(e, newfds); - newfds |= fds; - if (status) return env.status(status); - - int rv = 0; - for (int i = 3; i < bt.size(); ++i ) { - - if (control_c) throw execution_of_input_terminated(); - - env.set(bt[1].string, bt[i].string); - - try { - env.loop_indent_and([&]{ - rv = vector_command::execute(env, newfds); - }); - } - catch (break_command_t &ex) { - env.echo("end"); - break; - } - catch (continue_command_t &ex) {} - env.echo("end"); - } - - return env.status(rv); - -#endif } @@ -901,7 +704,7 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) { int tmp; if (first) { first = false; - tmp = begin_end_exec(c->clause, end, env, false, [&](token_vector &b, process &p){ + tmp = begin_end_exec(c->clause, end, env, fds, false, [&](token_vector &b, process &p){ newfds = p.fds | fds; @@ -923,7 +726,7 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) { } else { // second... - tmp = eval_exec(c->clause, env, false, [&](token_vector &b){ + tmp = eval_exec(c->clause, env, fds, false, [&](token_vector &b){ if (skip || error) return 0; int status = evaluate(c->type, std::move(b), env); @@ -946,49 +749,5 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) { env.echo("end"); if (error) return env.status(error, throwup); return env.status(rv, throwup); - -#if 0 - - - - - int rv = 0; - bool ok = false; - - std::string e = expand_vars(end, env); - - env.set("command", "end"); - - // parse end for indirection. - fdmask newfds; - int status = check_ends(e, newfds); - newfds |= fds; - if (status) { - rv = status; - ok = true; - } - - for (auto &c : clauses) { - std::string s = expand_vars(c->clause, env); - - if (c->type == IF) - env.echo("%s ... %s", s.c_str(), e.c_str()); - else - env.echo("%s", s.c_str()); - - if (ok) continue; - - int tmp = evaluate(c->type, s, env); - if (tmp < 0) { ok = true; rv = tmp; continue; } - if (tmp == 0) continue; - - env.indent_and([&](){ - rv = c->execute(env, newfds); - }); - } - - env.echo("end"); - return env.status(rv); -#endif } diff --git a/command.h b/command.h index 68b07b2..79524f6 100644 --- a/command.h +++ b/command.h @@ -6,7 +6,7 @@ #include #include #include -#include "phase2-parser.h" +#include "phase3.h" typedef std::unique_ptr command_ptr; typedef std::vector command_ptr_vector; diff --git a/mpw-shell-expand.rl b/mpw-shell-expand.rl index f778bb8..04bcc17 100644 --- a/mpw-shell-expand.rl +++ b/mpw-shell-expand.rl @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include @@ -42,16 +44,29 @@ fgoto *xcs; } - action einit{ /* einit */ ev.clear(); xcs = fcurs; fgoto estring_state; } - action epush{ /* epush */ ev.push_back(fc); } - action efinish1{ + action einit { /* einit */ ev.clear(); xcs = fcurs; fgoto estring_state; } + action epush { /* epush */ ev.push_back(fc); } + action efinish1 { /* efinish1 */ + + /* throw std::runtime_error("MPW Shell - `...` not yet supported."); + */ + + std::string s = subshell(ev, env, fds); + scratch.append(s); + fgoto *xcs; } - action efinish2{ + action efinish2 { /* efinish2 */ - throw std::runtime_error("MPW Shell - `...` not yet supported."); + + std::string s = subshell(ev, env, fds); + for (auto c : s) { + if (c == '\'' || c == '"' ) scratch.push_back(escape); + scratch.push_back(c); + } + fgoto *xcs; } @@ -110,9 +125,48 @@ namespace { %% write data; + + +std::string subshell(const std::string &s, Environment &env, const fdmask &fds) { + + + char temp[32] = "/tmp/mpw-shell-XXXXXXXX"; + + int fd = mkstemp(temp); + unlink(temp); + + fdset new_fds; + new_fds.set(1, fd); + + int rv = 0; + env.indent_and([&](){ + + rv = read_string(env, s, new_fds | fds); + + }); + + std::string tmp; + lseek(fd, 0, SEEK_SET); + for(;;) { + uint8_t buffer[1024]; + ssize_t len = read(fd, buffer, sizeof(buffer)); + if (len == 0) break; + if (len < 0) { + if (errno == EINTR) continue; + throw std::system_error(errno, std::system_category(), "read"); + } + tmp.append(buffer, buffer + len); + } + std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](uint8_t x){ + if (x == '\r' || x == '\n') x = ' '; + return x; + }); + return tmp; } -std::string expand_vars(const std::string &s, const Environment &env) { +} + +std::string expand_vars(const std::string &s, Environment &env, const fdmask &fds) { if (s.find_first_of("{`", 0, 2) == s.npos) return s; int cs; diff --git a/mpw-shell.cpp b/mpw-shell.cpp index 5604164..8c2a197 100644 --- a/mpw-shell.cpp +++ b/mpw-shell.cpp @@ -12,14 +12,12 @@ #include #include "mpw-shell.h" +#include "mpw_parser.h" + #include "fdset.h" #include "macroman.h" -#include "phase1.h" -#include "phase2.h" -#include "command.h" - #include "cxx/mapped_file.h" #include "cxx/filesystem.h" #include "cxx/string_splitter.h" @@ -80,48 +78,60 @@ void init(Environment &env) { } -int read_file(phase1 &p, const std::string &file) { + +int read_file(Environment &e, const std::string &file, const fdmask &fds) { std::error_code ec; const mapped_file mf(file, mapped_file::readonly, ec); if (ec) { fprintf(stderr, "# Error reading %s: %s\n", file.c_str(), ec.message().c_str()); - return 0; + return e.status(-1, false); } - p.process(mf.begin(), mf.end(), false); + + mpw_parser p(e, fds); + e.status(0, false); + + p.parse(mf.begin(), mf.end()); p.finish(); - return 0; + return e.status(); } -int read_fd(phase1 &p, int fd) { + +int read_string(Environment &e, const std::string &s, const fdmask &fds) { + mpw_parser p(e, fds); + e.status(0, false); + p.parse(s); + p.finish(); + return e.status(); +} + + +int read_fd(Environment &e, int fd, const fdmask &fds) { unsigned char buffer[2048]; ssize_t size; + mpw_parser p(e, fds); + e.status(0, false); + for (;;) { size = read(fd, buffer, sizeof(buffer)); if (size < 0) { if (errno == EINTR) continue; - perror("read: "); - return -1; - } - try { - if (size == 0) p.finish(); - else p.process(buffer, buffer + size); - } catch(std::exception &ex) { - fprintf(stderr, "### %s\n", ex.what()); - p.reset(); + perror("read"); + e.status(-1, false); } if (size == 0) break; + p.parse(buffer, buffer + size); } - - return 0; + p.finish(); + return e.status(); } void launch_mpw(const Environment &env, const std::vector &argv, const fdmask &fds); fs::path which(const Environment &env, const std::string &name); -int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector &argv) { +int read_make(Environment &env, const std::vector &argv) { int out[2]; int ok; @@ -153,10 +163,9 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector control_c{0}; @@ -196,7 +205,7 @@ void control_c_handler(int signal, siginfo_t *sinfo, void *context) { } -int interactive(Environment &env, phase1 &p1, phase2& p2) { +int interactive(Environment &env) { std::string history_file = root(); history_file += ".history"; @@ -213,18 +222,18 @@ int interactive(Environment &env, phase1 &p1, phase2& p2) { sigaction(SIGINT, &act, &old_act); + mpw_parser p(env, true); for(;;) { const char *prompt = "# "; - if (p1.continuation() || p2.continuation()) prompt = "> "; + if (p.continuation()) prompt = "> "; char *cp = readline(prompt); if (!cp) { if (control_c) { control_c = 0; fprintf(stdout, "\n"); - p1.abort(); - p2.abort(); + p.abort(); env.status(-9, false); continue; } @@ -246,29 +255,17 @@ int interactive(Environment &env, phase1 &p1, phase2& p2) { s = utf8_to_macroman(s); s.push_back('\n'); - try { - p1.process(s); - } catch(std::exception &ex) { - fprintf(stderr, "### %s\n", ex.what()); - p1.reset(); - } + p.parse(s); } - - try { - p1.finish(); - } catch(std::exception &ex) { - fprintf(stderr, "### %s\n", ex.what()); - p1.reset(); - } + p.finish(); sigaction(SIGINT, &old_act, nullptr); write_history(history_file.c_str()); fprintf(stdout, "\n"); - return 0; } @@ -361,34 +358,8 @@ int make(int argc, char **argv) { } - phase1 p1; - phase2 p2; - - p1 >>= [&p2](std::string &&s) { - if (s.empty()) p2.finish(); - else p2(std::move(s)); - }; - - p2 >>= [&e](command_ptr_vector &&v) { - - for (auto iter = v.begin(); iter != v.end(); ++iter) { - auto &ptr = *iter; - fdmask fds; - try { - ptr->execute(e, fds); - } catch (execution_of_input_terminated &ex) { - control_c = 0; - fprintf(stderr, "### %s\n", ex.what()); - if (e.exit()) - exit(ex.status()); - e.status(ex.status(), false); - } - } - }; - - e.startup(true); - read_file(p1, root() / "Startup"); + read_file(e, root() / "Startup"); e.startup(false); auto path = which(e, "Make"); @@ -399,7 +370,7 @@ int make(int argc, char **argv) { e.set("command", path); args[0] = path; - return read_make(p1, p2, e, args); + return read_make(e, args); } @@ -517,40 +488,14 @@ int main(int argc, char **argv) { - - - phase1 p1; - phase2 p2; - - p1 >>= [&p2](std::string &&s) { - if (s.empty()) p2.finish(); - else p2(std::move(s)); - }; - - p2 >>= [&e](command_ptr_vector &&v) { - - for (auto iter = v.begin(); iter != v.end(); ++iter) { - auto &ptr = *iter; - fdmask fds; - try { - ptr->execute(e, fds); - } catch (execution_of_input_terminated &ex) { - control_c = 0; - if (!(ptr->terminal() && ++iter == v.end())) { - fprintf(stderr, "### %s\n", ex.what()); - } - e.status(ex.status(), false); - return; - } - } - }; - if (!cflag) fprintf(stdout, "MPW Shell " VERSION "\n"); if (!fflag) { fs::path startup = root() / "Startup"; e.startup(true); + mpw_parser p(e); + try { - read_file(p1, startup); + read_file(e, startup); } catch (const std::system_error &ex) { fprintf(stderr, "### %s: %s\n", startup.c_str(), ex.what()); } @@ -558,18 +503,14 @@ int main(int argc, char **argv) { } if (cflag) { - std::string s(cflag); - s.push_back('\n'); - p1.process(s, true); - p2.finish(); + read_string(e, cflag); exit(e.status()); } if (isatty(STDIN_FILENO)) - interactive(e, p1, p2); + interactive(e); else - read_fd(p1, STDIN_FILENO); - p2.finish(); + read_fd(e, STDIN_FILENO); exit(e.status()); } diff --git a/mpw-shell.h b/mpw-shell.h index dee4ec2..c87df4a 100644 --- a/mpw-shell.h +++ b/mpw-shell.h @@ -8,6 +8,7 @@ #include #include "environment.h" +#include "fdset.h" const unsigned char escape = 0xb6; @@ -49,7 +50,7 @@ public: std::vector tokenize(std::string &s, bool eval = false); -std::string expand_vars(const std::string &s, const class Environment &); +std::string expand_vars(const std::string &s, class Environment &, const fdmask &fds = fdmask()); //std::string quote(std::string &&s); std::string quote(const std::string &s); @@ -67,4 +68,9 @@ int32_t evaluate_expression(const std::string &name, std::vector &&tokens +int read_file(Environment &e, const std::string &file, const fdmask &fds = fdmask()); +int read_string(Environment &e, const std::string &s, const fdmask &fds = fdmask()); +int read_fd(Environment &e, int fd, const fdmask &fds = fdmask()); + + #endif diff --git a/mpw_parser.cpp b/mpw_parser.cpp new file mode 100644 index 0000000..a966089 --- /dev/null +++ b/mpw_parser.cpp @@ -0,0 +1,104 @@ + +#include "mpw_parser.h" +#include "phase3_parser.h" + +#include "command.h" +#include "error.h" + +mpw_parser::mpw_parser(Environment &e, fdmask fds, bool interactive) : _env(e), _fds(fds), _interactive(interactive) +{ + + _p3 = phase3::make(); + _p2.set_next([this](int type, std::string &&s){ + _p3->parse(type, std::move(s)); + }); + + _p1.set_next([this](std::string &&s){ + _p2.parse(std::move(s)); + }); +} + + +mpw_parser::~mpw_parser() { + +} + + +bool mpw_parser::continuation() const { + if (_p1.continuation()) return true; + if (_p2.continuation()) return true; + if (_p3->continuation()) return true; + return false; +} + +void mpw_parser::finish() { + _p1.finish(); + _p2.finish(); + _p3->parse(0, ""); + + // and now execute the commands... + execute(); +} + + +void mpw_parser::reset() { + _p1.reset(); + _p2.reset(); + _p3->reset(); + _abort = false; +} + +void mpw_parser::abort() { + _p1.abort(); + _p2.abort(); + //_p3->abort(); + _p3->reset(); + _abort = true; +} + + +void mpw_parser::parse(const void *begin, const void *end) { + if (_abort) return; + _p1.parse((const unsigned char *)begin, (const unsigned char *)end); + + // and execute... + execute(); +} + + +void mpw_parser::execute() { + if (_abort) { + _p3->command_queue.clear(); + return; + } + + auto commands = std::move(_p3->command_queue); + _p3->command_queue.clear(); + + + std::reverse(commands.begin(), commands.end()); + + command_ptr cmd; + + try { + while (!commands.empty()) { + cmd = std::move(commands.back()); + commands.pop_back(); + cmd->execute(_env, _fds); + } + + } catch (execution_of_input_terminated &ex) { + + _env.status(ex.status(), false); + + if (_interactive) { + if (!cmd->terminal() || !commands.empty()) { + fprintf(stderr, "### %s\n", ex.what()); + } + return; + } + _abort = true; + throw; + } + +} \ No newline at end of file diff --git a/mpw_parser.h b/mpw_parser.h new file mode 100644 index 0000000..7a8022d --- /dev/null +++ b/mpw_parser.h @@ -0,0 +1,59 @@ +#ifndef __mpw_parser__ +#define __mpw_parser__ + +#include +#include + +#include "Environment.h" +#include "fdset.h" + +#include "phase1.h" +#include "phase2.h" + +class mpw_parser { + +public: + + mpw_parser(Environment &e, fdmask fds = fdmask(), bool interactive = false); + mpw_parser(Environment &e, bool interactive) : mpw_parser(e, fdmask(), interactive) + {} + + ~mpw_parser(); + + void parse(const void *begin, const void *end); + void parse(const std::string &s) { + parse(s.data(), s.data() + s.size()); + } + void parse(const void *begin, size_t length) { + parse(begin, (const char *)begin + length); + } + + void reset(); + void abort(); + void finish(); + + bool continuation() const; + +private: + + mpw_parser& operator=(const mpw_parser &) = delete; + mpw_parser& operator=(mpw_parser &&) = delete; + + mpw_parser(const mpw_parser &) = delete; + mpw_parser(mpw_parser &&) = delete; + + void execute(); + + Environment &_env; + fdmask _fds; + bool _interactive = false; + bool _abort = false; + + phase1 _p1; + phase2 _p2; + std::unique_ptr _p3; + +}; + + +#endif \ No newline at end of file diff --git a/phase1.cpp b/phase1.cpp index 62e7004..32a50d6 100644 --- a/phase1.cpp +++ b/phase1.cpp @@ -150,19 +150,10 @@ text: } -void phase1::process(const std::string &s, bool final) { - - for (auto c : s) { - cs = process(c, cs); - } - if (final) finish(); -} - -void phase1::process(const unsigned char *begin, const unsigned char *end, bool final) { +void phase1::parse(const unsigned char *begin, const unsigned char *end) { while (begin != end) { cs = process(*begin++, cs); } - if (final) finish(); } void phase1::finish() { @@ -181,7 +172,6 @@ void phase1::reset() { void phase1::flush() { multiline = false; if (scratch.empty()) return; - // strip trailing whitespace? - if (pipe_to) pipe_to(std::move(scratch)); + if (_then) _then(std::move(scratch)); scratch.clear(); } diff --git a/phase1.h b/phase1.h index 0630df8..53c9050 100644 --- a/phase1.h +++ b/phase1.h @@ -8,47 +8,33 @@ class phase1 { public: - typedef std::function pipe_function; + + typedef std::function next_function_type; phase1() = default; - void process(const unsigned char *begin, const unsigned char *end, bool final = false); - - void process(const char *begin, const char *end, bool final = false) { - process((const unsigned char *)begin, (const unsigned char *)end, final); - } - - void process(const std::string &s, bool final = false);// { process(s.data(), s.data() + s.size(), final); } - - void finish();// { const char *tmp = "\n"; process(tmp, tmp+1, true); } + void parse(const unsigned char *begin, const unsigned char *end); + void parse(const std::string &s) { parse((const unsigned char *)s.data(), (const unsigned char *)s.data() + s.size()); } + void finish(); void reset(); void abort() { reset(); } - - phase1 &operator >>= (pipe_function f) { pipe_to = f; return *this; } - - - template - phase1 &operator >>= (F &f) { - using std::placeholders::_1; - pipe_to = std::bind(&F::operator(), &f, _1); - return *this; - } - - bool continuation() const { return multiline; } + void set_next(next_function_type &&fx) { _then = std::move(fx); } + private: int process(unsigned char, int); void flush(); std::string scratch; - pipe_function pipe_to; int line = 1; int cs = 0; bool multiline = false; + + next_function_type _then; }; #endif diff --git a/phase2.h b/phase2.h index af917f7..77b5571 100644 --- a/phase2.h +++ b/phase2.h @@ -3,95 +3,41 @@ #define __phase2_h__ #include -#include #include -#include - -#include "command.h" -#include - -//typedef std::unique_ptr command_ptr; -//typedef std::vector command_ptr_vector; - -class phase2_parser : public lemon_base { - -public: - static std::unique_ptr make(); - - virtual void syntax_error(int yymajor, std::string &yyminor) override final; - virtual void parse_accept() override final; - virtual void parse_failure() override final; - - bool continuation() const; - -private: - friend class phase2; - - phase2_parser(const phase2_parser &) = delete; - phase2_parser(phase2_parser &&) = delete; - - phase2_parser& operator=(const phase2_parser &) = delete; - phase2_parser& operator=(phase2_parser &&) = delete; - -protected: - // these need to be accessible to the lemon-generated parser. - phase2_parser() = default; - - command_ptr_vector command_queue; - bool error = false; -}; - class phase2 { public: - typedef std::function pipe_function; - phase2(); - phase2(const phase2 &) = delete; - phase2(phase2 &&) = default; + typedef std::function next_function_type; - phase2 & operator=(const phase2 &) = delete; - phase2 & operator=(phase2 &&) = default; + phase2() = default; - void operator()(const std::string &line) { process(line); } - void process(const std::string &line); + void parse(std::string &&); void finish(); - phase2 &operator >>=(pipe_function f) { pipe_to = f; return *this; } + void reset(); + void abort() { reset(); } - template - phase2 &operator >>= (F &f) { - using std::placeholders::_1; - pipe_to = std::bind(&F::operator(), &f, _1); - return *this; - } + bool continuation() const { return false; } - bool continuation() const { - return parser ? parser->continuation() : false; - } - - void abort(); + void set_next(next_function_type &&fx) { _then = std::move(fx); } private: - void parse(int, std::string &&); - - - std::unique_ptr parser; + void parse(int type, std::string &&s); std::string scratch; int type = 0; int pcount = 0; - pipe_function pipe_to; void flush(); bool special(); int classify(); void exec(); - + next_function_type _then; }; diff --git a/phase2.rl b/phase2.rl index 82b9f49..aa44202 100644 --- a/phase2.rl +++ b/phase2.rl @@ -4,9 +4,8 @@ * */ -#include "phase2-parser.h" #include "phase2.h" -#include "command.h" +#include "phase3.h" %%{ machine main; @@ -217,9 +216,7 @@ void phase2::flush() { while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back(); - if (!scratch.empty()) { - parse(classify(), std::move(scratch)); - } + if (!scratch.empty()) parse(classify(), std::move(scratch)); type = 0; pcount = 0; @@ -242,14 +239,14 @@ bool phase2::special() { } } -void phase2::process(const std::string &line) { +void phase2::parse(int type, std::string &&s) { + if (_then) _then(type, std::move(s)); +} + +void phase2::parse(std::string &&line) { //fprintf(stderr, "-> %s\n", line.c_str()); - // still needed? - if (line.empty()) { finish(); return; } - - int cs; const unsigned char *p = (const unsigned char *)line.data(); const unsigned char *pe = p + line.size(); @@ -264,81 +261,19 @@ void phase2::process(const std::string &line) { %% write exec; flush(); - // 2 NLs to make the stack reduce. harmless if in a multi-line constuct. - parse(NL, ""); - parse(NL, ""); - - exec(); + if (_then) { + _then(NL, ""); + _then(NL, ""); + } } void phase2::finish() { - parse(0, ""); - exec(); } -void phase2::parse(int token, std::string &&s) { - if (parser) parser->parse(token, std::move(s)); -} - -void phase2::exec() { - - if (pipe_to && parser) { - command_ptr_vector tmp; - for (auto &p : parser->command_queue) { - if (p) tmp.emplace_back(std::move(p)); - } - parser->command_queue.clear(); - - if (!tmp.empty()) pipe_to(std::move(tmp)); - } - -} - -phase2::phase2() { - parser = phase2_parser::make(); - //parser->trace(stdout, " ] "); - -} - -void phase2::abort() { - parser = nullptr; - parser = phase2_parser::make(); -} - -#pragma mark - phase2_parser - -void phase2_parser::parse_accept() { - error = false; -} - -void phase2_parser::parse_failure() { - error = false; -} - -void phase2_parser::syntax_error(int yymajor, std::string &yyminor) { -/* - switch (yymajor) { - case END: - fprintf(stderr, "### MPW Shell - Extra END command.\n"); - break; - - case RPAREN: - fprintf(stderr, "### MPW Shell - Extra ) command.\n"); - break; - - case ELSE: - case ELSE_IF: - fprintf(stderr, "### MPW Shell - ELSE must be within IF ... END.\n"); - break; - - default: - fprintf(stderr, "### Parse error near %s\n", yyminor.c_str()); - break; - } -*/ - - - fprintf(stderr, "### MPW Shell - Parse error near %s\n", yymajor ? yyminor.c_str() : "EOF"); - error = true; +void phase2::reset() { + + type = 0; + pcount = 0; + scratch.clear(); } diff --git a/phase2-parser.lemon b/phase3.lemon similarity index 85% rename from phase2-parser.lemon rename to phase3.lemon index ab436e1..2937cca 100644 --- a/phase2-parser.lemon +++ b/phase3.lemon @@ -6,19 +6,19 @@ %include { -#include "phase2.h" +#include "phase3_parser.h" #include "command.h" -#define LEMON_SUPER phase2_parser -#include "phase2-parser.h" +#define LEMON_SUPER phase3 } + %code { -std::unique_ptr phase2_parser::make() { +std::unique_ptr phase3::make() { return std::make_unique(); } -bool phase2_parser::continuation() const { +bool phase3::continuation() const { yypParser *self = (yypParser *)this; for (const auto &e : *self) { @@ -36,6 +36,43 @@ bool phase2_parser::continuation() const { return false; } + +void phase3::parse_accept() { + error = false; +} + +void phase3::parse_failure() { + error = false; +} + +void phase3::syntax_error(int yymajor, std::string &yyminor) { +/* + switch (yymajor) { + case END: + fprintf(stderr, "### MPW Shell - Extra END command.\n"); + break; + + case RPAREN: + fprintf(stderr, "### MPW Shell - Extra ) command.\n"); + break; + + case ELSE: + case ELSE_IF: + fprintf(stderr, "### MPW Shell - ELSE must be within IF ... END.\n"); + break; + + default: + fprintf(stderr, "### Parse error near %s\n", yyminor.c_str()); + break; + } +*/ + + + fprintf(stderr, "### MPW Shell - Parse error near %s\n", yymajor ? yyminor.c_str() : "EOF"); + error = true; +} + + } %left PIPE_PIPE AMP_AMP. diff --git a/phase3_parser.h b/phase3_parser.h new file mode 100644 index 0000000..9a11460 --- /dev/null +++ b/phase3_parser.h @@ -0,0 +1,43 @@ + +#ifndef __phase3_h__ +#define __phase3_h__ + +#include +#include +#include + +#include "command.h" +#include + +class phase3 : public lemon_base { + +public: + static std::unique_ptr make(); + + virtual void syntax_error(int yymajor, std::string &yyminor) override final; + virtual void parse_accept() override final; + virtual void parse_failure() override final; + + bool continuation() const; + + //void parse(int type, std::string &&s) override final; + +private: + + phase3(const phase3 &) = delete; + phase3(phase3 &&) = delete; + + phase3& operator=(const phase3 &) = delete; + phase3& operator=(phase3 &&) = delete; + +protected: + // these need to be accessible to the lemon-generated parser. + phase3() = default; + + command_ptr_vector command_queue; + bool error = false; + + friend class mpw_parser; +}; + +#endif \ No newline at end of file