mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2024-12-26 11:30:27 +00:00
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:
parent
f6c5478063
commit
f125b533f7
118
builtins.cpp
118
builtins.cpp
@ -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.
|
||||
|
@ -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 &);
|
||||
|
53
command.cpp
53
command.cpp
@ -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;
|
||||
|
11
command.h
11
command.h
@ -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
11
error.h
@ -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
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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).
|
||||
|
Loading…
Reference in New Issue
Block a user