From c2c41f3a52165cf862354e46c7de485155750f22 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Thu, 28 Jul 2016 13:44:00 -0400 Subject: [PATCH] rewrite command execution to be more consistent with real mpw. --- command.cpp | 434 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 376 insertions(+), 58 deletions(-) diff --git a/command.cpp b/command.cpp index 608b4d7..77a9c62 100644 --- a/command.cpp +++ b/command.cpp @@ -53,8 +53,8 @@ namespace { return -3; } int evaluate(int type, const std::string &s, Environment &env) { - - auto tokens = tokenize(s, true); + std::string tmp(s); + auto tokens = tokenize(tmp, true); std::reverse(tokens.begin(), tokens.end()); int32_t e; @@ -92,6 +92,47 @@ namespace { } return !!e; } + + int evaluate(int type, std::vector &&tokens, Environment &env) { + std::reverse(tokens.begin(), tokens.end()); + + int32_t e; + + switch(type) { + default: return 0; + + case BREAK: + case CONTINUE: + case ELSE: + tokens.pop_back(); + + if (tokens.empty()) return 1; + + if (strcasecmp(tokens.back().string.c_str(), "if") != 0) { + const char *name = ""; + switch(type) { + case BREAK: name = "Break"; break; + case CONTINUE: name = "Continue"; break; + case ELSE: name = "Else"; break; + } + return bad_if(name); + } + // fall through. + case IF: + tokens.pop_back(); + try { + e = evaluate_expression("If", std::move(tokens)); + } + catch (std::exception &ex) { + fprintf(stderr, "%s\n", ex.what()); + return -5; + } + break; + } + return !!e; + } + + } @@ -285,61 +326,115 @@ command::~command() * echo and error should respect the active fdmask. */ -int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) { + + + +template +int exec(std::string command, Environment &env, bool throwup, F &&fx) { if (control_c) throw execution_of_input_terminated(); - - std::string s = expand_vars(text, env); - - - process p; + bool echo = true; try { - auto tokens = tokenize(s, false); + process p; + command = expand_vars(command, env); + auto tokens = tokenize(command, false); if (tokens.empty()) return 0; parse_tokens(std::move(tokens), p); - } catch(std::exception &e) { + env.echo("%s", command.c_str()); + echo = false; + + if (p.arguments.empty()) return env.status(0); + + return env.status(fx(p), throwup); + } + catch (mpw_error &e) { + if (echo) env.echo("%s", command.c_str()); fprintf(stderr, "%s\n", e.what()); + return env.status(e.status(), throwup); + } + catch (std::exception &e) { + if (echo) env.echo("%s", command.c_str()); + fprintf(stderr, "### %s\n", e.what()); return env.status(-4, throwup); } - if (p.arguments.empty()) return 0; - - env.echo("%s", s.c_str()); - fdmask newfds = p.fds | fds; - - std::string name = p.arguments.front(); - lowercase(name); - - auto iter = builtins.find(name); - if (iter != builtins.end()) { - env.set("command", name); - int status = iter->second(env, p.arguments, newfds); - return env.status(status, throwup); - } - - if (env.test()) return env.status(0); - if (env.startup()) { - fprintf(stderr, "### MPW Shell - startup file may not contain external commands.\n"); - return env.status(0); - } - - name = p.arguments.front(); - fs::path path = which(env, name); - if (path.empty()) { - fprintf(stderr, "### MPW Shell - Command \"%s\" was not found.\n", name.c_str()); - return env.status(-1, throwup); - } - env.set("command", path); - p.arguments[0] = path; - - int status = execute_external(env, p.arguments, newfds); - - return env.status(status, throwup); } +int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) { + + + return exec(text, env, throwup, [&](process &p){ + + fdmask newfds = p.fds | fds; + + std::string name = p.arguments.front(); + lowercase(name); + + auto iter = builtins.find(name); + if (iter != builtins.end()) { + env.set("command", name); + int status = iter->second(env, p.arguments, newfds); + return status; + } + + if (env.test()) return 0; + + if (env.startup()) { + fprintf(stderr, "### MPW Shell - startup file may not contain external commands.\n"); + return 0; + } + + name = p.arguments.front(); + fs::path path = which(env, name); + if (path.empty()) { + fprintf(stderr, "### MPW Shell - Command \"%s\" was not found.\n", name.c_str()); + return -1; + } + env.set("command", path); + p.arguments[0] = path; + + return execute_external(env, p.arguments, newfds); + }); +} + +template +int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){ + + bool echo = true; + if (control_c) throw execution_of_input_terminated(); + std::string name; + try { + command = expand_vars(command, env); + auto tokens = tokenize(command, true); + + if (tokens.empty()) return 0; + env.echo("%s", command.c_str()); + echo = false; + name = tokens[0].string; + + int ok = fx(tokens); + return env.status(ok, throwup); + } + + catch (mpw_error &e) { + if (echo) env.echo("%s", command.c_str()); + fprintf(stderr, "### %s\n", e.what()); + return env.status(e.status(), throwup); + } + + catch(std::exception &e) { + // these should include the argv0 name. + if (echo) env.echo("%s", command.c_str()); + fprintf(stderr, "### %s - %s\n", name.c_str(), e.what()); + return env.status(-4, throwup); + } +} +typedef std::vector token_vector; + 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); @@ -359,11 +454,28 @@ int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) fprintf(stderr, "%s\n", e.what()); return env.status(1, throwup); } +#endif + + return eval_exec(text, env, throwup, [&](token_vector &tokens){ + env.set("command", "evaluate"); + return builtin_evaluate(env, std::move(tokens), fds); + }); + } + int break_command::execute(Environment &env, const fdmask &fds, bool throwup) { + return eval_exec(text, env, throwup, [&](token_vector &tokens){ + env.set("command", "break"); + if (!env.loop()) throw break_error(); + int status = evaluate(BREAK, std::move(tokens), env); + if (status > 0) throw break_command_t(); + return status; + }); + +#if 0 if (control_c) throw execution_of_input_terminated(); std::string s = expand_vars(text, env); @@ -381,11 +493,20 @@ int break_command::execute(Environment &env, const fdmask &fds, bool throwup) { 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){ + env.set("command", "continue"); + if (!env.loop()) throw continue_error(); + int status = evaluate(CONTINUE, std::move(tokens), env); + if (status > 0) throw continue_command_t(); + return status; + }); + +#if 0 if (control_c) throw execution_of_input_terminated(); std::string s = expand_vars(text, env); @@ -404,7 +525,7 @@ int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) if (status > 0) throw continue_command_t(); return env.status(status, throwup); - +#endif } int or_command::execute(Environment &e, const fdmask &fds, bool throwup) { @@ -447,22 +568,17 @@ int pipe_command::execute(Environment &e, const fdmask &fds, bool throwup) { fdset pipe_fd; fd = mkstemp(temp); + unlink(temp); pipe_fd.set(1, fd); - try { + rv = children[0]->execute(e, pipe_fd | fds, throwup); - rv = children[0]->execute(e, pipe_fd | fds, throwup); + // fdset will close the fd ... + pipe_fd.swap_in_out(); + lseek(fd, 0, SEEK_SET); - // fdset will close the fd ... - pipe_fd.swap_in_out(); - lseek(fd, 0, SEEK_SET); + rv = children[1]->execute(e, pipe_fd | fds, throwup); - rv = children[1]->execute(e, pipe_fd | fds, throwup); - - } catch(std::exception &ex) { - unlink(temp); - throw; - } } if (children[0]) return children[0]->execute(e, fds, throwup); if (children[1]) return children[1]->execute(e, fds, throwup); @@ -518,7 +634,8 @@ namespace { // MPW ignores any END tokens other than redirection. process p; try { - auto tokens = tokenize(s, false); + std::string tmp(s); + auto tokens = tokenize(tmp, false); parse_tokens(std::move(tokens), p); } catch (std::exception &e) { @@ -530,8 +647,68 @@ namespace { } } +template +int begin_end_exec(std::string begin, std::string end, Environment &env, bool throwup, F &&fx) { + + if (control_c) throw execution_of_input_terminated(); + + bool echo = true; + try { + process p; + begin = expand_vars(begin, env); + end = expand_vars(end, env); + + auto b = tokenize(begin, true); + auto e = tokenize(end, false); + + parse_tokens(std::move(e), p); + + if (echo) env.echo("%s ... %s", begin.c_str(), end.c_str() ); + + int ok = fx(b, p); + return env.status(ok, throwup); + } + catch (execution_of_input_terminated &e) { + throw; + } + catch (mpw_error &e) { + if (echo) env.echo("%s ... %s", begin.c_str(), end.c_str() ); + fprintf(stderr, "%s\n", e.what()); + return env.status(e.status(), throwup); + } + catch (std::exception &e) { + if (echo) env.echo("%s ... %s", begin.c_str(), end.c_str() ); + fprintf(stderr, "### %s\n", e.what()); + return env.status(-4, throwup); + } + + +} + int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) { - // todo -- parse end for redirection. + + return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){ + + env.set("command", type == BEGIN ? "end" : ")"); + if (b.size() != 1) { + fprintf(stderr, "### Begin - Too many parameters were specified.\n"); + fprintf(stderr, "Usage - Begin\n"); + return -3; + } + + fdmask newfds = p.fds | fds; + + int rv; + env.indent_and([&]{ + rv = vector_command::execute(env, newfds); + }); + + env.echo("%s", type == BEGIN ? "end" : ")"); + + return rv; + }); + +#if 0 std::string b = expand_vars(begin, env); std::string e = expand_vars(end, env); @@ -570,11 +747,47 @@ int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) { 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){ + + env.set("command", "end"); + if (b.size() != 1) { + fprintf(stderr, "### Loop - Too many parameters were specified.\n"); + fprintf(stderr, "Usage - Loop\n"); + return -3; + } + + fdmask newfds = p.fds | fds; + + int rv = 0; + + for(;;) { + + if (control_c) throw execution_of_input_terminated(); + + try { + env.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 rv; + }); + + +#if 0 std::string b = expand_vars(begin, env); std::string e = expand_vars(end, env); @@ -618,10 +831,47 @@ int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) { 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){ + + env.set("command", "end"); + + if (b.size() < 3 || strcasecmp(b[2].string.c_str(), "in")) { + fprintf(stderr, "### For - Missing in keyword.\n"); + fprintf(stderr, "Usage - For name in [word...]\n"); + return -3; + } + + fdmask newfds = p.fds | fds; + + int rv = 0; + for (int i = 3; i < b.size(); ++i ) { + + if (control_c) throw execution_of_input_terminated(); + + env.set(b[1].string, b[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 rv; + }); + +#if 0 std::string b = expand_vars(begin, env); std::string e = expand_vars(end, env); @@ -668,16 +918,83 @@ int for_command::execute(Environment &env, const fdmask &fds, bool throwup) { } return env.status(rv); + +#endif } + + /* * the entire command prints even if there is an error with the expression. * */ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) { + + int rv = 0; + int error = 0; + bool skip = false; + bool first = true; + + fdmask newfds; + for (auto &c : clauses) { + + int tmp; + if (first) { + first = false; + tmp = begin_end_exec(c->clause, end, env, false, [&](token_vector &b, process &p){ + + newfds = p.fds | fds; + + int status = evaluate(c->type, std::move(b), env); + if (status < 0) { + error = status; + } + if (status > 0) { + skip = true; + + env.indent_and([&](){ + rv = c->execute(env, newfds); + }); + } + return 0; + }); + if (tmp != 0) error = tmp; + continue; + } + else { + // second... + tmp = eval_exec(c->clause, env, false, [&](token_vector &b){ + if (skip || error) return 0; + + int status = evaluate(c->type, std::move(b), env); + if (status < 0) { + error = status; + } + if (status > 0) { + skip = true; + + env.indent_and([&](){ + rv = c->execute(env, newfds); + }); + } + return 0; + + }); + if (tmp != 0 && !skip) error = tmp; + } + } + env.echo("end"); + if (error) return env.status(error, throwup); + return env.status(rv, throwup); + +#if 0 + + + + int rv = 0; bool ok = false; @@ -715,5 +1032,6 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) { env.echo("end"); return env.status(rv); +#endif }