mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2024-06-07 19:29:42 +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
|
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
|
mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp
|
||||||
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp)
|
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp)
|
||||||
|
|
||||||
# all this for -std=c++14
|
# all this for -std=c++14
|
||||||
set_property (TARGET mpw-shell PROPERTY CXX_STANDARD 14)
|
set_property (TARGET mpw-shell PROPERTY CXX_STANDARD 14)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "fdset.h"
|
#include "fdset.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
|
#include "environment.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -11,14 +12,17 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
|
//MAXPATHLEN
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/*
|
||||||
std::string &lowercase(std::string &s) {
|
std::string &lowercase(std::string &s) {
|
||||||
std::transform(s.begin(), s.end(), s.begin(), [](char c){ return std::tolower(c); });
|
std::transform(s.begin(), s.end(), s.begin(), [](char c){ return std::tolower(c); });
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
// doesn't handle flag arguments but builtins don't have arguments.
|
// doesn't handle flag arguments but builtins don't have arguments.
|
||||||
|
|
||||||
template<class FX>
|
template<class FX>
|
||||||
|
@ -40,83 +44,6 @@ namespace {
|
||||||
return out;
|
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
|
#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) {
|
for (auto iter = tokens.begin() + 1; iter != tokens.end(); ++iter) {
|
||||||
|
|
||||||
std::string name = *iter;
|
const std::string &name = *iter;
|
||||||
lowercase(name);
|
|
||||||
|
|
||||||
Environment.erase(name);
|
env.unset(name);
|
||||||
}
|
}
|
||||||
// unset [no arg] removes ALL variables
|
// unset [no arg] removes ALL variables
|
||||||
if (tokens.size() == 1) {
|
if (tokens.size() == 1) {
|
||||||
Environment.clear();
|
env.unset();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int builtin_set(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||||
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) {
|
|
||||||
// set var name -- set
|
// set var name -- set
|
||||||
// set var -- just print the value
|
// 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) {
|
if (tokens.size() == 1) {
|
||||||
|
|
||||||
for (const auto &kv : Environment) {
|
for (const auto &kv : env) {
|
||||||
std::string name = quote(kv.first);
|
std::string name = quote(kv.first);
|
||||||
std::string value = quote(kv.second);
|
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) {
|
if (tokens.size() == 2) {
|
||||||
std::string name = tokens[1];
|
std::string name = tokens[1];
|
||||||
lowercase(name);
|
auto iter = env.find(name);
|
||||||
auto iter = Environment.find(name);
|
if (iter == env.end()) {
|
||||||
if (iter == Environment.end()) {
|
|
||||||
fprintf(stderr, "### Set - No variable definition exists for %s.\n", name.c_str());
|
fprintf(stderr, "### Set - No variable definition exists for %s.\n", name.c_str());
|
||||||
return 2;
|
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 name = tokens[1+exported];
|
||||||
std::string value = tokens[2+exported];
|
std::string value = tokens[2+exported];
|
||||||
lowercase(name);
|
|
||||||
|
|
||||||
Environment[name] = EnvironmentEntry(std::move(value), exported);
|
env.set(name, value, exported);
|
||||||
return 0;
|
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";
|
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 ";
|
name = export_or_unexport ? "Export " : "Unexport ";
|
||||||
|
|
||||||
for (const auto &kv : Environment) {
|
for (const auto &kv : env) {
|
||||||
const std::string& vname = kv.first;
|
const std::string& vname = kv.first;
|
||||||
if (kv.second == export_or_unexport)
|
if (kv.second == export_or_unexport)
|
||||||
fprintf(stdout, "%s%s\n", flags._s ? "" : name, quote(vname).c_str());
|
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;
|
if (flags._r || flags._s) goto conflict;
|
||||||
|
|
||||||
for (std::string s : argv) {
|
for (std::string s : argv) {
|
||||||
lowercase(s);
|
auto iter = env.find(s);
|
||||||
auto iter = Environment.find(s);
|
if (iter != env.end()) iter->second = export_or_unexport;
|
||||||
if (iter != Environment.end()) iter->second = export_or_unexport;
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -295,21 +209,20 @@ conflict:
|
||||||
fprintf(stderr, "# Usage - %s [-r | -s | name...]\n", name);
|
fprintf(stderr, "# Usage - %s [-r | -s | name...]\n", name);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
int builtin_export(const std::vector<std::string> &tokens, const fdmask &fds) {
|
|
||||||
|
|
||||||
//io_helper io(fds);
|
int builtin_export(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||||
return export_common(true, tokens, 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(env, false, tokens, fds);
|
||||||
return export_common(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);
|
//io_helper io(fds);
|
||||||
|
|
||||||
|
@ -333,7 +246,7 @@ int builtin_echo(const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||||
return 0;
|
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...
|
// todo...
|
||||||
|
|
||||||
//io_helper io(fds);
|
//io_helper io(fds);
|
||||||
|
@ -359,7 +272,7 @@ int builtin_quote(const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||||
return 0;
|
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);
|
//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 [-q]
|
||||||
// directory path
|
// directory path
|
||||||
|
|
||||||
|
@ -416,12 +329,29 @@ int builtin_directory(const std::vector<std::string> &tokens, const fdmask &fds)
|
||||||
return 1;
|
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 {
|
else {
|
||||||
// pwd
|
// 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) {
|
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 expression
|
||||||
// evaluate variable = expression
|
// evaluate variable = 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)) {
|
if (is_assignment(type)) {
|
||||||
|
|
||||||
std::string name = tokens.back().string;
|
std::string name = tokens.back().string;
|
||||||
lowercase(name);
|
|
||||||
|
|
||||||
tokens.pop_back();
|
tokens.pop_back();
|
||||||
tokens.pop_back();
|
tokens.pop_back();
|
||||||
|
@ -490,14 +419,14 @@ int builtin_evaluate(std::vector<token> &&tokens, const fdmask &fds) {
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case '=':
|
case '=':
|
||||||
Environment[name] = std::to_string(i);
|
env.set(name, std::to_string(i));
|
||||||
break;
|
break;
|
||||||
case '+=':
|
case '+=':
|
||||||
case '-=':
|
case '-=':
|
||||||
{
|
{
|
||||||
value old;
|
value old;
|
||||||
auto iter = Environment.find(name);
|
auto iter = env.find(name);
|
||||||
if (iter != Environment.end()) old = (const std::string &)iter->second;
|
if (iter != env.end()) old = (const std::string &)iter->second;
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case '+=':
|
case '+=':
|
||||||
|
@ -509,10 +438,7 @@ int builtin_evaluate(std::vector<token> &&tokens, const fdmask &fds) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string s = std::to_string(i);
|
std::string s = std::to_string(i);
|
||||||
if (iter == Environment.end())
|
env.set(name, s);
|
||||||
Environment.emplace(std::move(name), std::move(s));
|
|
||||||
else iter->second = std::move(s);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -529,15 +455,6 @@ int builtin_evaluate(std::vector<token> &&tokens, const fdmask &fds) {
|
||||||
}
|
}
|
||||||
if (output == 'b') {
|
if (output == 'b') {
|
||||||
std::string tmp("0b");
|
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) {
|
for (int j = 0; j < 32; ++j) {
|
||||||
tmp.push_back(i & 0x80000000 ? '1' : '0');
|
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 "command.h"
|
||||||
#include "phase2-parser.h"
|
#include "phase2-parser.h"
|
||||||
|
#include "environment.h"
|
||||||
|
#include "fdset.h"
|
||||||
|
#include "builtins.h"
|
||||||
|
#include "mpw-shell.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
#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()
|
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() {
|
int simple_command::execute(Environment &env, const fdmask &fds) {
|
||||||
// echo
|
std::string s = expand_vars(text, env);
|
||||||
fprintf(stderr, " %s\n", text.c_str());
|
|
||||||
return 0;
|
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() {
|
int evaluate_command::execute(Environment &env, const fdmask &fds) {
|
||||||
/*
|
|
||||||
for (const auto &c : children) {
|
std::string s = expand_vars(text, env);
|
||||||
if (!c) throw std::runtime_error("corruption in || command.");
|
|
||||||
}
|
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;
|
int rv = 0;
|
||||||
|
bool pv = e.and_or(true);
|
||||||
|
|
||||||
for (auto &c : children) {
|
for (auto &c : children) {
|
||||||
rv = c->execute();
|
rv = c->execute(e, fds);
|
||||||
if (rv == 0) return rv;
|
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;
|
int rv = 0;
|
||||||
|
bool pv = e.and_or(true);
|
||||||
|
|
||||||
for (auto &c : children) {
|
for (auto &c : children) {
|
||||||
rv = c->execute();
|
rv = c->execute(e, fds);
|
||||||
if (rv != 0) return rv;
|
if (rv != 0) return rv;
|
||||||
}
|
}
|
||||||
|
e.and_or(pv);
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int vector_command::execute() {
|
int vector_command::execute(Environment &e, const fdmask &fds) {
|
||||||
|
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
for (auto &c : children) {
|
for (auto &c : children) {
|
||||||
|
|
||||||
rv = c->execute();
|
rv = c->execute(e, fds);
|
||||||
// todo -- env.exit
|
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.
|
// todo -- parse end for redirection.
|
||||||
|
|
||||||
|
std::string b = expand_vars(begin, env);
|
||||||
|
std::string e = expand_vars(end, env);
|
||||||
|
|
||||||
// echo!
|
// echo!
|
||||||
fprintf(stderr, " %s ... %s\n",
|
if (env.echo()) fprintf(stderr, " %s ... %s\n",
|
||||||
type == BEGIN ? "begin" : "(",
|
b.c_str(),
|
||||||
end.c_str()
|
e.c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
int rv = vector_command::execute();
|
// tokenize the begin and end commands.
|
||||||
fprintf(stderr, " %s\n", type == BEGIN ? "end" : ")");
|
// begin may not have extra arguments. end may have redirection.
|
||||||
return rv;
|
|
||||||
|
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;
|
int rv = 0;
|
||||||
bool ok = false;
|
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) {
|
for (auto &c : clauses) {
|
||||||
if (c->type == IF) {// special.
|
std::string s = expand_vars(c->clause, env);
|
||||||
fprintf(stderr, " %s ... %s\n",
|
|
||||||
c->clause.c_str(), end.c_str());
|
if (env.echo()) {
|
||||||
}
|
if (c->type == IF) { // special.
|
||||||
else {
|
fprintf(stderr, " %s ... %s\n",
|
||||||
fprintf(stderr, " %s\n", c->clause.c_str());
|
s.c_str(), e.c_str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(stderr, " %s\n", s.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok) continue;
|
if (ok) continue;
|
||||||
ok = c->evaluate();
|
ok = evaluate(c->type, s, env);
|
||||||
if (!ok) continue;
|
if (!ok) continue;
|
||||||
rv = c->execute();
|
rv = c->execute(env, newfds);
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, " end\n");
|
if (env.echo()) fprintf(stderr, " end\n");
|
||||||
return rv;
|
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;
|
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::vector<command_ptr> command_ptr_vector;
|
||||||
typedef std::array<command_ptr, 2> command_ptr_pair;
|
typedef std::array<command_ptr, 2> command_ptr_pair;
|
||||||
|
|
||||||
|
#include "environment.h"
|
||||||
|
class Environment;
|
||||||
|
class fdmask;
|
||||||
|
|
||||||
struct command {
|
struct command {
|
||||||
|
|
||||||
|
@ -19,7 +22,17 @@ struct command {
|
||||||
|
|
||||||
int type = 0;
|
int type = 0;
|
||||||
virtual ~command();
|
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 {
|
struct simple_command : public command {
|
||||||
|
@ -29,7 +42,17 @@ struct simple_command : public command {
|
||||||
|
|
||||||
std::string text;
|
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 {
|
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))
|
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 {
|
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))
|
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))
|
binary_command(PIPE, std::move(a), std::move(b))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual int execute() final override;
|
virtual int execute(Environment &e) final override;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -75,18 +98,19 @@ struct vector_command : public command {
|
||||||
{}
|
{}
|
||||||
|
|
||||||
command_ptr_vector children;
|
command_ptr_vector children;
|
||||||
virtual int execute() override;
|
virtual int execute(Environment &e, const fdmask &fds) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct begin_command : public vector_command {
|
struct begin_command : public vector_command {
|
||||||
template<class S>
|
template<class S1, class S2>
|
||||||
begin_command(int t, command_ptr_vector &&v, S &&s) :
|
begin_command(int t, command_ptr_vector &&v, S1 &&b, S2 &&e) :
|
||||||
vector_command(t, std::move(v)), end(std::forward<S>(s))
|
vector_command(t, std::move(v)), begin(std::forward<S1>(b)), end(std::forward<S2>(e))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
std::string begin;
|
||||||
std::string end;
|
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;
|
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;
|
clause_vector_type clauses;
|
||||||
std::string end;
|
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 {
|
struct if_else_clause : public vector_command {
|
||||||
|
@ -115,8 +139,7 @@ struct if_else_clause : public vector_command {
|
||||||
|
|
||||||
std::string clause;
|
std::string clause;
|
||||||
|
|
||||||
bool evaluate();
|
bool evaluate(const Environment &e);
|
||||||
//virtual int execute() final override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#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 {
|
int operator[](unsigned index) const {
|
||||||
return _fds[index];
|
if (_fds[index] >= 0) return _fds[index];
|
||||||
|
return default_fd(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fdmask &operator|=(const fdmask &rhs) {
|
fdmask &operator|=(const fdmask &rhs) {
|
||||||
|
@ -72,6 +73,12 @@ class fdmask {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
friend class fdset;
|
||||||
std::array<int, 3> _fds = {{ -1, -1, -1 }};
|
std::array<int, 3> _fds = {{ -1, -1, -1 }};
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
*
|
*
|
||||||
* Echo {Echo} # control the echoing of commands to diagnostic output
|
* Echo {Echo} # control the echoing of commands to diagnostic output
|
||||||
* Echo {Exit} # control script termination based on {Status}
|
* Echo {Exit} # control script termination based on {Status}
|
||||||
|
* Echo {Test} # don't actually run anything.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,11 @@
|
||||||
// flag to pass through vs "" ?
|
// flag to pass through vs "" ?
|
||||||
auto iter = env.find(var);
|
auto iter = env.find(var);
|
||||||
if (iter == env.end()) {
|
if (iter == env.end()) {
|
||||||
line.push_back('{');
|
if (env.passthrough()) {
|
||||||
line.append(var);
|
line.push_back('{');
|
||||||
line.push_back('}');
|
line.append(var);
|
||||||
|
line.push_back('}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
line.append((std::string)iter->second);
|
line.append((std::string)iter->second);
|
||||||
|
@ -112,7 +114,7 @@
|
||||||
* set q '"' ; echo {q} dsfsdf"
|
* 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;
|
if (s.find('{') == s.npos) return s;
|
||||||
std::string var;
|
std::string var;
|
||||||
|
|
|
@ -9,23 +9,20 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
#define NOCOMMAND
|
|
||||||
#include "mpw-shell.h"
|
#include "mpw-shell.h"
|
||||||
|
#include "fdset.h"
|
||||||
|
|
||||||
#include "phase1.h"
|
#include "phase1.h"
|
||||||
#include "phase2.h"
|
#include "phase2.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
|
|
||||||
std::unordered_map<std::string, EnvironmentEntry> Environment;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// should set {MPW}, {MPWVersion}, then execute {MPW}StartUp
|
// should set {MPW}, {MPWVersion}, then execute {MPW}StartUp
|
||||||
void init(void) {
|
void init(Environment &e) {
|
||||||
Environment.emplace("status", std::string("0"));
|
e.set("status", std::string("0"));
|
||||||
Environment.emplace("exit", std::string("1")); // terminate script on error.
|
e.set("exit", std::string("1")); // terminate script on error.
|
||||||
|
e.set("echo", std::string("1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int read_stdin(phase1 &p) {
|
int read_stdin(phase1 &p) {
|
||||||
|
@ -61,18 +58,16 @@ int read_stdin(phase1 &p) {
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
init();
|
Environment e;
|
||||||
|
init(e);
|
||||||
command_ptr head;
|
|
||||||
|
|
||||||
|
|
||||||
phase1 p1;
|
phase1 p1;
|
||||||
phase2 p2;
|
phase2 p2;
|
||||||
p1 >>= p2;
|
p1 >>= p2;
|
||||||
|
|
||||||
p2 >>= [](command_ptr &&ptr) {
|
p2 >>= [&e](command_ptr &&ptr) {
|
||||||
printf("command: %d\n", ptr->type);
|
fdmask fds;
|
||||||
ptr->execute();
|
ptr->execute(e, fds);
|
||||||
};
|
};
|
||||||
/*
|
/*
|
||||||
p1 >>= [&p2](std::string &&s) {
|
p1 >>= [&p2](std::string &&s) {
|
||||||
|
|
98
mpw-shell.h
98
mpw-shell.h
|
@ -7,94 +7,10 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "environment.h"
|
||||||
|
|
||||||
const unsigned char escape = 0xb6;
|
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 {
|
class token {
|
||||||
public:
|
public:
|
||||||
|
@ -133,7 +49,7 @@ public:
|
||||||
|
|
||||||
|
|
||||||
std::vector<token> tokenize(const std::string &s, bool eval = false);
|
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(std::string &&s);
|
||||||
std::string quote(const 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);
|
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
|
#endif
|
||||||
|
|
|
@ -28,11 +28,13 @@ std::unique_ptr<phase2_parser> phase2_parser::make() {
|
||||||
%type start {void}
|
%type start {void}
|
||||||
%type opt_command_list {void}
|
%type opt_command_list {void}
|
||||||
%type command_list {void}
|
%type command_list {void}
|
||||||
|
%type opt_command_sep {void}
|
||||||
|
|
||||||
/* these are put into a queue for immmediate execution */
|
/* these are put into a queue for immmediate execution */
|
||||||
|
|
||||||
start ::= opt_command_list.
|
|
||||||
|
|
||||||
|
|
||||||
|
start ::= opt_command_list.
|
||||||
opt_command_list ::= .
|
opt_command_list ::= .
|
||||||
opt_command_list ::= 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));
|
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.
|
compound_list is identical to command_list, but it is not executed immediately.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
%type opt_compound_list { command_ptr_vector }
|
%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 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 . {
|
compound_list(RV) ::= compound_list(L) opt_command(C) sep . {
|
||||||
RV = std::move(L);
|
RV = std::move(L);
|
||||||
if (C) RV.emplace_back(std::move(C));
|
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));
|
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 ::= SEMI.
|
||||||
sep ::= NL.
|
sep ::= NL.
|
||||||
|
|
||||||
%type opt_command { command_ptr }
|
%type opt_command { command_ptr }
|
||||||
opt_command(RV) ::= command(C). { RV = std::move(C); }
|
opt_command(RV) ::= command(C). { RV = std::move(C); }
|
||||||
opt_command(RV) ::= . { /* RV */ }
|
opt_command ::= .
|
||||||
|
|
||||||
%type command { command_ptr }
|
%type command { command_ptr }
|
||||||
|
|
||||||
|
/* nb -- ||, &&, | -- both sides are optional. This does not. */
|
||||||
|
|
||||||
command(RV) ::= command(L) PIPE_PIPE opt_nl command(R). {
|
command(RV) ::= command(L) PIPE_PIPE opt_nl command(R). {
|
||||||
RV = std::make_unique<or_command>(std::move(L), std::move(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));
|
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); }
|
command(RV) ::= term(T). { RV = std::move(T); }
|
||||||
|
|
||||||
term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move(C)); }
|
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) ::= if_command(C). { RV = std::move(C); }
|
||||||
term(RV) ::= begin_command(C). { RV = std::move(C); }
|
term(RV) ::= begin_command(C). { RV = std::move(C); }
|
||||||
term(RV) ::= paren_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). {
|
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). {
|
if_command(RV) ::= IF(I) sep opt_compound_list(L) END(E). {
|
||||||
|
|
28
phase2.rl
28
phase2.rl
|
@ -34,10 +34,10 @@
|
||||||
|
|
||||||
vstring =
|
vstring =
|
||||||
[{]
|
[{]
|
||||||
( (any-[{]) )*
|
( (any-[}]) )*
|
||||||
[}]
|
[}]
|
||||||
$err{
|
$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) {
|
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;
|
error = true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user