mpw-shell/builtins.cpp

475 lines
9.5 KiB
C++
Raw Normal View History

2016-01-27 17:19:38 +00:00
2016-01-27 15:43:34 +00:00
#include "mpw-shell.h"
#include "fdset.h"
#include "value.h"
2016-02-02 01:38:29 +00:00
#include "environment.h"
2016-01-27 15:43:34 +00:00
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cctype>
2016-02-02 01:38:29 +00:00
//MAXPATHLEN
#include <sys/param.h>
2016-01-27 15:43:34 +00:00
2016-02-02 01:38:29 +00:00
namespace {
2016-01-27 15:43:34 +00:00
2016-02-02 01:38:29 +00:00
/*
2016-01-27 15:43:34 +00:00
std::string &lowercase(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), [](char c){ return std::tolower(c); });
return s;
}
2016-02-02 01:38:29 +00:00
*/
2016-01-27 15:43:34 +00:00
// doesn't handle flag arguments but builtins don't have arguments.
template<class FX>
std::vector<std::string> getopt(const std::vector<std::string> &argv, FX fx) {
std::vector<std::string> out;
out.reserve(argv.size());
std::copy_if(argv.begin()+1, argv.end(), std::back_inserter(out), [&fx](const std::string &s){
if (s.empty()) return false; // ?
if (s.front() == '-') {
std::for_each(s.begin() + 1, s.end(), fx);
return false;
}
return true;
});
return out;
}
}
#undef stdin
#undef stdout
#undef stderr
#define stdin fds[0]
#define stdout fds[1]
#define stderr fds[2]
#undef fprintf
#undef fputs
#undef fputc
#define fprintf(...) dprintf(__VA_ARGS__)
inline int fputs(const char *data, int fd) {
auto rv = write(fd, data, strlen(data)); return rv < 0 ? EOF : rv;
}
inline int fputc(int c, int fd) {
unsigned char tmp = c;
auto rv = write(fd, &tmp, 1); return rv < 0 ? EOF : c;
}
2016-01-27 15:43:34 +00:00
2016-02-02 01:38:29 +00:00
int builtin_unset(Environment &env, const std::vector<std::string> &tokens, const fdmask &) {
2016-01-27 15:43:34 +00:00
for (auto iter = tokens.begin() + 1; iter != tokens.end(); ++iter) {
2016-02-02 01:38:29 +00:00
const std::string &name = *iter;
2016-01-27 15:43:34 +00:00
2016-02-02 01:38:29 +00:00
env.unset(name);
2016-01-27 15:43:34 +00:00
}
// unset [no arg] removes ALL variables
if (tokens.size() == 1) {
2016-02-02 01:38:29 +00:00
env.unset();
2016-01-27 15:43:34 +00:00
}
return 0;
}
2016-02-02 01:38:29 +00:00
int builtin_set(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
2016-01-27 15:43:34 +00:00
// set var name -- set
// set var -- just print the value
// 3.5 supports -e to also export it.
//io_helper io(fds);
2016-01-27 15:43:34 +00:00
if (tokens.size() == 1) {
2016-02-02 01:38:29 +00:00
for (const auto &kv : env) {
2016-01-27 15:43:34 +00:00
std::string name = quote(kv.first);
std::string value = quote(kv.second);
fprintf(stdout, "Set %s%s %s\n",
bool(kv.second) ? "-e " : "",
name.c_str(), value.c_str());
}
return 0;
}
if (tokens.size() == 2) {
std::string name = tokens[1];
2016-02-02 01:38:29 +00:00
auto iter = env.find(name);
if (iter == env.end()) {
2016-01-27 15:43:34 +00:00
fprintf(stderr, "### Set - No variable definition exists for %s.\n", name.c_str());
return 2;
}
name = quote(name);
std::string value = quote(iter->second);
fprintf(stdout, "Set %s%s %s\n",
bool(iter->second) ? "-e " : "",
name.c_str(), value.c_str());
return 0;
}
bool exported = false;
if (tokens.size() == 4 && tokens[1] == "-e") {
exported = true;
}
if (tokens.size() > 3 && !exported) {
fputs("### Set - Too many parameters were specified.\n", stderr);
fputs("# Usage - set [name [value]]\n", stderr);
return 1;
}
std::string name = tokens[1+exported];
std::string value = tokens[2+exported];
2016-02-02 01:38:29 +00:00
env.set(name, value, exported);
2016-01-27 15:43:34 +00:00
return 0;
}
2016-02-02 01:38:29 +00:00
static int export_common(Environment &env, bool export_or_unexport, const std::vector<std::string> &tokens, const fdmask &fds) {
2016-01-27 15:43:34 +00:00
const char *name = export_or_unexport ? "Export" : "Unexport";
struct {
int _r = 0;
int _s = 0;
} flags;
bool error = false;
std::vector<std::string> argv = getopt(tokens, [&](char c){
switch(c) {
case 'r':
case 'R':
flags._r = true;
break;
case 's':
case 'S':
flags._s = true;
break;
default:
fprintf(stderr, "### %s - \"-%c\" is not an option.\n", name, c);
error = true;
break;
}
});
if (error) {
fprintf(stderr, "# Usage - %s [-r | -s | name...]\n", name);
return 1;
}
if (argv.empty()) {
if (flags._r && flags._s) goto conflict;
// list of exported vars.
// -r will generate unexport commands for exported variables.
// -s will only print the names.
name = export_or_unexport ? "Export " : "Unexport ";
2016-02-02 01:38:29 +00:00
for (const auto &kv : env) {
2016-01-27 15:43:34 +00:00
const std::string& vname = kv.first;
if (kv.second == export_or_unexport)
fprintf(stdout, "%s%s\n", flags._s ? "" : name, quote(vname).c_str());
}
return 0;
}
else {
// mark as exported.
if (flags._r || flags._s) goto conflict;
for (std::string s : argv) {
2016-02-02 01:38:29 +00:00
auto iter = env.find(s);
if (iter != env.end()) iter->second = export_or_unexport;
2016-01-27 15:43:34 +00:00
}
return 0;
}
conflict:
fprintf(stderr, "### %s - Conflicting options or parameters were specified.\n", name);
fprintf(stderr, "# Usage - %s [-r | -s | name...]\n", name);
return 1;
}
2016-02-02 01:38:29 +00:00
int builtin_export(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
return export_common(env, true, tokens, fds);
2016-01-27 15:43:34 +00:00
}
2016-02-02 01:38:29 +00:00
int builtin_unexport(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
2016-01-27 15:43:34 +00:00
2016-02-02 01:38:29 +00:00
return export_common(env, false, tokens, fds);
2016-01-27 15:43:34 +00:00
}
2016-02-02 01:38:29 +00:00
int builtin_echo(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
2016-01-27 15:43:34 +00:00
//io_helper io(fds);
2016-01-27 15:43:34 +00:00
bool space = false;
bool n = false;
for (auto iter = tokens.begin() + 1; iter != tokens.end(); ++iter) {
const std::string &s = *iter;
if (s == "-n" || s == "-N") {
n = true;
continue;
}
if (space) {
fputs(" ", stdout);
}
fputs(s.c_str(), stdout);
space = true;
}
if (!n) fputs("\n", stdout);
return 0;
}
2016-02-02 01:38:29 +00:00
int builtin_quote(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
2016-01-27 15:43:34 +00:00
// todo...
//io_helper io(fds);
2016-01-27 15:43:34 +00:00
bool space = false;
bool n = false;
for (auto iter = tokens.begin() + 1; iter != tokens.end(); ++iter) {
std::string s = *iter;
if (s == "-n" || s == "-N") {
n = true;
continue;
}
if (space) {
fputs(" ", stdout);
}
s = quote(std::move(s));
fputs(s.c_str(), stdout);
space = true;
}
if (!n) fputs("\n", stdout);
return 0;
}
2016-02-02 01:38:29 +00:00
int builtin_parameters(Environment &env, const std::vector<std::string> &argv, const fdmask &fds) {
2016-01-27 15:43:34 +00:00
//io_helper io(fds);
2016-01-27 15:43:34 +00:00
int i = 0;
for (const auto &s : argv) {
fprintf(stdout, "{%d} %s\n", i++, s.c_str());
}
return 0;
}
2016-02-02 01:38:29 +00:00
int builtin_directory(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
2016-01-27 15:43:34 +00:00
// directory [-q]
// directory path
// for relative names, uses {DirectoryPath} (if set) rather than .
// set DirectoryPath ":,{MPW},{MPW}Projects:"
//io_helper io(fds);
2016-01-27 15:43:34 +00:00
bool q = false;
bool error = false;
std::vector<std::string> argv = getopt(tokens, [&](char c){
switch(c)
{
case 'q':
case 'Q':
q = true;
break;
default:
fprintf(stderr, "### Directory - \"-%c\" is not an option.\n", c);
error = true;
break;
}
});
if (error) {
fputs("# Usage - Directory [-q | directory]\n", stderr);
return 1;
}
if (argv.size() > 1) {
fputs("### Directory - Too many parameters were specified.\n", stderr);
fputs("# Usage - Directory [-q | directory]\n", stderr);
return 1;
}
if (argv.size() == 1) {
//cd
if (q) {
fputs("### Directory - Conflicting options or parameters were specified.\n", stderr);
return 1;
}
2016-02-02 01:38:29 +00:00
// 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;
}
2016-01-27 15:43:34 +00:00
}
else {
// pwd
2016-02-02 01:38:29 +00:00
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());
2016-01-27 15:43:34 +00:00
}
2016-02-02 01:38:29 +00:00
return 0;
2016-01-27 15:43:34 +00:00
}
static bool is_assignment(int type) {
switch(type)
{
case '=':
case '+=':
case '-=':
return true;
default:
return false;
}
}
2016-02-02 01:38:29 +00:00
int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask &fds) {
2016-01-27 15:43:34 +00:00
// evaluate expression
// evaluate variable = expression
// evaluate variable += expression
// evaluate variable -= expression
// flags -- -h -o -b -- print in hex, octal, or binary
// convert the arguments to a stack.
int output = 'd';
//io_helper io(fds);
2016-01-27 15:43:34 +00:00
std::reverse(tokens.begin(), tokens.end());
// remove 'Evaluate'
tokens.pop_back();
// check for -h -x -o
if (tokens.size() >= 2 && tokens.back().type == '-') {
const token &t = tokens[tokens.size() - 2];
if (t.type == token::text && t.string.length() == 1) {
int flag = tolower(t.string[0]);
switch(flag) {
case 'o':
case 'h':
case 'b':
output = flag;
tokens.pop_back();
tokens.pop_back();
}
}
}
if (tokens.size() >= 2 && tokens.back().type == token::text)
{
int type = tokens[tokens.size() -2].type;
if (is_assignment(type)) {
std::string name = tokens.back().string;
tokens.pop_back();
tokens.pop_back();
int32_t i = evaluate_expression("Evaluate", std::move(tokens));
switch(type) {
case '=':
2016-02-02 01:38:29 +00:00
env.set(name, std::to_string(i));
2016-01-27 15:43:34 +00:00
break;
case '+=':
case '-=':
{
value old;
2016-02-02 01:38:29 +00:00
auto iter = env.find(name);
if (iter != env.end()) old = (const std::string &)iter->second;
2016-01-27 15:43:34 +00:00
switch(type) {
case '+=':
i = old.to_number() + i;
break;
case '-=':
i = old.to_number() - i;
break;
}
std::string s = std::to_string(i);
2016-02-02 01:38:29 +00:00
env.set(name, s);
2016-01-27 15:43:34 +00:00
}
break;
}
return 0;
}
}
int32_t i = evaluate_expression("Evaluate", std::move(tokens));
// todo -- format based on -h, -o, or -b flag.
if (output == 'h') {
fprintf(stdout, "0x%08x\n", i);
return 0;
}
if (output == 'b') {
std::string tmp("0b");
2016-01-27 15:43:34 +00:00
for (int j = 0; j < 32; ++j) {
tmp.push_back(i & 0x80000000 ? '1' : '0');
i <<= 1;
}
tmp.push_back('\n');
fputs(tmp.c_str(), stdout);
return 0;
2016-01-27 15:43:34 +00:00
}
if (output == 'o') {
// octal.
fprintf(stdout, "0%o\n", i);
return 0;
}
fprintf(stdout, "%d\n", i);
return 0;
}