mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2024-12-28 09:29:57 +00:00
lots of updates!
This commit is contained in:
parent
c8d12c4b3e
commit
5723656988
@ -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)
|
||||
|
@ -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
22
builtins.h
Normal 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
|
346
command.cpp
346
command.cpp
@ -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;
|
||||
}
|
||||
|
49
command.h
49
command.h
@ -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
102
environment.cpp
Normal 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
106
environment.h
Normal 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
|
9
fdset.h
9
fdset.h
@ -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 }};
|
||||
};
|
||||
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
98
mpw-shell.h
98
mpw-shell.h
@ -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
|
||||
|
@ -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). {
|
||||
|
28
phase2.rl
28
phase2.rl
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user