lots of updates!

This commit is contained in:
Kelvin Sherlock 2016-02-01 20:38:29 -05:00
parent c8d12c4b3e
commit 5723656988
14 changed files with 752 additions and 319 deletions

View File

@ -108,10 +108,10 @@ add_custom_command(
)
# mpw-shell-execute.cpp mpw-shell-builtins.cpp
add_executable(mpw-shell mpw-shell.cpp mpw-shell-read.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
mpw-shell-execute.cpp mpw-shell-builtins.cpp mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp)
mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp)
# all this for -std=c++14
set_property (TARGET mpw-shell PROPERTY CXX_STANDARD 14)

View File

@ -3,6 +3,7 @@
#include "fdset.h"
#include "value.h"
#include "environment.h"
#include <string>
#include <vector>
@ -11,14 +12,17 @@
#include <cstdio>
#include <cctype>
//MAXPATHLEN
#include <sys/param.h>
namespace {
/*
std::string &lowercase(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), [](char c){ return std::tolower(c); });
return s;
}
*/
// doesn't handle flag arguments but builtins don't have arguments.
template<class FX>
@ -40,83 +44,6 @@ namespace {
return out;
}
#if 0
/*
* the fdopen() will assume ownership of the fd and close it.
* this is not desirable.
*/
// linux uses size_t. *BSD uses int.
int readfn(void *cookie, char *buffer, int size) {
return ::read((int)(std::ptrdiff_t)cookie, buffer, size);
}
int readfn(void *cookie, char *buffer, size_t size) {
return ::read((int)(std::ptrdiff_t)cookie, buffer, size);
}
int writefn(void *cookie, const char *buffer, int size) {
return ::write((int)(std::ptrdiff_t)cookie, buffer, size);
}
int writefn(void *cookie, const char *buffer, size_t size) {
return ::write((int)(std::ptrdiff_t)cookie, buffer, size);
}
FILE *file_stream(int index, int fd) {
if (fd < 0) {
switch (index) {
case 0: return stdin;
case 1: return stdout;
case 2: return stderr;
default:
return stderr;
}
}
// will not close.
#ifdef __linux__
/* Linux */
cookie_io_functions_t io = { readfn, writefn, nullptr, nullptr };
return fopencookie((void *)(std::ptrdiff_t)fd, "w+", io);
#else
/* *BSD */
return funopen((const void *)(std::ptrdiff_t)fd, readfn, writefn, nullptr, nullptr);
#endif
}
class io_helper {
public:
FILE *in;
FILE *out;
FILE *err;
io_helper(const fdmask &fds) {
in = file_stream(0, fds[0]);
out = file_stream(1, fds[1]);
err = file_stream(2, fds[2]);
}
~io_helper() {
#define __(x, target) if (x != target) fclose(x)
__(in, stdin);
__(out, stdout);
__(err, stderr);
#undef __
}
io_helper() = delete;
io_helper(const io_helper &) = delete;
io_helper &operator=(const io_helper &) = delete;
};
#endif
}
#undef stdin
@ -142,31 +69,21 @@ inline int fputc(int c, int fd) {
}
int builtin_unset(const std::vector<std::string> &tokens, const fdmask &) {
int builtin_unset(Environment &env, const std::vector<std::string> &tokens, const fdmask &) {
for (auto iter = tokens.begin() + 1; iter != tokens.end(); ++iter) {
std::string name = *iter;
lowercase(name);
const std::string &name = *iter;
Environment.erase(name);
env.unset(name);
}
// unset [no arg] removes ALL variables
if (tokens.size() == 1) {
Environment.clear();
env.unset();
}
return 0;
}
template <class UO, class K, class M>
void insert_or_assign(UO &map, K&& k, M&& obj) {
auto iter = map.find(k);
if (iter != map.end()) iter->second = std::forward<M>(obj);
else map.emplace(std::make_pair(std::forward<K>(k), std::forward<M>(obj)));
}
int builtin_set(const std::vector<std::string> &tokens, const fdmask &fds) {
int builtin_set(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
// set var name -- set
// set var -- just print the value
@ -177,7 +94,7 @@ int builtin_set(const std::vector<std::string> &tokens, const fdmask &fds) {
if (tokens.size() == 1) {
for (const auto &kv : Environment) {
for (const auto &kv : env) {
std::string name = quote(kv.first);
std::string value = quote(kv.second);
@ -190,9 +107,8 @@ int builtin_set(const std::vector<std::string> &tokens, const fdmask &fds) {
if (tokens.size() == 2) {
std::string name = tokens[1];
lowercase(name);
auto iter = Environment.find(name);
if (iter == Environment.end()) {
auto iter = env.find(name);
if (iter == env.end()) {
fprintf(stderr, "### Set - No variable definition exists for %s.\n", name.c_str());
return 2;
}
@ -220,15 +136,14 @@ int builtin_set(const std::vector<std::string> &tokens, const fdmask &fds) {
std::string name = tokens[1+exported];
std::string value = tokens[2+exported];
lowercase(name);
Environment[name] = EnvironmentEntry(std::move(value), exported);
env.set(name, value, exported);
return 0;
}
static int export_common(bool export_or_unexport, const std::vector<std::string> &tokens, const fdmask &fds) {
static int export_common(Environment &env, bool export_or_unexport, const std::vector<std::string> &tokens, const fdmask &fds) {
const char *name = export_or_unexport ? "Export" : "Unexport";
@ -270,7 +185,7 @@ static int export_common(bool export_or_unexport, const std::vector<std::string>
name = export_or_unexport ? "Export " : "Unexport ";
for (const auto &kv : Environment) {
for (const auto &kv : env) {
const std::string& vname = kv.first;
if (kv.second == export_or_unexport)
fprintf(stdout, "%s%s\n", flags._s ? "" : name, quote(vname).c_str());
@ -283,9 +198,8 @@ static int export_common(bool export_or_unexport, const std::vector<std::string>
if (flags._r || flags._s) goto conflict;
for (std::string s : argv) {
lowercase(s);
auto iter = Environment.find(s);
if (iter != Environment.end()) iter->second = export_or_unexport;
auto iter = env.find(s);
if (iter != env.end()) iter->second = export_or_unexport;
}
return 0;
}
@ -295,21 +209,20 @@ conflict:
fprintf(stderr, "# Usage - %s [-r | -s | name...]\n", name);
return 1;
}
int builtin_export(const std::vector<std::string> &tokens, const fdmask &fds) {
//io_helper io(fds);
return export_common(true, tokens, fds);
int builtin_export(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
return export_common(env, true, tokens, fds);
}
int builtin_unexport(const std::vector<std::string> &tokens, const fdmask &fds) {
int builtin_unexport(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
//io_helper io(fds);
return export_common(false, tokens, fds);
return export_common(env, false, tokens, fds);
}
int builtin_echo(const std::vector<std::string> &tokens, const fdmask &fds) {
int builtin_echo(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
//io_helper io(fds);
@ -333,7 +246,7 @@ int builtin_echo(const std::vector<std::string> &tokens, const fdmask &fds) {
return 0;
}
int builtin_quote(const std::vector<std::string> &tokens, const fdmask &fds) {
int builtin_quote(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
// todo...
//io_helper io(fds);
@ -359,7 +272,7 @@ int builtin_quote(const std::vector<std::string> &tokens, const fdmask &fds) {
return 0;
}
int builtin_parameters(const std::vector<std::string> &argv, const fdmask &fds) {
int builtin_parameters(Environment &env, const std::vector<std::string> &argv, const fdmask &fds) {
//io_helper io(fds);
@ -371,7 +284,7 @@ int builtin_parameters(const std::vector<std::string> &argv, const fdmask &fds)
}
int builtin_directory(const std::vector<std::string> &tokens, const fdmask &fds) {
int builtin_directory(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
// directory [-q]
// directory path
@ -416,12 +329,29 @@ int builtin_directory(const std::vector<std::string> &tokens, const fdmask &fds)
return 1;
}
return 0;
// todo -- pathname translation.
int ok = chdir(argv.front().c_str());
if (ok < 0) {
fputs("### Directory - Unable to set current directory.\n", stderr);
perror("chdir: ");
return 1;
}
}
else {
// pwd
return 0;
char buffer[MAXPATHLEN];
char *cp = getcwd(buffer, sizeof(buffer));
if (!cp) {
perror("getcwd: ");
return -1;
}
// todo -- pathname translation?
std::string s = buffer;
if (!q) s = quote(std::move(s));
fprintf(stdout, "%s\n", s.c_str());
}
return 0;
}
static bool is_assignment(int type) {
@ -436,7 +366,7 @@ static bool is_assignment(int type) {
}
}
int builtin_evaluate(std::vector<token> &&tokens, const fdmask &fds) {
int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask &fds) {
// evaluate expression
// evaluate variable = expression
// evaluate variable += expression
@ -481,7 +411,6 @@ int builtin_evaluate(std::vector<token> &&tokens, const fdmask &fds) {
if (is_assignment(type)) {
std::string name = tokens.back().string;
lowercase(name);
tokens.pop_back();
tokens.pop_back();
@ -490,14 +419,14 @@ int builtin_evaluate(std::vector<token> &&tokens, const fdmask &fds) {
switch(type) {
case '=':
Environment[name] = std::to_string(i);
env.set(name, std::to_string(i));
break;
case '+=':
case '-=':
{
value old;
auto iter = Environment.find(name);
if (iter != Environment.end()) old = (const std::string &)iter->second;
auto iter = env.find(name);
if (iter != env.end()) old = (const std::string &)iter->second;
switch(type) {
case '+=':
@ -509,10 +438,7 @@ int builtin_evaluate(std::vector<token> &&tokens, const fdmask &fds) {
}
std::string s = std::to_string(i);
if (iter == Environment.end())
Environment.emplace(std::move(name), std::move(s));
else iter->second = std::move(s);
env.set(name, s);
}
break;
}
@ -529,15 +455,6 @@ int builtin_evaluate(std::vector<token> &&tokens, const fdmask &fds) {
}
if (output == 'b') {
std::string tmp("0b");
/*
fputc('0', stdout);
fputc('b', stdout);
for (int j = 0; j < 32; ++j) {
fputc(i & 0x80000000 ? '1' : '0', stdout);
i <<= 1;
}
fputc('\n', stdout);
*/
for (int j = 0; j < 32; ++j) {
tmp.push_back(i & 0x80000000 ? '1' : '0');

22
builtins.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef __builtins_h__
#define __builtins_h__
#include <vector>
#include <string>
class Environment;
class fdmask;
class token;
int builtin_directory(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_echo(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_parameters(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_quote(Environment &e, const std::vector<std::string> &tokens, const fdmask &);
int builtin_set(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_unset(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_export(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_unexport(Environment &e, const std::vector<std::string> &, const fdmask &);
int builtin_evaluate(Environment &e, std::vector<token> &&, const fdmask &);
#endif

View File

@ -1,92 +1,362 @@
#include "command.h"
#include "phase2-parser.h"
#include "environment.h"
#include "fdset.h"
#include "builtins.h"
#include "mpw-shell.h"
#include <stdexcept>
#include <unordered_map>
#include <cctype>
#include <cerrno>
#include <cstdlib>
#include <unistd.h>
#include <sys/wait.h>
#include <sysexits.h>
namespace {
std::string &lowercase(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), [](char c){ return std::tolower(c); });
return s;
}
std::unordered_map<std::string, int (*)(Environment &, const std::vector<std::string> &, const fdmask &)> builtins = {
{"directory", builtin_directory},
{"echo", builtin_echo},
{"parameters", builtin_parameters},
{"quote", builtin_quote},
{"set", builtin_set},
{"unset", builtin_unset},
{"export", builtin_export},
{"unexport", builtin_unexport},
};
int execute_external(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds) {
std::vector<char *> cargv;
cargv.reserve(argv.size() + 3);
int status;
int pid;
cargv.push_back((char *)"mpw");
//cargv.push_back((char *)"--shell");
unsigned offset = cargv.size();
std::transform(argv.begin(), argv.end(), std::back_inserter(cargv),
[](const std::string &s) { return strdup(s.c_str()); }
);
cargv.push_back(nullptr);
pid = fork();
if (pid < 0) {
perror("fork: ");
exit(EX_OSERR);
}
if (pid == 0) {
// also export environment...
// handle any indirection...
fds.dup();
execvp(cargv.front(), cargv.data());
perror("execvp: ");
exit(EX_OSERR);
}
std::for_each(cargv.begin()+offset, cargv.end(), free);
for(;;) {
int status;
pid_t ok;
ok = waitpid(pid, &status, 0);
if (ok < 0) {
if (errno == EINTR) continue;
perror("waitpid:");
exit(EX_OSERR);
}
if (WIFEXITED(status)) return WEXITSTATUS(status);
if (WIFSIGNALED(status)) return -9; // command-. user abort exits via -9.
fprintf(stderr, "waitpid - unexpected result\n");
exit(EX_OSERR);
}
}
}
//std::string expand_vars(const std::string &s, const class Environment &);
command::~command()
{}
/*
* todo -- all errors should be handled the same, regardless of source. (throw, parse, etc).
* echo and error should respect the active fdmask.
*/
int simple_command::execute() {
// echo
fprintf(stderr, " %s\n", text.c_str());
return 0;
int simple_command::execute(Environment &env, const fdmask &fds) {
std::string s = expand_vars(text, env);
if (env.echo()) fprintf(stderr, " %s\n", s.c_str());
process p;
try {
auto tokens = tokenize(s, false);
parse_tokens(std::move(tokens), p);
} catch(std::exception &e) {
fprintf(stderr, "%s", e.what());
return env.status(-4);
}
if (p.arguments.empty()) return 0;
fdmask newfds = p.fds | fds;
std::string name = p.arguments.front();
lowercase(name);
auto iter = builtins.find(name);
if (iter != builtins.end()) {
int status = iter->second(env, p.arguments, newfds);
return env.status(status);
}
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);
}
int status = execute_external(env, p.arguments, newfds);
return env.status(status);
}
int or_command::execute() {
/*
for (const auto &c : children) {
if (!c) throw std::runtime_error("corruption in || command.");
}
*/
int evaluate_command::execute(Environment &env, const fdmask &fds) {
std::string s = expand_vars(text, env);
if (env.echo()) fprintf(stderr, " %s\n", s.c_str());
auto tokens = tokenize(s, true);
if (tokens.empty()) return 0;
int status = builtin_evaluate(env, std::move(tokens), fds);
return env.status(status);
}
int or_command::execute(Environment &e, const fdmask &fds) {
int rv = 0;
bool pv = e.and_or(true);
for (auto &c : children) {
rv = c->execute();
rv = c->execute(e, fds);
if (rv == 0) return rv;
}
return rv;
e.and_or(pv);
return e.status(rv);
}
int and_command::execute() {
int and_command::execute(Environment &e, const fdmask &fds) {
int rv = 0;
bool pv = e.and_or(true);
for (auto &c : children) {
rv = c->execute();
rv = c->execute(e, fds);
if (rv != 0) return rv;
}
e.and_or(pv);
return rv;
}
int vector_command::execute() {
int vector_command::execute(Environment &e, const fdmask &fds) {
int rv = 0;
for (auto &c : children) {
rv = c->execute();
// todo -- env.exit
rv = c->execute(e, fds);
if (e.exit()) break;
}
return rv;
return e.status(rv);
}
int begin_command::execute() {
int error_command::execute(Environment &e, const fdmask &fds) {
std::string s = expand_vars(text, e);
if (e.echo()) fprintf(stderr, " %s\n", s.c_str());
switch(type) {
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;
}
return e.status(-3);
}
namespace {
int check_ends(const std::string &s, fdmask &fds) {
// MPW ignores any END tokens other than redirection.
process p;
try {
auto tokens = tokenize(s, false);
parse_tokens(std::move(tokens), p);
}
catch (std::exception &e) {
fprintf(stderr, "%s\n", e.what());
return -4;
}
fds = p.fds.to_mask();
return 0;
}
}
int begin_command::execute(Environment &env, const fdmask &fds) {
// todo -- parse end for redirection.
std::string b = expand_vars(begin, env);
std::string e = expand_vars(end, env);
// echo!
fprintf(stderr, " %s ... %s\n",
type == BEGIN ? "begin" : "(",
end.c_str()
if (env.echo()) fprintf(stderr, " %s ... %s\n",
b.c_str(),
e.c_str()
);
int rv = vector_command::execute();
fprintf(stderr, " %s\n", type == BEGIN ? "end" : ")");
return rv;
// 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 = vector_command::execute(env, newfds);
if (env.echo()) fprintf(stderr, " %s\n", type == BEGIN ? "end" : ")");
return env.status(rv);
}
int if_command::execute() {
namespace {
bool evaluate(int type, const std::string &s, Environment &env) {
auto tokens = tokenize(s, true);
std::reverse(tokens.begin(), tokens.end());
int32_t e;
switch(type) {
default: return 0;
case ELSE_IF:
tokens.pop_back();
case IF:
tokens.pop_back();
try {
e = evaluate_expression("If", std::move(tokens));
}
catch (std::exception &ex) {
fprintf(stderr, "%s\n", ex.what());
env.status(-5);
e = 0;
}
break;
case ELSE:
e = 1;
if (tokens.size() > 1) {
fprintf(stderr, "### Else - Missing if keyword.\n");
fprintf(stderr, "# Usage - Else [if expression...]\n");
env.status(-3);
e = 0;
}
}
return e;
}
}
int if_command::execute(Environment &env, const fdmask &fds) {
int rv = 0;
bool ok = false;
std::string e = expand_vars(end, env);
// parse end for indirection.
fdmask newfds;
int status = check_ends(e, newfds);
newfds |= fds;
if (status) return env.status(status);
// todo -- parse end for redirection.
for (auto &c : clauses) {
if (c->type == IF) {// special.
fprintf(stderr, " %s ... %s\n",
c->clause.c_str(), end.c_str());
}
else {
fprintf(stderr, " %s\n", c->clause.c_str());
std::string s = expand_vars(c->clause, env);
if (env.echo()) {
if (c->type == IF) { // special.
fprintf(stderr, " %s ... %s\n",
s.c_str(), e.c_str());
}
else {
fprintf(stderr, " %s\n", s.c_str());
}
}
if (ok) continue;
ok = c->evaluate();
ok = evaluate(c->type, s, env);
if (!ok) continue;
rv = c->execute();
rv = c->execute(env, newfds);
}
fprintf(stderr, " end\n");
return rv;
if (env.echo()) fprintf(stderr, " end\n");
return env.status(rv);
}
/*
@ -95,6 +365,6 @@ int if_else_clause::execute() {
}
*/
bool if_else_clause::evaluate() {
bool if_else_clause::evaluate(const Environment &e) {
return false;
}

View File

@ -11,6 +11,9 @@ typedef std::unique_ptr<struct command> command_ptr;
typedef std::vector<command_ptr> command_ptr_vector;
typedef std::array<command_ptr, 2> command_ptr_pair;
#include "environment.h"
class Environment;
class fdmask;
struct command {
@ -19,7 +22,17 @@ struct command {
int type = 0;
virtual ~command();
virtual int execute() = 0;
virtual int execute(Environment &e, const fdmask &fds) = 0;
};
struct error_command : public command {
template<class S>
error_command(int t, S &&s) : command(t), text(std::forward<S>(s))
{}
std::string text;
virtual int execute(Environment &e, const fdmask &fds) override final;
};
struct simple_command : public command {
@ -29,7 +42,17 @@ struct simple_command : public command {
std::string text;
virtual int execute() final override;
virtual int execute(Environment &e, const fdmask &fds) final override;
};
struct evaluate_command : public command {
template<class S>
evaluate_command(S &&s) : command(EVALUATE), text(std::forward<S>(s))
{}
std::string text;
virtual int execute(Environment &e, const fdmask &fds) final override;
};
struct binary_command : public command {
@ -47,7 +70,7 @@ struct or_command : public binary_command {
binary_command(PIPE_PIPE, std::move(a), std::move(b))
{}
virtual int execute() final override;
virtual int execute(Environment &e, const fdmask &fds) final override;
};
struct and_command : public binary_command {
@ -55,7 +78,7 @@ struct and_command : public binary_command {
binary_command(AMP_AMP, std::move(a), std::move(b))
{}
virtual int execute() final override;
virtual int execute(Environment &e, const fdmask &fds) final override;
};
@ -65,7 +88,7 @@ struct pipe_command : public binary_command {
binary_command(PIPE, std::move(a), std::move(b))
{}
virtual int execute() final override;
virtual int execute(Environment &e) final override;
};
#endif
@ -75,18 +98,19 @@ struct vector_command : public command {
{}
command_ptr_vector children;
virtual int execute() override;
virtual int execute(Environment &e, const fdmask &fds) override;
};
struct begin_command : public vector_command {
template<class S>
begin_command(int t, command_ptr_vector &&v, S &&s) :
vector_command(t, std::move(v)), end(std::forward<S>(s))
template<class S1, class S2>
begin_command(int t, command_ptr_vector &&v, S1 &&b, S2 &&e) :
vector_command(t, std::move(v)), begin(std::forward<S1>(b)), end(std::forward<S2>(e))
{}
std::string begin;
std::string end;
virtual int execute() final override;
virtual int execute(Environment &e, const fdmask &fds) final override;
};
typedef std::unique_ptr<struct if_else_clause> if_else_clause_ptr;
@ -103,7 +127,7 @@ struct if_command : public command {
clause_vector_type clauses;
std::string end;
virtual int execute() final override;
virtual int execute(Environment &e, const fdmask &fds) final override;
};
struct if_else_clause : public vector_command {
@ -115,8 +139,7 @@ struct if_else_clause : public vector_command {
std::string clause;
bool evaluate();
//virtual int execute() final override;
bool evaluate(const Environment &e);
};
#endif

102
environment.cpp Normal file
View File

@ -0,0 +1,102 @@
#include "environment.h"
#include <cstdio>
#include <cstdarg>
namespace {
std::string &lowercase(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), [](char c){ return std::tolower(c); });
return s;
}
/* 0 or "" -> false. all others -> true */
bool tf(const std::string &s) {
if (s.empty()) return false;
if (s.size() == 1 && s == "0") return false;
return true;
}
}
Environment::iterator Environment::find( const std::string & key ) {
std::string k(key);
lowercase(k);
return _table.find(k);
}
Environment::const_iterator Environment::find( const std::string & key ) const {
std::string k(key);
lowercase(k);
return _table.find(k);
}
void Environment::set(const std::string &key, const std::string &value, bool exported) {
std::string k(key);
lowercase(k);
if (k == "echo") _echo = tf(value);
if (k == "exit") _exit = tf(value);
if (k == "test") _test = tf(value);
// don't need to check {status} because that will be clobbered
// by the return value.
EnvironmentEntry v(value, exported);
auto iter = _table.find(k);
if (iter == _table.end()) {
_table.emplace(std::make_pair(k, std::move(v)));
}
else {
// if previously exported, keep exported.
if (iter->second) v = true;
iter->second = std::move(v);
}
}
void Environment::unset(const std::string &key) {
std::string k(key);
lowercase(k);
if (k == "echo") _echo = false;
if (k == "exit") _exit = false;
if (k == "test") _test = false;
_table.erase(k);
}
void Environment::unset() {
_table.clear();
_echo = false;
_exit = false;
_test = false;
_status = 0;
}
int Environment::status(int i) {
if (_status == i) return i;
_status = i;
_table["status"] = std::to_string(i);
return i;
}
/*
int Environment::status(int i) {
status(i, std::nothrow);
if (_exit) {
throw std::runtime_error("Execution of input terminated.");
}
return i;
}
void Environment::echo(const char *fmt, ...) {
if (_echo) {}
va_list ap;
va_start(ap, fmt);
va_end(ap);
vfprintf(stderr, fmt, ap);
}
}
*/

106
environment.h Normal file
View File

@ -0,0 +1,106 @@
#ifndef __environment_h__
#define __environment_h__
#include <string>
#include <unordered_map>
#include <utility>
// environment has a bool which indicates if exported.
struct EnvironmentEntry {
public:
operator bool() const { return exported; }
operator bool&() { return exported; }
operator const std::string&() const { return value; }
operator std::string&() { return value; }
EnvironmentEntry() = default;
EnvironmentEntry(const EnvironmentEntry &) = default;
EnvironmentEntry(EnvironmentEntry &&) = default;
EnvironmentEntry(const std::string &s, bool e = false) : value(s), exported(e)
{}
EnvironmentEntry(std::string &&s, bool e = false) : value(std::move(s)), exported(e)
{}
~EnvironmentEntry() = default;
EnvironmentEntry& operator=(bool rhs) { exported = rhs; return *this; }
EnvironmentEntry& operator=(const std::string &rhs) { value = rhs; return *this; }
EnvironmentEntry& operator=(const EnvironmentEntry &) = default;
EnvironmentEntry& operator=(EnvironmentEntry &&) = default;
private:
std::string value;
bool exported = false;
};
class Environment {
public:
typedef std::unordered_map<std::string, EnvironmentEntry> mapped_type;
typedef mapped_type::iterator iterator;
typedef mapped_type::const_iterator const_iterator;
//const EnvironmentEntry & lookup(const std::string &s);
void set(const std::string &k, const std::string &value, bool exported = false);
void unset(const std::string &k);
void unset();
constexpr bool echo() const noexcept { return _echo; }
constexpr bool test() const noexcept { return _test; }
constexpr bool exit() const noexcept { return _and_or ? false : _exit; }
constexpr int status() const noexcept { return _status; }
bool and_or(bool v) { std::swap(v, _and_or); return v; }
int status(int i);
constexpr bool startup() const noexcept { return _startup; }
constexpr void startup(bool tf) noexcept { _startup = tf; }
constexpr bool passthrough() const noexcept { return _passthrough; }
constexpr void passthrough(bool tf) noexcept { _passthrough = tf; }
template<class FX>
void foreach(FX && fx) { for (const auto &kv : _table) { fx(kv.first, kv.second); }}
iterator begin() { return _table.begin(); }
const_iterator begin() const { return _table.begin(); }
const_iterator cbegin() const { return _table.cbegin(); }
iterator end() { return _table.end(); }
const_iterator end() const { return _table.end(); }
const_iterator cend() const { return _table.cend(); }
iterator find( const std::string & key );
const_iterator find( const std::string & key ) const;
void echo(const char *format, ...);
private:
// magic variables.
bool _exit = false;
bool _test = false;
bool _and_or = false;
bool _echo = false;
int _status = 0;
int _indent = 0;
bool _startup = false;
bool _passthrough = false;
std::unordered_map<std::string, EnvironmentEntry> _table;
};
#endif

View File

@ -61,7 +61,8 @@ class fdmask {
int operator[](unsigned index) const {
return _fds[index];
if (_fds[index] >= 0) return _fds[index];
return default_fd(index);
}
fdmask &operator|=(const fdmask &rhs) {
@ -72,6 +73,12 @@ class fdmask {
}
private:
static int default_fd(int index) {
static constexpr std::array<int, 3> _default_fds = {{ STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO }};
return _default_fds[index];
}
friend class fdset;
std::array<int, 3> _fds = {{ -1, -1, -1 }};
};

View File

@ -20,6 +20,7 @@
*
* Echo {Echo} # control the echoing of commands to diagnostic output
* Echo {Exit} # control script termination based on {Status}
* Echo {Test} # don't actually run anything.
*
*/

View File

@ -46,9 +46,11 @@
// flag to pass through vs "" ?
auto iter = env.find(var);
if (iter == env.end()) {
line.push_back('{');
line.append(var);
line.push_back('}');
if (env.passthrough()) {
line.push_back('{');
line.append(var);
line.push_back('}');
}
}
else {
line.append((std::string)iter->second);
@ -112,7 +114,7 @@
* set q '"' ; echo {q} dsfsdf"
*/
std::string expand_vars(const std::string &s, const std::unordered_map<std::string, EnvironmentEntry> &env) {
std::string expand_vars(const std::string &s, const Environment &env) {
if (s.find('{') == s.npos) return s;
std::string var;

View File

@ -9,23 +9,20 @@
#include <stdlib.h>
#include <cerrno>
#define NOCOMMAND
#include "mpw-shell.h"
#include "fdset.h"
#include "phase1.h"
#include "phase2.h"
#include "command.h"
std::unordered_map<std::string, EnvironmentEntry> Environment;
// should set {MPW}, {MPWVersion}, then execute {MPW}StartUp
void init(void) {
Environment.emplace("status", std::string("0"));
Environment.emplace("exit", std::string("1")); // terminate script on error.
void init(Environment &e) {
e.set("status", std::string("0"));
e.set("exit", std::string("1")); // terminate script on error.
e.set("echo", std::string("1"));
}
int read_stdin(phase1 &p) {
@ -61,18 +58,16 @@ int read_stdin(phase1 &p) {
int main(int argc, char **argv) {
init();
command_ptr head;
Environment e;
init(e);
phase1 p1;
phase2 p2;
p1 >>= p2;
p2 >>= [](command_ptr &&ptr) {
printf("command: %d\n", ptr->type);
ptr->execute();
p2 >>= [&e](command_ptr &&ptr) {
fdmask fds;
ptr->execute(e, fds);
};
/*
p1 >>= [&p2](std::string &&s) {

View File

@ -7,94 +7,10 @@
#include <memory>
#include <cstdint>
#include "environment.h"
const unsigned char escape = 0xb6;
// environment has a bool which indicates if exported.
struct EnvironmentEntry {
public:
operator bool() const { return exported; }
operator bool&() { return exported; }
operator const std::string&() const { return value; }
operator std::string&() { return value; }
EnvironmentEntry() = default;
EnvironmentEntry(const EnvironmentEntry &) = default;
EnvironmentEntry(EnvironmentEntry &&) = default;
EnvironmentEntry(const std::string &s, bool e = false) : value(s), exported(e)
{}
EnvironmentEntry(std::string &&s, bool e = false) : value(std::move(s)), exported(e)
{}
~EnvironmentEntry() = default;
EnvironmentEntry& operator=(bool &rhs) { exported = rhs; return *this; }
EnvironmentEntry& operator=(const std::string &rhs) { value = rhs; return *this; }
EnvironmentEntry& operator=(const EnvironmentEntry &) = default;
EnvironmentEntry& operator=(EnvironmentEntry &&) = default;
private:
std::string value;
bool exported = false;
};
extern std::unordered_map<std::string, EnvironmentEntry> Environment;
#ifndef NOCOMMAND
enum {
command_if = 1,
command_else,
command_else_if,
command_end,
command_begin,
command_evaluate,
};
class command;
typedef std::shared_ptr<command> command_ptr;
typedef std::weak_ptr<command> weak_command_ptr;
class command {
public:
unsigned type = 0;
unsigned line = 0;
unsigned level = 0;
std::string string;
command_ptr next;
weak_command_ptr alternate; // if -> else -> end. weak to prevent cycles.
command() = default;
command(command &&) = default;
command(const command &) = default;
command(unsigned t, const std::string &s) :
type(t), string(s)
{}
command(unsigned t, std::string &&s) :
type(t), string(std::move(s))
{}
command(const std::string &s) : string(s)
{}
command(std::string &&s) : string(std::move(s))
{}
};
command_ptr read_fd(int fd);
command_ptr read_file(const std::string &);
command_ptr read_string(const std::string &);
int execute(command_ptr cmd);
#endif
class token {
public:
@ -133,7 +49,7 @@ public:
std::vector<token> tokenize(const std::string &s, bool eval = false);
std::string expand_vars(const std::string &s, const std::unordered_map<std::string, EnvironmentEntry> &env);
std::string expand_vars(const std::string &s, const class Environment &);
//std::string quote(std::string &&s);
std::string quote(const std::string &s);
@ -149,16 +65,6 @@ void parse_tokens(std::vector<token> &&tokens, process &p);
int32_t evaluate_expression(const std::string &name, std::vector<token> &&tokens);
int builtin_directory(const std::vector<std::string> &, const fdmask &);
int builtin_echo(const std::vector<std::string> &, const fdmask &);
int builtin_parameters(const std::vector<std::string> &, const fdmask &);
int builtin_quote(const std::vector<std::string> &tokens, const fdmask &);
int builtin_set(const std::vector<std::string> &, const fdmask &);
int builtin_unset(const std::vector<std::string> &, const fdmask &);
int builtin_export(const std::vector<std::string> &, const fdmask &);
int builtin_unexport(const std::vector<std::string> &, const fdmask &);
int builtin_evaluate(std::vector<token> &&, const fdmask &);
#endif

View File

@ -28,11 +28,13 @@ std::unique_ptr<phase2_parser> phase2_parser::make() {
%type start {void}
%type opt_command_list {void}
%type command_list {void}
%type opt_command_sep {void}
/* these are put into a queue for immmediate execution */
start ::= opt_command_list.
start ::= opt_command_list.
opt_command_list ::= .
opt_command_list ::= command_list.
@ -44,16 +46,30 @@ command_list ::= opt_command(C) sep. {
if (C) command_queue.emplace_back(std::move(C));
}
/*
command_list ::= opt_command_sep.
command_list ::= command_list opt_command_sep.
opt_command_sep ::= opt_command(C) sep. {
if (C) command_queue.emplace_back(std::move(C));
}
*/
/*
compound_list is identical to command_list, but it is not executed immediately.
*/
%type opt_compound_list { command_ptr_vector }
opt_compound_list(RV) ::= . { /* RV */ }
opt_compound_list(RV) ::= compound_list(L). { RV = std::move(L); }
%type compound_list { command_ptr_vector }
//%type opt_paren_list { command_ptr_vector }
//%type paren_list { command_ptr_vector }
opt_compound_list ::= .
opt_compound_list(RV) ::= compound_list(L). { RV = std::move(L); }
compound_list(RV) ::= compound_list(L) opt_command(C) sep . {
RV = std::move(L);
if (C) RV.emplace_back(std::move(C));
@ -63,21 +79,36 @@ compound_list(RV) ::= opt_command(C) sep. {
if (C) RV.emplace_back(std::move(C));
}
/*
opt_paren_list ::= .
opt_paren_list(RV) ::= paren_list(L). { RV = std::move(L); }
paren_list(RV) ::= opt_command(C). {
if (C) RV.emplace_back(std::move(C));
}
paren_list(RV) ::= paren_list(L) sep opt_command(C). {
RV = std::move(L);
if (C) RV.emplace_back(std::move(C));
}
*/
sep ::= SEMI.
sep ::= NL.
%type opt_command { command_ptr }
opt_command(RV) ::= command(C). { RV = std::move(C); }
opt_command(RV) ::= . { /* RV */ }
opt_command ::= .
%type command { command_ptr }
/* nb -- ||, &&, | -- both sides are optional. This does not. */
command(RV) ::= command(L) PIPE_PIPE opt_nl command(R). {
RV = std::make_unique<or_command>(std::move(L), std::move(R));
}
command(RV) ::= command(L) AMP_AMP opt_nl command(R). {
command(RV) ::= command(L) AMP_AMP opt_nl command(R). {
RV = std::make_unique<and_command>(std::move(L), std::move(R));
}
@ -90,17 +121,48 @@ command(RV) ::= command PIPE opt_nl command. {
command(RV) ::= term(T). { RV = std::move(T); }
term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move(C)); }
term(RV) ::= EVALUATE(C). { RV = std::make_unique<simple_command>(std::move(C)); }
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
term(RV) ::= if_command(C). { RV = std::move(C); }
term(RV) ::= begin_command(C). { RV = std::move(C); }
term(RV) ::= paren_command(C). { RV = std::move(C); }
paren_command(RV) ::= LPAREN(T) opt_compound_list(L) RPAREN(E). {
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(E));
/*
* fall back to an end error. w/o fallback, it will cause a parse conflict.
*/
/*
%fallback ERROR END RPAREN ELSE ELSE_IF.
term(RV) ::= ERROR(C). {
RV = std::make_unique<error_command>(@C, std::move(C));
}
*/
/* opt_compound_list requires a sep after every item -- not ok for paren command! */
/*
paren_command(RV) ::= LPAREN(T) opt_paren_list(L) RPAREN(E). {
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(T), std::move(E));
}
*/
/* compound list ends with a separator. paren command does not need the final separator */
paren_command(RV) ::= LPAREN(T) opt_command(C) RPAREN(E). {
command_ptr_vector L;
if (C) L.emplace_back(std::move(C));
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(T), std::move(E));
}
paren_command(RV) ::= LPAREN(T) compound_list(L) RPAREN(E). {
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(T), std::move(E));
}
paren_command(RV) ::= LPAREN(T) compound_list(L) command(C) RPAREN(E). {
if (C) L.emplace_back(std::move(C));
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(T), std::move(E));
}
begin_command(RV) ::= BEGIN(T) sep opt_compound_list(L) END(E). {
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(E));
RV = std::make_unique<begin_command>(@T, std::move(L), std::move(T), std::move(E));
}
if_command(RV) ::= IF(I) sep opt_compound_list(L) END(E). {

View File

@ -34,10 +34,10 @@
vstring =
[{]
( (any-[{]) )*
( (any-[}]) )*
[}]
$err{
throw std::runtime_error("### MPW Shell - 's must occur in pairs.");
throw std::runtime_error("### MPW Shell - {s must occur in pairs.");
}
;
@ -265,8 +265,28 @@ void phase2_parser::parse_failure() {
}
void phase2_parser::syntax_error(int yymajor, std::string &yyminor) {
if (!error)
fprintf(stderr, "### Parse error near %s\n", yyminor.c_str());
/*
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;
}