mpw-shell/mpw-shell.cpp

282 lines
4.9 KiB
C++
Raw Normal View History

2016-01-27 15:43:34 +00:00
#include <vector>
#include <string>
#include <unordered_map>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
2016-01-30 17:45:50 +00:00
#include <cerrno>
2016-02-05 18:19:20 +00:00
#include <signal.h>
2016-01-27 15:43:34 +00:00
#include "mpw-shell.h"
2016-02-02 01:38:29 +00:00
#include "fdset.h"
2016-01-27 15:43:34 +00:00
2016-01-30 17:45:50 +00:00
#include "phase1.h"
#include "phase2.h"
#include "command.h"
2016-01-27 15:43:34 +00:00
2016-02-02 03:32:21 +00:00
#include "mapped_file.h"
#include "error.h"
2016-02-02 03:32:21 +00:00
2016-02-02 02:17:46 +00:00
//#include <histedit.h>
#include <editline/readline.h>
2016-01-27 15:43:34 +00:00
2016-02-03 02:57:53 +00:00
#include <sys/types.h>
#include <pwd.h>
#include <sysexits.h>
//#include <uuid/uuid.h>
std::string root() {
static std::string root;
if (root.empty()) {
const char *cp = getenv("HOME");
if (!cp || !*cp) {
auto pw = getpwuid(getuid());
if (!pw) {
fprintf(stderr,"Unable to determine home directory\n.");
exit(EX_NOUSER);
}
cp = pw->pw_dir;
}
root = cp;
if (root.back() != '/') root.push_back('/');
root += "mpw/";
}
return root;
}
2016-01-27 15:43:34 +00:00
// should set {MPW}, {MPWVersion}, then execute {MPW}StartUp
2016-02-03 02:57:53 +00:00
void init(Environment &env) {
env.set("mpw", root());
env.set("status", std::string("0"));
env.set("exit", std::string("1")); // terminate script on error.
env.set("echo", std::string("1"));
2016-01-27 15:43:34 +00:00
}
2016-02-05 18:19:20 +00:00
2016-02-02 03:32:21 +00:00
int read_file(phase1 &p, const std::string &file) {
const mapped_file mf(file, mapped_file::readonly);
2016-02-02 03:32:21 +00:00
2016-02-03 20:08:10 +00:00
p.process(mf.begin(), mf.end(), false);
p.finish();
2016-02-02 03:32:21 +00:00
return 0;
}
int read_fd(phase1 &p, int fd) {
2016-01-30 17:45:50 +00:00
unsigned char buffer[2048];
ssize_t size;
for (;;) {
size = read(fd, buffer, sizeof(buffer));
2016-01-30 17:45:50 +00:00
if (size == 0) break;
if (size < 0) {
if (errno == EINTR) continue;
perror("read: ");
return -1;
}
try {
p.process(buffer, buffer + size);
} catch(std::exception &ex) {
fprintf(stderr, "%s\n", ex.what());
p.reset();
}
}
try {
p.finish();
} catch(std::exception &ex) {
fprintf(stderr, "%s\n", ex.what());
p.reset();
}
return 0;
}
2016-02-05 18:19:20 +00:00
volatile int control_c = 0;
void control_c_handler(int signal, siginfo_t *sinfo, void *context) {
// libedit gobbles up the first control-C and doesn't return until the second.
// GNU's readline may return no the first.
if (control_c > 3) abort();
control_c++;
//fprintf(stderr, "interrupt!\n");
}
2016-02-05 18:47:34 +00:00
int interactive(Environment &env, phase1 &p, phase2& p2) {
2016-02-02 02:17:46 +00:00
2016-02-03 03:59:18 +00:00
std::string history_file = root();
history_file += ".history";
read_history(history_file.c_str());
2016-02-05 18:19:20 +00:00
struct sigaction act;
struct sigaction old_act;
memset(&act, 0, sizeof(struct sigaction));
sigemptyset(&act.sa_mask);
act.sa_sigaction = control_c_handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &act, &old_act);
2016-02-02 02:17:46 +00:00
for(;;) {
2016-02-05 02:57:17 +00:00
const char *prompt = "# ";
if (p2.continuation()) prompt = "> ";
char *cp = readline(prompt);
2016-02-05 18:19:20 +00:00
if (!cp) {
if (control_c) {
control_c = 0;
fprintf(stdout, "\n");
p.abort();
p2.abort();
2016-02-05 18:47:34 +00:00
env.status(-9, false);
2016-02-05 18:19:20 +00:00
continue;
}
break;
}
control_c = 0;
2016-02-02 02:17:46 +00:00
std::string s(cp);
free(cp);
if (s.empty()) continue;
// don't add if same as previous entry.
2016-02-03 03:59:18 +00:00
HIST_ENTRY *he = history_get(history_length);
if (he == nullptr || s != he->line)
add_history(s.c_str());
2016-02-02 02:17:46 +00:00
s.push_back('\n');
try {
p.process(s);
} catch(std::exception &ex) {
fprintf(stderr, "%s\n", ex.what());
p.reset();
}
}
try {
p.finish();
} catch(std::exception &ex) {
fprintf(stderr, "%s\n", ex.what());
p.reset();
}
2016-02-05 18:19:20 +00:00
sigaction(SIGINT, &old_act, nullptr);
2016-02-03 03:59:18 +00:00
write_history(history_file.c_str());
2016-02-02 02:17:46 +00:00
fprintf(stdout, "\n");
2016-02-03 03:59:18 +00:00
2016-02-05 18:19:20 +00:00
2016-02-02 02:17:46 +00:00
return 0;
}
2016-02-03 03:15:31 +00:00
void help() {
}
void define(Environment &env, const std::string &s) {
auto pos = s.find('=');
if (pos == s.npos) env.set(s, "1");
else {
std::string k = s.substr(0, pos);
std::string v = s.substr(pos+1);
env.set(k, v);
}
}
2016-01-27 15:43:34 +00:00
int main(int argc, char **argv) {
2016-02-02 01:38:29 +00:00
Environment e;
init(e);
2016-01-27 15:43:34 +00:00
2016-02-03 03:39:06 +00:00
const char *cflag = nullptr;
2016-02-03 03:15:31 +00:00
int c;
2016-02-03 03:39:06 +00:00
while ((c = getopt(argc, argv, "c:D:v:h")) != -1) {
2016-02-03 03:15:31 +00:00
switch (c) {
2016-02-03 03:39:06 +00:00
case 'c':
// -c command
cflag = optarg;
break;
2016-02-03 03:15:31 +00:00
case 'D':
// -Dname or -Dname=value
define(e, optarg);
break;
case 'v':
// -v verbose
e.set("echo", "1");
break;
case 'h':
help();
exit(0);
default:
help();
exit(EX_USAGE);
}
}
2016-01-30 17:45:50 +00:00
phase1 p1;
phase2 p2;
2016-01-31 05:41:30 +00:00
2016-01-30 17:45:50 +00:00
p1 >>= [&p2](std::string &&s) {
if (s.empty()) p2.finish();
else p2(std::move(s));
};
2016-01-30 17:45:50 +00:00
p2 >>= [&e](command_ptr_vector &&v) {
for (auto iter = v.begin(); iter != v.end(); ++iter) {
auto &ptr = *iter;
fdmask fds;
try {
ptr->execute(e, fds);
} catch (execution_of_input_terminated &ex) {
2016-02-05 18:19:20 +00:00
control_c = 0;
if (!(ptr->terminal() && ++iter == v.end())) {
fprintf(stderr, "%s\n", ex.what());
}
2016-02-05 18:19:20 +00:00
e.status(ex.status(), false);
return;
}
}
2016-01-30 17:45:50 +00:00
};
2016-02-03 03:39:06 +00:00
if (!cflag) fprintf(stdout, "MPW Shell 0.0\n");
e.startup(true);
2016-02-02 03:32:21 +00:00
read_file(p1, "/Users/kelvin/mpw/Startup");
//p2.finish();
e.startup(false);
2016-02-02 03:32:21 +00:00
2016-02-03 03:39:06 +00:00
if (cflag) {
std::string s(cflag);
s.push_back('\n');
2016-02-03 20:08:10 +00:00
p1.process(s, true);
2016-02-03 03:39:06 +00:00
p2.finish();
exit(e.status());
}
if (isatty(STDIN_FILENO))
2016-02-05 18:47:34 +00:00
interactive(e, p1, p2);
else
read_fd(p1, STDIN_FILENO);
2016-01-31 05:41:30 +00:00
p2.finish();
2016-01-30 17:45:50 +00:00
2016-02-03 03:39:06 +00:00
exit(e.status());
2016-01-27 15:43:34 +00:00
}