Squashed commit of the following:

commit f0944a89f27e44b1764988806e655f09764e80df
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Tue Aug 30 12:24:08 2016 -0400

    exit throws execution of input error w/ possible 0 value.  catch it.

commit 9e7f9c1ae049aa26513413f4767268b47ee22e98
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Tue Aug 30 12:23:21 2016 -0400

    builtins - more consistent argument handling.

commit be4c1c902f5a3a3f01e92ae52c7d6cc5d8731b65
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Tue Aug 30 12:23:01 2016 -0400

    .

commit 68d0c29fec112c6e7bc3a672b41eb7eb758a8941
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Tue Aug 30 12:22:51 2016 -0400

    exit command.

commit 25b0a7f7da9220b03026123bb5072c2da1d73fde
Author: Kelvin Sherlock <ksherlock@gmail.com>
Date:   Tue Aug 30 12:21:16 2016 -0400

    builtin quit command.
This commit is contained in:
Kelvin Sherlock 2016-08-30 12:25:43 -04:00
parent f6c5478063
commit f125b533f7
9 changed files with 190 additions and 79 deletions

View File

@ -4,6 +4,7 @@
#include "fdset.h"
#include "value.h"
#include "environment.h"
#include "error.h"
#include <string>
#include <vector>
@ -279,22 +280,14 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
const char *name = export_or_unexport ? "Export" : "Unexport";
struct {
int _r = 0;
int _s = 0;
} flags;
bool _r = false;
bool _s = false;
bool error = false;
std::vector<std::string> argv = getopt(tokens, [&](char c){
switch(c) {
case 'r':
case 'R':
flags._r = true;
break;
case 's':
case 'S':
flags._s = true;
break;
auto argv = getopt(tokens, [&](char c){
switch(tolower(c)) {
case 'r': _r = true; break;
case 's': _s = true; break;
default:
fdprintf(stderr, "### %s - \"-%c\" is not an option.\n", name, c);
error = true;
@ -308,7 +301,7 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
}
if (argv.empty()) {
if (flags._r && flags._s) goto conflict;
if (_r && _s) goto conflict;
// list of exported vars.
// -r will generate unexport commands for exported variables.
@ -320,14 +313,14 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
for (const auto &kv : env) {
const std::string& vname = kv.first;
if (kv.second == export_or_unexport)
fdprintf(stdout, "%s%s\n", flags._s ? "" : name, quote(vname).c_str());
fdprintf(stdout, "%s%s\n", _s ? "" : name, quote(vname).c_str());
}
return 0;
}
else {
// mark as exported.
if (flags._r || flags._s) goto conflict;
if (_r || _s) goto conflict;
for (std::string s : argv) {
auto iter = env.find(s);
@ -509,12 +502,9 @@ int builtin_directory(Environment &env, const std::vector<std::string> &tokens,
bool error = false;
std::vector<std::string> argv = getopt(tokens, [&](char c){
switch(c)
switch(tolower(c))
{
case 'q':
case 'Q':
q = true;
break;
case 'q': q = true; break;
default:
fdprintf(stderr, "### Directory - \"-%c\" is not an option.\n", c);
error = true;
@ -585,24 +575,12 @@ int builtin_exists(Environment &env, const std::vector<std::string> &tokens, con
std::vector<std::string> argv = getopt(tokens, [&](char c){
switch(tolower(c))
{
case 'a':
_a = true;
break;
case 'd':
_d = true;
break;
case 'f':
_f = true;
break;
case 'n':
_n = true;
break;
case 'q':
_q = true;
break;
case 'w':
_w = true;
break;
case 'a': _a = true; break;
case 'd': _d = true; break;
case 'f': _f = true; break;
case 'n': _n = true; break;
case 'q': _q = true; break;
case 'w': _w = true; break;
default:
fdprintf(stderr, "### Exists - \"-%c\" is not an option.\n", c);
error = true;
@ -773,15 +751,15 @@ int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask
int builtin_which(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
// which [-a] [-p] [command]
bool a = false;
bool p = false;
bool _a = false;
bool _p = false;
bool error = false;
std::vector<std::string> argv = getopt(tokens, [&](char c){
switch(c)
switch(tolower(c))
{
case 'a': case 'A': a = true; break;
case 'p': case 'P': p = true; break;
case 'a': _a = true; break;
case 'p': _p = true; break;
default:
fdprintf(stderr, "### Which - \"-%c\" is not an option.\n", c);
@ -832,7 +810,7 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
}
for(; ss; ++ss) {
if (p) fdprintf(stderr, "checking %s\n", ss->c_str());
if (_p) fdprintf(stderr, "checking %s\n", ss->c_str());
std::error_code ec;
fs::path p(ToolBox::MacToUnix(ss->c_str()));
@ -841,13 +819,13 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
if (fs::exists(p, ec)) {
found = true;
fdprintf(stdout, "%s\n", quote(p).c_str());
if (!a) break;
if (!_a) break;
}
}
// check builtins...
if (!found || a) {
if (!found || _a) {
static const char *builtins[] = {
"aboutbox",
@ -951,9 +929,7 @@ int builtin_version(Environment &env, const std::vector<std::string> &tokens, co
auto argv = getopt(tokens, [&](char c){
switch(tolower(c))
{
case 'v':
_v = true;
break;
case 'v': _v = true; break;
default:
fdprintf(stderr, "### Version - \"-%c\" is not an option.\n", c);
error = true;
@ -1039,6 +1015,46 @@ int builtin_false(Environment &, const std::vector<std::string> &, const fdmask
}
int builtin_quit(Environment &, const std::vector<std::string> &tokens, const fdmask &fds) {
bool error = false;
bool _y = false;
bool _n = false;
bool _c = false;
auto argv = getopt(tokens, [&](char c){
switch(tolower(c))
{
case 'c': _c = true; break;
case 'n': _n = true; break;
case 'y': _y = true; break;
default:
fdprintf(stderr, "### Quit - \"-%c\" is not an option.\n", c);
error = true;
break;
}
});
if (_y + _n + _c > 1) {
fdprintf(stderr, "### Quit - Conflicting options were specified.\n");
error = true;
}
if (argv.size() > 0) {
fdprintf(stderr, "### Quit - Too many parameters were specified.\n");
error = true;
}
if (error) {
fdprintf(stderr, "# Usage - Quit [-y | -n | -c]\n");
return 1;
}
throw quit_command_t{};
return 0;
}
int builtin_execute(Environment &e, const std::vector<std::string> &tokens, const fdmask &fds) {
// runs argv[1] in the current environment. unlike MPW, argv[1] must be a script.

View File

@ -28,7 +28,7 @@ int builtin_unalias(Environment &e, const std::vector<std::string> &, const fdma
int builtin_execute(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_true(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_false(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_execute(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_quit(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_evaluate(Environment &e, std::vector<token> &&, const fdmask &);

View File

@ -5,6 +5,7 @@
#include "builtins.h"
#include "mpw-shell.h"
#include "error.h"
#include "value.h"
#include <stdexcept>
#include <unordered_map>
@ -17,6 +18,7 @@
#include "cxx/filesystem.h"
#include "cxx/string_splitter.h"
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>
@ -40,8 +42,7 @@ typedef std::vector<token> token_vector;
namespace {
struct break_command_t {};
struct continue_command_t {};
/*
* returns:
@ -56,6 +57,12 @@ namespace {
return -3;
}
int bad_exit() {
fprintf(stderr, "### Exit - Missing if keyword.\n");
fprintf(stderr, "# Usage - Exit [Number] [if expression...]\n");
return -3;
}
int evaluate(int type, token_vector &&tokens, Environment &env) {
std::reverse(tokens.begin(), tokens.end());
@ -64,6 +71,8 @@ namespace {
switch(type) {
default: return 0;
// exit [number] [if expr] ([number has been removed])
case EXIT:
case BREAK:
case CONTINUE:
case ELSE:
@ -77,10 +86,12 @@ namespace {
case BREAK: name = "Break"; break;
case CONTINUE: name = "Continue"; break;
case ELSE: name = "Else"; break;
case EXIT: name = "Exit"; return bad_exit(); break;
}
return bad_if(name);
}
// fall through.
case IF:
tokens.pop_back();
try {
@ -202,6 +213,7 @@ namespace {
{"exists", builtin_exists},
{"export", builtin_export},
{"parameters", builtin_parameters},
{"quit", builtin_quit},
{"quote", builtin_quote},
{"set", builtin_set},
{"shift", builtin_shift},
@ -388,6 +400,10 @@ int eval_exec(std::string command, Environment &env, const fdmask &fds, bool thr
rv = fx(tokens);
}
catch (const exit_command_t &ex) {
// convert to execution of input terminated.
throw execution_of_input_terminated(ex.value);
}
catch (mpw_error &e) {
if (echo) env.echo("%s", command.c_str());
@ -442,6 +458,39 @@ int continue_command::execute(Environment &env, const fdmask &fds, bool throwup)
}
int exit_command::execute(Environment &env, const fdmask &fds, bool throwup) {
// exit
// exit [number] [if expr ]]
// todo --
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
env.set("command", "exit");
env.status(0);
if (tokens.size() <= 1) {
throw exit_command_t{};
}
int status = 0;
value v(tokens[1]);
// remove the return value to make processing easier.
if (v.is_number()) {
tokens.erase(tokens.begin() + 1);
}
status = evaluate(EXIT, std::move(tokens), env);
if (status) {
int ok = v.to_number(0);
throw exit_command_t{ok};
}
return 0;
});
}
int or_command::execute(Environment &e, const fdmask &fds, bool throwup) {
int rv = 0;

View File

@ -22,7 +22,7 @@ struct command {
{}
virtual bool terminal() const noexcept {
return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE || type == ERROR;
return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE || type == ERROR || type == EXIT;
}
int type = 0;
@ -81,6 +81,15 @@ struct continue_command : public command {
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
};
struct exit_command : public command {
template<class S>
exit_command(S &&s) : command(EXIT), text(std::forward<S>(s))
{}
std::string text;
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
};
struct binary_command : public command {

11
error.h
View File

@ -67,4 +67,15 @@ public:
{}
};
/*
these are used for flow-control.
they do not inherit from std::exception to prevent being caught
by normal handlers.
*/
struct break_command_t {};
struct continue_command_t {};
struct exit_command_t { int value = 0; };
struct quit_command_t {};
#endif

View File

@ -91,8 +91,12 @@ int read_file(Environment &e, const std::string &file, const fdmask &fds) {
mpw_parser p(e, fds);
e.status(0, false);
p.parse(mf.begin(), mf.end());
p.finish();
try {
p.parse(mf.begin(), mf.end());
p.finish();
} catch(const execution_of_input_terminated &ex) {
return ex.status();
}
return e.status();
}
@ -100,9 +104,14 @@ int read_file(Environment &e, const std::string &file, const fdmask &fds) {
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();
try {
p.parse(s);
p.finish();
} catch(const execution_of_input_terminated &ex) {
return ex.status();
}
return e.status();
}
@ -114,17 +123,21 @@ int read_fd(Environment &e, int fd, const fdmask &fds) {
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");
e.status(-1, false);
try {
for (;;) {
size = read(fd, buffer, sizeof(buffer));
if (size < 0) {
if (errno == EINTR) continue;
perror("read");
e.status(-1, false);
}
if (size == 0) break;
p.parse(buffer, buffer + size);
}
if (size == 0) break;
p.parse(buffer, buffer + size);
p.finish();
} catch(const execution_of_input_terminated &ex) {
return ex.status();
}
p.finish();
return e.status();
}
@ -498,19 +511,28 @@ int main(int argc, char **argv) {
read_file(e, startup);
} catch (const std::system_error &ex) {
fprintf(stderr, "### %s: %s\n", startup.c_str(), ex.what());
} catch (const quit_command_t &) {
}
e.startup(false);
}
if (cflag) {
read_string(e, cflag);
exit(e.status());
try {
int rv = 0;
if (cflag) {
rv = read_string(e, cflag);
exit(rv);
}
if (isatty(STDIN_FILENO))
rv = interactive(e);
else
rv = read_fd(e, STDIN_FILENO);
exit(rv);
}
catch (const quit_command_t &) {
exit(0);
}
if (isatty(STDIN_FILENO))
interactive(e);
else
read_fd(e, STDIN_FILENO);
exit(e.status());
}

View File

@ -81,6 +81,7 @@ void mpw_parser::execute() {
command_ptr cmd;
try {
while (!commands.empty()) {
cmd = std::move(commands.back());
@ -94,7 +95,7 @@ void mpw_parser::execute() {
if (_interactive) {
if (!cmd->terminal() || !commands.empty()) {
fprintf(stderr, "### %s\n", ex.what());
if (ex.status()) fprintf(stderr, "### %s\n", ex.what());
}
return;
}

View File

@ -193,6 +193,7 @@ int phase2::classify() {
_("else", ELSE)
_("end", END)
_("evaluate", EVALUATE)
_("exit", EXIT)
_("for", FOR)
_("if", IF)
_("loop", LOOP)
@ -233,6 +234,7 @@ bool phase2::special() {
case EVALUATE:
case BREAK:
case CONTINUE:
case EXIT:
return true;
default:
return false;

View File

@ -139,6 +139,7 @@ term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
term(RV) ::= BREAK(C). { RV = std::make_unique<break_command>(std::move(C)); }
term(RV) ::= CONTINUE(C). { RV = std::make_unique<continue_command>(std::move(C)); }
term(RV) ::= EXIT(C). { RV = std::make_unique<exit_command>(std::move(C)); }
term(C) ::= if_command(C).
term(C) ::= begin_command(C).
term(C) ::= paren_command(C).