mirror of
https://github.com/ksherlock/mpw-shell.git
synced 2025-07-19 20:24:11 +00:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
fa804e91f0 | ||
|
0ec15bcd63 | ||
|
06e2e1f309 | ||
|
32c72cb89a | ||
|
f125b533f7 | ||
|
f6c5478063 | ||
|
efd51bcb48 | ||
|
33dd3228f8 | ||
|
548880a517 | ||
|
1da6c3e9da | ||
|
bdba86249e | ||
|
87f5398649 | ||
|
811c8b976a | ||
|
0422976719 | ||
|
2893f7fe79 | ||
|
34a4f431c0 | ||
|
f3db9b7cc0 |
@@ -1,25 +1,50 @@
|
|||||||
|
|
||||||
# CMAKE_INSTALL_PREFIX defaults to /usr/local.
|
# CMAKE_INSTALL_PREFIX defaults to /usr/local.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.6)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
project("mpw-shell")
|
project("mpw-shell")
|
||||||
set (PROJECT_TYPE "CXX")
|
set (PROJECT_TYPE "CXX")
|
||||||
set (PROJECT_NAME "MPW Shell")
|
set (PROJECT_NAME "MPW Shell")
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "-g -Wall -Wno-unused-variable -Wno-multichar -O1")
|
|
||||||
|
# -std=c++14
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS FALSE)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# build config.h
|
||||||
|
#
|
||||||
|
|
||||||
|
INCLUDE (CheckFunctionExists)
|
||||||
|
INCLUDE (CheckLibraryExists)
|
||||||
|
|
||||||
|
SET(CMAKE_EXTRA_INCLUDE_FILES stdio.h)
|
||||||
|
CHECK_FUNCTION_EXISTS(dprintf HAVE_DPRINTF)
|
||||||
|
SET(CMAKE_EXTRA_INCLUDE_FILES)
|
||||||
|
|
||||||
|
CHECK_LIBRARY_EXISTS(edit readline "" HAVE_LIBEDIT)
|
||||||
|
CHECK_LIBRARY_EXISTS(readline readline "" HAVE_LIBREADLINE)
|
||||||
|
CHECK_LIBRARY_EXISTS(history add_history "" HAVE_LIBHISTORY)
|
||||||
|
|
||||||
|
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
|
|
||||||
|
add_compile_options(-g -Wall -Wno-unused-variable -Wno-multichar -O1)
|
||||||
|
|
||||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-const-variable ")
|
add_compile_options(-Wno-unused-const-variable)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-but-set-variable")
|
add_compile_options(-Wno-unused-but-set-variable)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR})
|
||||||
add_definitions(-I ${CMAKE_SOURCE_DIR}/ -I ${CMAKE_CURRENT_BINARY_DIR}/ )
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
# from https://github.com/gsauthof/cmake-ragel
|
# from https://github.com/gsauthof/cmake-ragel
|
||||||
macro(RAGEL_TARGET Name Input Output)
|
macro(RAGEL_TARGET Name Input Output)
|
||||||
@@ -65,17 +90,17 @@ RAGEL_TARGET(value value.rl value.cpp COMPILE_FLAGS "-p -G2")
|
|||||||
|
|
||||||
# need to copy all OUTPUT file to the build dir
|
# need to copy all OUTPUT file to the build dir
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT phase2-parser.cpp phase2-parser.h phase2-parser.out
|
OUTPUT phase3.cpp phase3.h phase3.out
|
||||||
COMMAND lemon++ phase2-parser.lemon
|
COMMAND lemon++ phase3.lemon
|
||||||
COMMAND mv -f phase2-parser.cpp phase2-parser.h phase2-parser.out ${CMAKE_CURRENT_BINARY_DIR}/
|
COMMAND mv -f phase3.cpp phase3.h phase3.out ${CMAKE_CURRENT_BINARY_DIR}/
|
||||||
MAIN_DEPENDENCY phase2-parser.lemon
|
MAIN_DEPENDENCY phase3.lemon
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
|
add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
|
||||||
mpw-shell-parser.cpp value.cpp mpw-shell-quote.cpp
|
mpw-shell-parser.cpp mpw_parser.cpp value.cpp mpw-shell-quote.cpp
|
||||||
phase1.cpp phase2.cpp phase2-parser.cpp command.cpp environment.cpp builtins.cpp
|
phase1.cpp phase2.cpp phase3.cpp command.cpp environment.cpp builtins.cpp
|
||||||
pathnames.cpp
|
pathnames.cpp
|
||||||
macroman.cpp
|
macroman.cpp
|
||||||
cxx/mapped_file.cpp
|
cxx/mapped_file.cpp
|
||||||
@@ -84,12 +109,19 @@ add_executable(mpw-shell mpw-shell.cpp mpw-shell-token.cpp mpw-shell-expand.cpp
|
|||||||
cxx/directory_iterator.cpp
|
cxx/directory_iterator.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# -ledit includes history stuff. gnu -lreadline does not.
|
||||||
|
#
|
||||||
|
if(HAVE_LIBEDIT)
|
||||||
target_link_libraries(mpw-shell edit)
|
target_link_libraries(mpw-shell edit)
|
||||||
|
elseif(HAVE_LIBREADLINE)
|
||||||
|
target_link_libraries(mpw-shell readline)
|
||||||
|
if (HAVE_LIBHISTORY)
|
||||||
|
target_link_libraries(mpw-shell history)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# all this for -std=c++14
|
|
||||||
set_property (TARGET mpw-shell PROPERTY CXX_STANDARD 14)
|
|
||||||
set_property (TARGET mpw-shell PROPERTY CXX_STANDARD_REQUIRED TRUE)
|
|
||||||
set_property (TARGET mpw-shell PROPERTY CXX_EXTENSIONS FALSE)
|
|
||||||
|
|
||||||
# create a symlink for mpw-make
|
# create a symlink for mpw-make
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
22
README.md
22
README.md
@@ -11,16 +11,17 @@ Supported features
|
|||||||
* Begin ... End
|
* Begin ... End
|
||||||
* Loop ... End
|
* Loop ... End
|
||||||
* For name In [word...] ... End
|
* For name In [word...] ... End
|
||||||
* Break [If], Continue [If]
|
* Break [If], Continue [If], Exit [If]
|
||||||
* ( ... )
|
* ( ... )
|
||||||
* ||
|
* ||
|
||||||
* &&
|
* &&
|
||||||
* redirection
|
* Redirection
|
||||||
* | "pipes" (via a temporary file. Presumably, that's what MPW did as well.)
|
* | "pipes" (via a temporary file. Presumably, that's what MPW did as well.)
|
||||||
|
* Subshells (`...`, ``...``)
|
||||||
|
|
||||||
|
|
||||||
Not (yet) supported
|
Not (yet) supported
|
||||||
-------------
|
-------------
|
||||||
* subshells (`...`, ``...``)
|
|
||||||
* aliases
|
* aliases
|
||||||
* regular expressions
|
* regular expressions
|
||||||
* text-editing commands (search forward/backward, et cetera)
|
* text-editing commands (search forward/backward, et cetera)
|
||||||
@@ -33,9 +34,11 @@ Builtin Commands
|
|||||||
* Directory
|
* Directory
|
||||||
* Echo
|
* Echo
|
||||||
* Evaluate
|
* Evaluate
|
||||||
|
* Execute
|
||||||
* Exists
|
* Exists
|
||||||
* Export
|
* Export
|
||||||
* Parameters
|
* Parameters
|
||||||
|
* Quit
|
||||||
* Quote
|
* Quote
|
||||||
* Set
|
* Set
|
||||||
* Shift
|
* Shift
|
||||||
@@ -49,7 +52,18 @@ Builtin Commands
|
|||||||
Setup
|
Setup
|
||||||
-----
|
-----
|
||||||
1. Install MPW. The mpw binary should be somewhere in your `$PATH`.
|
1. Install MPW. The mpw binary should be somewhere in your `$PATH`.
|
||||||
It also checks `/usr/local/bin/mpw` and `$HOME/mpw/bin/mpw`
|
It also checks `/usr/local/bin/mpw` and `$HOME/mpw/bin/mpw`. You can
|
||||||
|
use mpw-shell without it but only with builtin commands.
|
||||||
2. Copy the `Startup` script to `$HOME/mpw/`. This script is executed
|
2. Copy the `Startup` script to `$HOME/mpw/`. This script is executed
|
||||||
when mpw-shell (or mpw-make) starts up (imagine that) and should
|
when mpw-shell (or mpw-make) starts up (imagine that) and should
|
||||||
be used to set environment variables.
|
be used to set environment variables.
|
||||||
|
|
||||||
|
|
||||||
|
Command Line Flags
|
||||||
|
------------------
|
||||||
|
|
||||||
|
-D name=value Define environment variable
|
||||||
|
-v Be verbose (equivalent to -Decho=1)
|
||||||
|
-f Ignore the Startup script
|
||||||
|
-c string Execute string
|
||||||
|
-h Display help
|
||||||
|
155
builtins.cpp
155
builtins.cpp
@@ -4,6 +4,7 @@
|
|||||||
#include "fdset.h"
|
#include "fdset.h"
|
||||||
#include "value.h"
|
#include "value.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
namespace ToolBox {
|
namespace ToolBox {
|
||||||
std::string MacToUnix(const std::string path);
|
std::string MacToUnix(const std::string path);
|
||||||
@@ -114,16 +116,23 @@ namespace {
|
|||||||
#define fputc DO_NOT_USE_FPUTC
|
#define fputc DO_NOT_USE_FPUTC
|
||||||
|
|
||||||
inline int fdputs(const char *data, int fd) {
|
inline int fdputs(const char *data, int fd) {
|
||||||
auto rv = write(fd, data, strlen(data)); return rv < 0 ? EOF : rv;
|
auto rv = write(fd, data, strlen(data));
|
||||||
|
return rv < 0 ? EOF : rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int fdputs(const std::string &s, int fd) {
|
||||||
|
auto rv = write(fd, s.data(), s.size());
|
||||||
|
return rv < 0 ? EOF : rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int fdputc(int c, int fd) {
|
inline int fdputc(int c, int fd) {
|
||||||
unsigned char tmp = c;
|
unsigned char tmp = c;
|
||||||
auto rv = write(fd, &tmp, 1); return rv < 0 ? EOF : c;
|
auto rv = write(fd, &tmp, 1);
|
||||||
|
return rv < 0 ? EOF : c;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DPRINTF
|
#ifdef HAVE_DPRINTF
|
||||||
#define fdprintf(...) dprintf(__VA_ARGS__)
|
#define fdprintf dprintf
|
||||||
#else
|
#else
|
||||||
inline int fdprintf(int fd, const char *format, ...) {
|
inline int fdprintf(int fd, const char *format, ...) {
|
||||||
char *cp = nullptr;
|
char *cp = nullptr;
|
||||||
@@ -271,22 +280,14 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
|
|||||||
|
|
||||||
const char *name = export_or_unexport ? "Export" : "Unexport";
|
const char *name = export_or_unexport ? "Export" : "Unexport";
|
||||||
|
|
||||||
struct {
|
bool _r = false;
|
||||||
int _r = 0;
|
bool _s = false;
|
||||||
int _s = 0;
|
|
||||||
} flags;
|
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
auto argv = getopt(tokens, [&](char c){
|
||||||
switch(c) {
|
switch(tolower(c)) {
|
||||||
case 'r':
|
case 'r': _r = true; break;
|
||||||
case 'R':
|
case 's': _s = true; break;
|
||||||
flags._r = true;
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
case 'S':
|
|
||||||
flags._s = true;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
fdprintf(stderr, "### %s - \"-%c\" is not an option.\n", name, c);
|
fdprintf(stderr, "### %s - \"-%c\" is not an option.\n", name, c);
|
||||||
error = true;
|
error = true;
|
||||||
@@ -300,7 +301,7 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (argv.empty()) {
|
if (argv.empty()) {
|
||||||
if (flags._r && flags._s) goto conflict;
|
if (_r && _s) goto conflict;
|
||||||
|
|
||||||
// list of exported vars.
|
// list of exported vars.
|
||||||
// -r will generate unexport commands for exported variables.
|
// -r will generate unexport commands for exported variables.
|
||||||
@@ -312,14 +313,14 @@ static int export_common(Environment &env, bool export_or_unexport, const std::v
|
|||||||
for (const auto &kv : env) {
|
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)
|
||||||
fdprintf(stdout, "%s%s\n", flags._s ? "" : name, quote(vname).c_str());
|
fdprintf(stdout, "%s%s\n", _s ? "" : name, quote(vname).c_str());
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// mark as exported.
|
// mark as exported.
|
||||||
|
|
||||||
if (flags._r || flags._s) goto conflict;
|
if (_r || _s) goto conflict;
|
||||||
|
|
||||||
for (std::string s : argv) {
|
for (std::string s : argv) {
|
||||||
auto iter = env.find(s);
|
auto iter = env.find(s);
|
||||||
@@ -501,12 +502,9 @@ int builtin_directory(Environment &env, const std::vector<std::string> &tokens,
|
|||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
||||||
switch(c)
|
switch(tolower(c))
|
||||||
{
|
{
|
||||||
case 'q':
|
case 'q': q = true; break;
|
||||||
case 'Q':
|
|
||||||
q = true;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
fdprintf(stderr, "### Directory - \"-%c\" is not an option.\n", c);
|
fdprintf(stderr, "### Directory - \"-%c\" is not an option.\n", c);
|
||||||
error = true;
|
error = true;
|
||||||
@@ -577,24 +575,12 @@ int builtin_exists(Environment &env, const std::vector<std::string> &tokens, con
|
|||||||
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
||||||
switch(tolower(c))
|
switch(tolower(c))
|
||||||
{
|
{
|
||||||
case 'a':
|
case 'a': _a = true; break;
|
||||||
_a = true;
|
case 'd': _d = true; break;
|
||||||
break;
|
case 'f': _f = true; break;
|
||||||
case 'd':
|
case 'n': _n = true; break;
|
||||||
_d = true;
|
case 'q': _q = true; break;
|
||||||
break;
|
case 'w': _w = true; break;
|
||||||
case 'f':
|
|
||||||
_f = true;
|
|
||||||
break;
|
|
||||||
case 'n':
|
|
||||||
_n = true;
|
|
||||||
break;
|
|
||||||
case 'q':
|
|
||||||
_q = true;
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
_w = true;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
fdprintf(stderr, "### Exists - \"-%c\" is not an option.\n", c);
|
fdprintf(stderr, "### Exists - \"-%c\" is not an option.\n", c);
|
||||||
error = true;
|
error = true;
|
||||||
@@ -765,15 +751,15 @@ int builtin_evaluate(Environment &env, std::vector<token> &&tokens, const fdmask
|
|||||||
int builtin_which(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
|
int builtin_which(Environment &env, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||||
|
|
||||||
// which [-a] [-p] [command]
|
// which [-a] [-p] [command]
|
||||||
bool a = false;
|
bool _a = false;
|
||||||
bool p = false;
|
bool _p = false;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
std::vector<std::string> argv = getopt(tokens, [&](char c){
|
||||||
switch(c)
|
switch(tolower(c))
|
||||||
{
|
{
|
||||||
case 'a': case 'A': a = true; break;
|
case 'a': _a = true; break;
|
||||||
case 'p': case 'P': p = true; break;
|
case 'p': _p = true; break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fdprintf(stderr, "### Which - \"-%c\" is not an option.\n", c);
|
fdprintf(stderr, "### Which - \"-%c\" is not an option.\n", c);
|
||||||
@@ -824,7 +810,7 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(; ss; ++ss) {
|
for(; ss; ++ss) {
|
||||||
if (p) fdprintf(stderr, "checking %s\n", ss->c_str());
|
if (_p) fdprintf(stderr, "checking %s\n", ss->c_str());
|
||||||
|
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
fs::path p(ToolBox::MacToUnix(ss->c_str()));
|
fs::path p(ToolBox::MacToUnix(ss->c_str()));
|
||||||
@@ -833,13 +819,13 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
|
|||||||
if (fs::exists(p, ec)) {
|
if (fs::exists(p, ec)) {
|
||||||
found = true;
|
found = true;
|
||||||
fdprintf(stdout, "%s\n", quote(p).c_str());
|
fdprintf(stdout, "%s\n", quote(p).c_str());
|
||||||
if (!a) break;
|
if (!_a) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check builtins...
|
// check builtins...
|
||||||
if (!found || a) {
|
if (!found || _a) {
|
||||||
|
|
||||||
static const char *builtins[] = {
|
static const char *builtins[] = {
|
||||||
"aboutbox",
|
"aboutbox",
|
||||||
@@ -847,12 +833,15 @@ int builtin_which(Environment &env, const std::vector<std::string> &tokens, cons
|
|||||||
"catenate",
|
"catenate",
|
||||||
"directory",
|
"directory",
|
||||||
"echo",
|
"echo",
|
||||||
|
"execute",
|
||||||
"exists",
|
"exists",
|
||||||
"export",
|
"export",
|
||||||
|
"false", // not in MPW
|
||||||
"parameters",
|
"parameters",
|
||||||
"quote",
|
"quote",
|
||||||
"set",
|
"set",
|
||||||
"shift",
|
"shift",
|
||||||
|
"true", // not in MPW
|
||||||
"unalias",
|
"unalias",
|
||||||
"unexport",
|
"unexport",
|
||||||
"unset",
|
"unset",
|
||||||
@@ -940,9 +929,7 @@ int builtin_version(Environment &env, const std::vector<std::string> &tokens, co
|
|||||||
auto argv = getopt(tokens, [&](char c){
|
auto argv = getopt(tokens, [&](char c){
|
||||||
switch(tolower(c))
|
switch(tolower(c))
|
||||||
{
|
{
|
||||||
case 'v':
|
case 'v': _v = true; break;
|
||||||
_v = true;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
fdprintf(stderr, "### Version - \"-%c\" is not an option.\n", c);
|
fdprintf(stderr, "### Version - \"-%c\" is not an option.\n", c);
|
||||||
error = true;
|
error = true;
|
||||||
@@ -1026,3 +1013,63 @@ int builtin_true(Environment &, const std::vector<std::string> &, const fdmask &
|
|||||||
int builtin_false(Environment &, const std::vector<std::string> &, const fdmask &) {
|
int builtin_false(Environment &, const std::vector<std::string> &, const fdmask &) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int builtin_quit(Environment &, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||||
|
|
||||||
|
bool error = false;
|
||||||
|
bool _y = false;
|
||||||
|
bool _n = false;
|
||||||
|
bool _c = false;
|
||||||
|
|
||||||
|
auto argv = getopt(tokens, [&](char c){
|
||||||
|
switch(tolower(c))
|
||||||
|
{
|
||||||
|
case 'c': _c = true; break;
|
||||||
|
case 'n': _n = true; break;
|
||||||
|
case 'y': _y = true; break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fdprintf(stderr, "### Quit - \"-%c\" is not an option.\n", c);
|
||||||
|
error = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (_y + _n + _c > 1) {
|
||||||
|
fdprintf(stderr, "### Quit - Conflicting options were specified.\n");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.size() > 0) {
|
||||||
|
fdprintf(stderr, "### Quit - Too many parameters were specified.\n");
|
||||||
|
error = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
fdprintf(stderr, "# Usage - Quit [-y | -n | -c]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw quit_command_t{};
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int builtin_execute(Environment &e, const std::vector<std::string> &tokens, const fdmask &fds) {
|
||||||
|
|
||||||
|
// runs argv[1] in the current environment. unlike MPW, argv[1] must be a script.
|
||||||
|
// special enhancement -- '-' means execute stdin.
|
||||||
|
|
||||||
|
if (tokens.size() < 2) return 0;
|
||||||
|
std::string filename = tokens[1];
|
||||||
|
if (filename == "-") {
|
||||||
|
// since we're parsing stdin, don't let any children read it. [???]
|
||||||
|
fdset new_fds;
|
||||||
|
int fd = open("/dev/null", O_RDONLY);
|
||||||
|
new_fds.set(0, fd);
|
||||||
|
|
||||||
|
return read_fd(e, fds[0], new_fds | fds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read_file(e, filename, fds);
|
||||||
|
}
|
||||||
|
@@ -25,6 +25,12 @@ int builtin_which(Environment &e, const std::vector<std::string> &, const fdmask
|
|||||||
int builtin_alias(Environment &e, const std::vector<std::string> &, const fdmask &);
|
int builtin_alias(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
int builtin_unalias(Environment &e, const std::vector<std::string> &, const fdmask &);
|
int builtin_unalias(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
|
|
||||||
|
int builtin_execute(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
|
int builtin_true(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
|
int builtin_false(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
|
int builtin_quit(Environment &e, const std::vector<std::string> &, const fdmask &);
|
||||||
|
|
||||||
|
|
||||||
int builtin_evaluate(Environment &e, std::vector<token> &&, const fdmask &);
|
int builtin_evaluate(Environment &e, std::vector<token> &&, const fdmask &);
|
||||||
|
|
||||||
#endif
|
#endif
|
337
command.cpp
337
command.cpp
@@ -1,10 +1,11 @@
|
|||||||
#include "command.h"
|
#include "command.h"
|
||||||
#include "phase2-parser.h"
|
#include "phase3.h"
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
#include "fdset.h"
|
#include "fdset.h"
|
||||||
#include "builtins.h"
|
#include "builtins.h"
|
||||||
#include "mpw-shell.h"
|
#include "mpw-shell.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
#include "cxx/filesystem.h"
|
#include "cxx/filesystem.h"
|
||||||
#include "cxx/string_splitter.h"
|
#include "cxx/string_splitter.h"
|
||||||
|
|
||||||
|
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@@ -40,8 +42,7 @@ typedef std::vector<token> token_vector;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct break_command_t {};
|
|
||||||
struct continue_command_t {};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* returns:
|
* returns:
|
||||||
@@ -56,6 +57,12 @@ namespace {
|
|||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bad_exit() {
|
||||||
|
fprintf(stderr, "### Exit - Missing if keyword.\n");
|
||||||
|
fprintf(stderr, "# Usage - Exit [Number] [if expression...]\n");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
|
||||||
int evaluate(int type, token_vector &&tokens, Environment &env) {
|
int evaluate(int type, token_vector &&tokens, Environment &env) {
|
||||||
std::reverse(tokens.begin(), tokens.end());
|
std::reverse(tokens.begin(), tokens.end());
|
||||||
|
|
||||||
@@ -64,6 +71,8 @@ namespace {
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
default: return 0;
|
default: return 0;
|
||||||
|
|
||||||
|
// exit [number] [if expr] ([number has been removed])
|
||||||
|
case EXIT:
|
||||||
case BREAK:
|
case BREAK:
|
||||||
case CONTINUE:
|
case CONTINUE:
|
||||||
case ELSE:
|
case ELSE:
|
||||||
@@ -77,10 +86,12 @@ namespace {
|
|||||||
case BREAK: name = "Break"; break;
|
case BREAK: name = "Break"; break;
|
||||||
case CONTINUE: name = "Continue"; break;
|
case CONTINUE: name = "Continue"; break;
|
||||||
case ELSE: name = "Else"; break;
|
case ELSE: name = "Else"; break;
|
||||||
|
case EXIT: name = "Exit"; return bad_exit(); break;
|
||||||
}
|
}
|
||||||
return bad_if(name);
|
return bad_if(name);
|
||||||
}
|
}
|
||||||
// fall through.
|
// fall through.
|
||||||
|
|
||||||
case IF:
|
case IF:
|
||||||
tokens.pop_back();
|
tokens.pop_back();
|
||||||
try {
|
try {
|
||||||
@@ -198,9 +209,11 @@ namespace {
|
|||||||
{"catenate", builtin_catenate},
|
{"catenate", builtin_catenate},
|
||||||
{"directory", builtin_directory},
|
{"directory", builtin_directory},
|
||||||
{"echo", builtin_echo},
|
{"echo", builtin_echo},
|
||||||
|
{"execute", builtin_execute},
|
||||||
{"exists", builtin_exists},
|
{"exists", builtin_exists},
|
||||||
{"export", builtin_export},
|
{"export", builtin_export},
|
||||||
{"parameters", builtin_parameters},
|
{"parameters", builtin_parameters},
|
||||||
|
{"quit", builtin_quit},
|
||||||
{"quote", builtin_quote},
|
{"quote", builtin_quote},
|
||||||
{"set", builtin_set},
|
{"set", builtin_set},
|
||||||
{"shift", builtin_shift},
|
{"shift", builtin_shift},
|
||||||
@@ -209,6 +222,10 @@ namespace {
|
|||||||
{"unset", builtin_unset},
|
{"unset", builtin_unset},
|
||||||
{"version", builtin_version},
|
{"version", builtin_version},
|
||||||
{"which", builtin_which},
|
{"which", builtin_which},
|
||||||
|
|
||||||
|
// not in MPW.
|
||||||
|
{"true", builtin_true},
|
||||||
|
{"false", builtin_false},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -278,9 +295,6 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//std::string expand_vars(const std::string &s, const class Environment &);
|
|
||||||
|
|
||||||
command::~command()
|
command::~command()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -293,7 +307,7 @@ command::~command()
|
|||||||
|
|
||||||
|
|
||||||
template<class F>
|
template<class F>
|
||||||
int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
int exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx) {
|
||||||
|
|
||||||
bool echo = true;
|
bool echo = true;
|
||||||
int rv = 0;
|
int rv = 0;
|
||||||
@@ -302,7 +316,7 @@ int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
process p;
|
process p;
|
||||||
command = expand_vars(command, env);
|
command = expand_vars(command, env, fds);
|
||||||
auto tokens = tokenize(command, false);
|
auto tokens = tokenize(command, false);
|
||||||
if (tokens.empty()) return 0;
|
if (tokens.empty()) return 0;
|
||||||
parse_tokens(std::move(tokens), p);
|
parse_tokens(std::move(tokens), p);
|
||||||
@@ -330,7 +344,7 @@ int exec(std::string command, Environment &env, bool throwup, F &&fx) {
|
|||||||
int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
|
|
||||||
return exec(text, env, throwup, [&](process &p){
|
return exec(text, env, fds, throwup, [&](process &p){
|
||||||
|
|
||||||
fdmask newfds = p.fds | fds;
|
fdmask newfds = p.fds | fds;
|
||||||
|
|
||||||
@@ -367,7 +381,7 @@ int simple_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class F>
|
template<class F>
|
||||||
int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
int eval_exec(std::string command, Environment &env, const fdmask &fds, bool throwup, F &&fx){
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
bool echo = true;
|
bool echo = true;
|
||||||
@@ -376,7 +390,7 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
|||||||
if (control_c) throw execution_of_input_terminated();
|
if (control_c) throw execution_of_input_terminated();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
command = expand_vars(command, env);
|
command = expand_vars(command, env, fds);
|
||||||
auto tokens = tokenize(command, true);
|
auto tokens = tokenize(command, true);
|
||||||
|
|
||||||
if (tokens.empty()) return 0;
|
if (tokens.empty()) return 0;
|
||||||
@@ -386,6 +400,10 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
|||||||
|
|
||||||
rv = fx(tokens);
|
rv = fx(tokens);
|
||||||
}
|
}
|
||||||
|
catch (const exit_command_t &ex) {
|
||||||
|
// convert to execution of input terminated.
|
||||||
|
throw execution_of_input_terminated(ex.value);
|
||||||
|
}
|
||||||
|
|
||||||
catch (mpw_error &e) {
|
catch (mpw_error &e) {
|
||||||
if (echo) env.echo("%s", command.c_str());
|
if (echo) env.echo("%s", command.c_str());
|
||||||
@@ -406,29 +424,8 @@ int eval_exec(std::string command, Environment &env, bool throwup, F &&fx){
|
|||||||
|
|
||||||
int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (control_c) throw execution_of_input_terminated();
|
|
||||||
|
|
||||||
std::string s = expand_vars(text, env);
|
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||||
|
|
||||||
env.set("command", "evaluate");
|
|
||||||
|
|
||||||
env.echo("%s", s.c_str());
|
|
||||||
|
|
||||||
try {
|
|
||||||
auto tokens = tokenize(s, true);
|
|
||||||
if (tokens.empty()) return 0;
|
|
||||||
|
|
||||||
int status = builtin_evaluate(env, std::move(tokens), fds);
|
|
||||||
|
|
||||||
return env.status(status, throwup);
|
|
||||||
} catch (std::exception &e) {
|
|
||||||
fprintf(stderr, "%s\n", e.what());
|
|
||||||
return env.status(1, throwup);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
|
||||||
env.set("command", "evaluate");
|
env.set("command", "evaluate");
|
||||||
return builtin_evaluate(env, std::move(tokens), fds);
|
return builtin_evaluate(env, std::move(tokens), fds);
|
||||||
});
|
});
|
||||||
@@ -439,7 +436,7 @@ int evaluate_command::execute(Environment &env, const fdmask &fds, bool throwup)
|
|||||||
|
|
||||||
int break_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int break_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||||
env.set("command", "break");
|
env.set("command", "break");
|
||||||
if (!env.loop()) throw break_error();
|
if (!env.loop()) throw break_error();
|
||||||
int status = evaluate(BREAK, std::move(tokens), env);
|
int status = evaluate(BREAK, std::move(tokens), env);
|
||||||
@@ -447,30 +444,11 @@ int break_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
return status;
|
return status;
|
||||||
});
|
});
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (control_c) throw execution_of_input_terminated();
|
|
||||||
|
|
||||||
std::string s = expand_vars(text, env);
|
|
||||||
|
|
||||||
env.set("command", "break");
|
|
||||||
|
|
||||||
env.echo("%s", s.c_str());
|
|
||||||
if (!env.loop()) {
|
|
||||||
fputs("### MPW Shell - Break must be within for or loop.\n", stderr);
|
|
||||||
return env.status(-3, throwup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check/evaluate if clause.
|
|
||||||
int status = evaluate(BREAK, s, env);
|
|
||||||
if (status > 0)
|
|
||||||
throw break_command_t();
|
|
||||||
return env.status(status, throwup);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int continue_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
return eval_exec(text, env, throwup, [&](token_vector &tokens){
|
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||||
env.set("command", "continue");
|
env.set("command", "continue");
|
||||||
if (!env.loop()) throw continue_error();
|
if (!env.loop()) throw continue_error();
|
||||||
int status = evaluate(CONTINUE, std::move(tokens), env);
|
int status = evaluate(CONTINUE, std::move(tokens), env);
|
||||||
@@ -478,26 +456,39 @@ int continue_command::execute(Environment &env, const fdmask &fds, bool throwup)
|
|||||||
return status;
|
return status;
|
||||||
});
|
});
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (control_c) throw execution_of_input_terminated();
|
|
||||||
|
|
||||||
std::string s = expand_vars(text, env);
|
|
||||||
|
|
||||||
env.set("command", "continue");
|
|
||||||
|
|
||||||
env.echo("%s", s.c_str());
|
|
||||||
if (!env.loop()) {
|
|
||||||
fputs("### MPW Shell - Continue must be within for or loop.\n", stderr);
|
|
||||||
return env.status(-3, throwup);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// check/evaluate if clause.
|
int exit_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
int status = evaluate(CONTINUE, s, env);
|
|
||||||
if (status > 0)
|
// exit
|
||||||
throw continue_command_t();
|
// exit [number] [if expr ]]
|
||||||
return env.status(status, throwup);
|
// todo --
|
||||||
#endif
|
|
||||||
|
return eval_exec(text, env, fds, throwup, [&](token_vector &tokens){
|
||||||
|
env.set("command", "exit");
|
||||||
|
env.status(0);
|
||||||
|
|
||||||
|
if (tokens.size() <= 1) {
|
||||||
|
throw exit_command_t{};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
value v(tokens[1]);
|
||||||
|
// remove the return value to make processing easier.
|
||||||
|
if (v.is_number()) {
|
||||||
|
tokens.erase(tokens.begin() + 1);
|
||||||
|
}
|
||||||
|
status = evaluate(EXIT, std::move(tokens), env);
|
||||||
|
|
||||||
|
if (status) {
|
||||||
|
int ok = v.to_number(0);
|
||||||
|
throw exit_command_t{ok};
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int or_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
int or_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
||||||
@@ -550,8 +541,9 @@ int pipe_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
|||||||
lseek(fd, 0, SEEK_SET);
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
rv = children[1]->execute(e, pipe_fd | fds, throwup);
|
rv = children[1]->execute(e, pipe_fd | fds, throwup);
|
||||||
|
return e.status(rv, throwup);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (children[0]) return children[0]->execute(e, fds, throwup);
|
if (children[0]) return children[0]->execute(e, fds, throwup);
|
||||||
if (children[1]) return children[1]->execute(e, fds, throwup);
|
if (children[1]) return children[1]->execute(e, fds, throwup);
|
||||||
return e.status(0, throwup);
|
return e.status(0, throwup);
|
||||||
@@ -576,7 +568,7 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
|||||||
return e.status(-3);
|
return e.status(-3);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string s = expand_vars(text, e);
|
std::string s = expand_vars(text, e, fds);
|
||||||
|
|
||||||
e.echo("%s", s.c_str());
|
e.echo("%s", s.c_str());
|
||||||
|
|
||||||
@@ -600,7 +592,7 @@ int error_command::execute(Environment &e, const fdmask &fds, bool throwup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class F>
|
template<class F>
|
||||||
int begin_end_exec(std::string begin, std::string end, Environment &env, bool throwup, F &&fx) {
|
int begin_end_exec(std::string begin, std::string end, Environment &env, const fdmask &fds, bool throwup, F &&fx) {
|
||||||
|
|
||||||
|
|
||||||
bool echo = true;
|
bool echo = true;
|
||||||
@@ -610,8 +602,8 @@ int begin_end_exec(std::string begin, std::string end, Environment &env, bool th
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
process p;
|
process p;
|
||||||
begin = expand_vars(begin, env);
|
begin = expand_vars(begin, env, fds);
|
||||||
end = expand_vars(end, env);
|
end = expand_vars(end, env, fds);
|
||||||
|
|
||||||
auto b = tokenize(begin, true);
|
auto b = tokenize(begin, true);
|
||||||
auto e = tokenize(end, false);
|
auto e = tokenize(end, false);
|
||||||
@@ -638,12 +630,11 @@ int begin_end_exec(std::string begin, std::string end, Environment &env, bool th
|
|||||||
}
|
}
|
||||||
|
|
||||||
return env.status(rv, throwup);
|
return env.status(rv, throwup);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||||
|
|
||||||
env.set("command", type == BEGIN ? "end" : ")");
|
env.set("command", type == BEGIN ? "end" : ")");
|
||||||
if (b.size() != 1) {
|
if (b.size() != 1) {
|
||||||
@@ -663,53 +654,12 @@ int begin_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
});
|
});
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
std::string b = expand_vars(begin, env);
|
|
||||||
std::string e = expand_vars(end, env);
|
|
||||||
|
|
||||||
|
|
||||||
env.set("command", type == BEGIN ? "end" : ")");
|
|
||||||
|
|
||||||
// echo!
|
|
||||||
env.echo("%s ... %s",
|
|
||||||
b.c_str(),
|
|
||||||
e.c_str()
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
env.indent_and([&]{
|
|
||||||
rv = vector_command::execute(env, newfds);
|
|
||||||
});
|
|
||||||
|
|
||||||
env.echo("%s", type == BEGIN ? "end" : ")");
|
|
||||||
|
|
||||||
return env.status(rv);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||||
|
|
||||||
env.set("command", "end");
|
env.set("command", "end");
|
||||||
if (b.size() != 1) {
|
if (b.size() != 1) {
|
||||||
@@ -741,58 +691,11 @@ int loop_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
std::string b = expand_vars(begin, env);
|
|
||||||
std::string e = expand_vars(end, env);
|
|
||||||
|
|
||||||
|
|
||||||
env.set("command", "end");
|
|
||||||
|
|
||||||
// echo!
|
|
||||||
env.echo("%s ... %s",
|
|
||||||
b.c_str(),
|
|
||||||
e.c_str()
|
|
||||||
);
|
|
||||||
|
|
||||||
// check for extra tokens...
|
|
||||||
auto bt = tokenize(b, true);
|
|
||||||
if (bt.size() != 1) {
|
|
||||||
fprintf(stderr, "### Loop - Too many parameters were specified.\n");
|
|
||||||
fprintf(stderr, "Usage - Loop\n");
|
|
||||||
return env.status(-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
fdmask newfds;
|
|
||||||
int status = check_ends(e, newfds);
|
|
||||||
newfds |= fds;
|
|
||||||
if (status) return env.status(status);
|
|
||||||
|
|
||||||
int rv = 0;
|
|
||||||
for(;;) {
|
|
||||||
|
|
||||||
if (control_c) throw execution_of_input_terminated();
|
|
||||||
|
|
||||||
try {
|
|
||||||
env.loop_indent_and([&]{
|
|
||||||
rv = vector_command::execute(env, newfds);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (break_command_t &ex) {
|
|
||||||
env.echo("end");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (continue_command_t &ex) {}
|
|
||||||
env.echo("end");
|
|
||||||
}
|
|
||||||
return env.status(rv);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int for_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
int for_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
||||||
|
|
||||||
return begin_end_exec(begin, end, env, throwup, [&](token_vector &b, process &p){
|
return begin_end_exec(begin, end, env, fds, throwup, [&](token_vector &b, process &p){
|
||||||
|
|
||||||
env.set("command", "end");
|
env.set("command", "end");
|
||||||
|
|
||||||
@@ -826,56 +729,6 @@ int for_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
});
|
});
|
||||||
|
|
||||||
#if 0
|
|
||||||
std::string b = expand_vars(begin, env);
|
|
||||||
std::string e = expand_vars(end, env);
|
|
||||||
|
|
||||||
|
|
||||||
env.set("command", "end");
|
|
||||||
|
|
||||||
// echo!
|
|
||||||
env.echo("%s ... %s",
|
|
||||||
b.c_str(),
|
|
||||||
e.c_str()
|
|
||||||
);
|
|
||||||
|
|
||||||
// check for extra tokens...
|
|
||||||
auto bt = tokenize(b, true);
|
|
||||||
if (bt.size() < 3 || strcasecmp(bt[2].string.c_str(), "in")) {
|
|
||||||
fprintf(stderr, "### For - Missing in keyword.\n");
|
|
||||||
fprintf(stderr, "Usage - For name in [word...]\n");
|
|
||||||
return env.status(-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
fdmask newfds;
|
|
||||||
int status = check_ends(e, newfds);
|
|
||||||
newfds |= fds;
|
|
||||||
if (status) return env.status(status);
|
|
||||||
|
|
||||||
int rv = 0;
|
|
||||||
for (int i = 3; i < bt.size(); ++i ) {
|
|
||||||
|
|
||||||
if (control_c) throw execution_of_input_terminated();
|
|
||||||
|
|
||||||
env.set(bt[1].string, bt[i].string);
|
|
||||||
|
|
||||||
try {
|
|
||||||
env.loop_indent_and([&]{
|
|
||||||
rv = vector_command::execute(env, newfds);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (break_command_t &ex) {
|
|
||||||
env.echo("end");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
catch (continue_command_t &ex) {}
|
|
||||||
env.echo("end");
|
|
||||||
}
|
|
||||||
|
|
||||||
return env.status(rv);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -900,7 +753,7 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
int tmp;
|
int tmp;
|
||||||
if (first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
tmp = begin_end_exec(c->clause, end, env, false, [&](token_vector &b, process &p){
|
tmp = begin_end_exec(c->clause, end, env, fds, false, [&](token_vector &b, process &p){
|
||||||
|
|
||||||
newfds = p.fds | fds;
|
newfds = p.fds | fds;
|
||||||
|
|
||||||
@@ -922,7 +775,7 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// second...
|
// second...
|
||||||
tmp = eval_exec(c->clause, env, false, [&](token_vector &b){
|
tmp = eval_exec(c->clause, env, fds, false, [&](token_vector &b){
|
||||||
if (skip || error) return 0;
|
if (skip || error) return 0;
|
||||||
|
|
||||||
int status = evaluate(c->type, std::move(b), env);
|
int status = evaluate(c->type, std::move(b), env);
|
||||||
@@ -945,49 +798,5 @@ int if_command::execute(Environment &env, const fdmask &fds, bool throwup) {
|
|||||||
env.echo("end");
|
env.echo("end");
|
||||||
if (error) return env.status(error, throwup);
|
if (error) return env.status(error, throwup);
|
||||||
return env.status(rv, throwup);
|
return env.status(rv, throwup);
|
||||||
|
|
||||||
#if 0
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int rv = 0;
|
|
||||||
bool ok = false;
|
|
||||||
|
|
||||||
std::string e = expand_vars(end, env);
|
|
||||||
|
|
||||||
env.set("command", "end");
|
|
||||||
|
|
||||||
// parse end for indirection.
|
|
||||||
fdmask newfds;
|
|
||||||
int status = check_ends(e, newfds);
|
|
||||||
newfds |= fds;
|
|
||||||
if (status) {
|
|
||||||
rv = status;
|
|
||||||
ok = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &c : clauses) {
|
|
||||||
std::string s = expand_vars(c->clause, env);
|
|
||||||
|
|
||||||
if (c->type == IF)
|
|
||||||
env.echo("%s ... %s", s.c_str(), e.c_str());
|
|
||||||
else
|
|
||||||
env.echo("%s", s.c_str());
|
|
||||||
|
|
||||||
if (ok) continue;
|
|
||||||
|
|
||||||
int tmp = evaluate(c->type, s, env);
|
|
||||||
if (tmp < 0) { ok = true; rv = tmp; continue; }
|
|
||||||
if (tmp == 0) continue;
|
|
||||||
|
|
||||||
env.indent_and([&](){
|
|
||||||
rv = c->execute(env, newfds);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
env.echo("end");
|
|
||||||
return env.status(rv);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
command.h
13
command.h
@@ -6,7 +6,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "phase2-parser.h"
|
#include "phase3.h"
|
||||||
|
|
||||||
typedef std::unique_ptr<struct command> command_ptr;
|
typedef std::unique_ptr<struct command> command_ptr;
|
||||||
typedef std::vector<command_ptr> command_ptr_vector;
|
typedef std::vector<command_ptr> command_ptr_vector;
|
||||||
@@ -22,7 +22,7 @@ struct command {
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
virtual bool terminal() const noexcept {
|
virtual bool terminal() const noexcept {
|
||||||
return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE || type == ERROR;
|
return type == EVALUATE || type == COMMAND || type == BREAK || type == CONTINUE || type == ERROR || type == EXIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
int type = 0;
|
int type = 0;
|
||||||
@@ -81,6 +81,15 @@ struct continue_command : public command {
|
|||||||
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct exit_command : public command {
|
||||||
|
|
||||||
|
template<class S>
|
||||||
|
exit_command(S &&s) : command(EXIT), text(std::forward<S>(s))
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
virtual int execute(Environment &e, const fdmask &fds, bool throwup) final override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct binary_command : public command {
|
struct binary_command : public command {
|
||||||
|
7
config.h.in
Normal file
7
config.h.in
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#ifndef __mpw_shell_config_h__
|
||||||
|
#define __mpw_shell_config_h__
|
||||||
|
|
||||||
|
#cmakedefine HAVE_DPRINTF
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@@ -269,10 +269,10 @@ namespace filesystem {
|
|||||||
path current_path(error_code& ec) {
|
path current_path(error_code& ec) {
|
||||||
|
|
||||||
char *cp;
|
char *cp;
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX+1];
|
||||||
|
|
||||||
ec.clear();
|
ec.clear();
|
||||||
cp = ::getcwd(buffer, sizeof(buffer));
|
cp = ::getcwd(buffer, PATH_MAX);
|
||||||
if (cp) return path(cp);
|
if (cp) return path(cp);
|
||||||
|
|
||||||
ec = error_code(errno, std::system_category());
|
ec = error_code(errno, std::system_category());
|
||||||
@@ -304,7 +304,7 @@ namespace filesystem {
|
|||||||
|
|
||||||
path canonical(const path& p, error_code& ec) {
|
path canonical(const path& p, error_code& ec) {
|
||||||
char *cp;
|
char *cp;
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX+1];
|
||||||
|
|
||||||
ec.clear();
|
ec.clear();
|
||||||
cp = realpath(p.c_str(), buffer);
|
cp = realpath(p.c_str(), buffer);
|
||||||
@@ -316,7 +316,7 @@ namespace filesystem {
|
|||||||
path canonical(const path& p, const path& base, error_code& ec) {
|
path canonical(const path& p, const path& base, error_code& ec) {
|
||||||
|
|
||||||
char *cp;
|
char *cp;
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX+1];
|
||||||
|
|
||||||
ec.clear();
|
ec.clear();
|
||||||
|
|
||||||
|
@@ -23,17 +23,12 @@ namespace {
|
|||||||
FX _fx;
|
FX _fx;
|
||||||
};
|
};
|
||||||
|
|
||||||
void throw_error(int error) {
|
|
||||||
throw std::system_error(error, std::system_category());
|
void set_or_throw_error(std::error_code *ec, int error, const std::string &what) {
|
||||||
|
if (ec) *ec = std::error_code(error, std::system_category());
|
||||||
|
else throw std::system_error(error, std::system_category(), what);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void throw_error(int error, const std::string &what) {
|
|
||||||
throw std::system_error(error, std::system_category(), what);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -41,12 +36,8 @@ namespace {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void throw_error() {
|
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||||
throw_error(GetLastError());
|
set_or_throw_error(ec, GetLastError(), what);
|
||||||
}
|
|
||||||
|
|
||||||
void throw_error(const std::string &what) {
|
|
||||||
throw_error(GetLastError(), what);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -61,7 +52,7 @@ void mapped_file_base::close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) {
|
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset, std::error_code *ec) {
|
||||||
|
|
||||||
|
|
||||||
HANDLE fh;
|
HANDLE fh;
|
||||||
@@ -70,9 +61,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
|||||||
// length of 0 in CreateFileMapping / MapViewOfFile
|
// length of 0 in CreateFileMapping / MapViewOfFile
|
||||||
// means map the entire file.
|
// means map the entire file.
|
||||||
|
|
||||||
if (is_open()) {
|
if (is_open()) close();
|
||||||
throw std::runtime_error("mapped_file_base::open - file already open");
|
|
||||||
}
|
|
||||||
|
|
||||||
fh = CreateFile(p.c_str(),
|
fh = CreateFile(p.c_str(),
|
||||||
flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
flags == readonly ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
|
||||||
@@ -83,7 +72,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
|||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
if (fh == INVALID_HANDLE_VALUE) {
|
if (fh == INVALID_HANDLE_VALUE) {
|
||||||
throw_error();
|
return set_or_throw_error(ec, "CreateFile");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fh_close = make_unique_resource(fh, CloseHandle);
|
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||||||
@@ -116,7 +105,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
|||||||
|
|
||||||
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||||||
if (mh == INVALID_HANDLE_VALUE) {
|
if (mh == INVALID_HANDLE_VALUE) {
|
||||||
throw_error();
|
return set_or_throw_error(ec, "CreateFileMapping");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mh_close = make_unique_resource(mh, CloseHandle);
|
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||||||
@@ -129,7 +118,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
|||||||
length,
|
length,
|
||||||
nullptr);
|
nullptr);
|
||||||
if (!_data) {
|
if (!_data) {
|
||||||
throw_error();
|
return set_or_throw_error(ec, "MapViewOfFileEx");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -152,10 +141,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
|||||||
const DWORD access = FILE_MAP_WRITE;
|
const DWORD access = FILE_MAP_WRITE;
|
||||||
|
|
||||||
|
|
||||||
if (is_open()) {
|
if (is_open()) close();
|
||||||
throw std::runtime_error("mapped_file_base::create - file already open");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fh = CreateFile(p.c_str(),
|
fh = CreateFile(p.c_str(),
|
||||||
GENERIC_READ | GENERIC_WRITE,
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
@@ -166,7 +152,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
|||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
if (fh == INVALID_HANDLE_VALUE) {
|
if (fh == INVALID_HANDLE_VALUE) {
|
||||||
throw_error();
|
return set_or_throw_error(nullptr, "CreateFile");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fh_close = make_unique_resource(fh, CloseHandle);
|
auto fh_close = make_unique_resource(fh, CloseHandle);
|
||||||
@@ -174,11 +160,11 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
|||||||
|
|
||||||
file_size.QuadPart = length;
|
file_size.QuadPart = length;
|
||||||
if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN));
|
if (!SetFilePointerEx(fh, file_size, nullptr, FILE_BEGIN));
|
||||||
if (!SetEndOfFile(fh)) throw_error();
|
if (!SetEndOfFile(fh)) return set_or_throw_error(nullptr, "SetEndOfFile");
|
||||||
|
|
||||||
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
mh = CreateFileMapping(fh, nullptr, protect, 0, 0, 0);
|
||||||
if (mh == INVALID_HANDLE_VALUE) {
|
if (mh == INVALID_HANDLE_VALUE) {
|
||||||
throw_error();
|
return set_or_throw_error(nullptr, "CreateFileMapping");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mh_close = make_unique_resource(mh, CloseHandle);
|
auto mh_close = make_unique_resource(mh, CloseHandle);
|
||||||
@@ -191,7 +177,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
|||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
if (!_data) {
|
if (!_data) {
|
||||||
throw_error();
|
return set_or_throw_error(nullptr, "MapViewOfFileEx");
|
||||||
}
|
}
|
||||||
|
|
||||||
_file_handle = fh_close.release();
|
_file_handle = fh_close.release();
|
||||||
@@ -213,13 +199,10 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void throw_error() {
|
void set_or_throw_error(std::error_code *ec, const std::string &what) {
|
||||||
throw_error(errno);
|
set_or_throw_error(ec, errno, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_error(const std::string &what) {
|
|
||||||
throw_error(errno, what);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,15 +215,15 @@ void mapped_file_base::close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset) {
|
void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, size_t offset, std::error_code *ec) {
|
||||||
|
|
||||||
|
if (ec) ec->clear();
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
int oflags = 0;
|
int oflags = 0;
|
||||||
|
|
||||||
if (is_open()) {
|
if (is_open()) close();
|
||||||
throw std::runtime_error("mapped_file_base::open - file already open");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (flags) {
|
switch (flags) {
|
||||||
case readonly:
|
case readonly:
|
||||||
@@ -253,7 +236,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
|||||||
|
|
||||||
fd = ::open(p.c_str(), oflags);
|
fd = ::open(p.c_str(), oflags);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
throw_error(errno);
|
return set_or_throw_error(ec, "open");
|
||||||
}
|
}
|
||||||
|
|
||||||
//defer([fd](){::close(fd); });
|
//defer([fd](){::close(fd); });
|
||||||
@@ -265,7 +248,8 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
|||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
if (::fstat(fd, &st) < 0) {
|
if (::fstat(fd, &st) < 0) {
|
||||||
throw_error(errno);
|
set_or_throw_error(ec, "stat");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
length = st.st_size;
|
length = st.st_size;
|
||||||
}
|
}
|
||||||
@@ -279,7 +263,7 @@ void mapped_file_base::open(const path_type& p, mapmode flags, size_t length, si
|
|||||||
|
|
||||||
if (_data == MAP_FAILED) {
|
if (_data == MAP_FAILED) {
|
||||||
_data = nullptr;
|
_data = nullptr;
|
||||||
throw_error(errno);
|
return set_or_throw_error(ec, "mmap");
|
||||||
}
|
}
|
||||||
|
|
||||||
_fd = close_fd.release();
|
_fd = close_fd.release();
|
||||||
@@ -292,15 +276,11 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
|||||||
int fd;
|
int fd;
|
||||||
const size_t offset = 0;
|
const size_t offset = 0;
|
||||||
|
|
||||||
if (is_open()) {
|
if (is_open()) close();
|
||||||
throw std::runtime_error("mapped_file_base::create - file already open");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC);
|
fd = ::open(p.c_str(), O_RDWR | O_CREAT | O_TRUNC);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
throw_error(errno);
|
return set_or_throw_error(nullptr, "open");
|
||||||
}
|
}
|
||||||
|
|
||||||
//defer([fd](){::close(fd); });
|
//defer([fd](){::close(fd); });
|
||||||
@@ -309,7 +289,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
|||||||
|
|
||||||
|
|
||||||
if (::ftruncate(fd, length) < 0) {
|
if (::ftruncate(fd, length) < 0) {
|
||||||
throw_error(errno);
|
return set_or_throw_error(nullptr, "ftruncate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -320,7 +300,7 @@ void mapped_file_base::create(const path_type& p, size_t length) {
|
|||||||
|
|
||||||
if (_data == MAP_FAILED) {
|
if (_data == MAP_FAILED) {
|
||||||
_data = nullptr;
|
_data = nullptr;
|
||||||
throw_error(errno);
|
return set_or_throw_error(nullptr, "mmap");
|
||||||
}
|
}
|
||||||
|
|
||||||
_fd = close_fd.release();
|
_fd = close_fd.release();
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
class mapped_file_base {
|
class mapped_file_base {
|
||||||
public:
|
public:
|
||||||
@@ -40,7 +40,7 @@ protected:
|
|||||||
|
|
||||||
void swap(mapped_file_base &rhs);
|
void swap(mapped_file_base &rhs);
|
||||||
|
|
||||||
void open(const path_type& p, mapmode flags, size_t length, size_t offset);
|
void open(const path_type& p, mapmode flags, size_t length, size_t offset, std::error_code *ec);
|
||||||
void create(const path_type &p, size_t new_size); // always creates readwrite.
|
void create(const path_type &p, size_t new_size); // always creates readwrite.
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
@@ -79,6 +79,22 @@ public:
|
|||||||
open(p, flags, length, offset);
|
open(p, flags, length, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mapped_file(const path_type &p, std::error_code &ec) noexcept {
|
||||||
|
open(p, readonly, -1, 0, ec);
|
||||||
|
}
|
||||||
|
mapped_file(const path_type &p, mapmode flags, std::error_code &ec) noexcept {
|
||||||
|
open(p, flags, -1, 0, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapped_file(const path_type &p, mapmode flags, size_t length, std::error_code &ec) noexcept {
|
||||||
|
open(p, flags, length, 0, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapped_file(const path_type &p, mapmode flags, size_t length, size_t offset, std::error_code &ec) noexcept {
|
||||||
|
open(p, flags, length, offset, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
mapped_file(mapped_file &&);
|
mapped_file(mapped_file &&);
|
||||||
mapped_file(const mapped_file &) = delete;
|
mapped_file(const mapped_file &) = delete;
|
||||||
|
|
||||||
@@ -87,7 +103,20 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
void open(const path_type& p, mapmode flags, size_t length = -1, size_t offset = 0) {
|
void open(const path_type& p, mapmode flags, size_t length = -1, size_t offset = 0) {
|
||||||
base::open(p, flags, length, offset);
|
base::open(p, flags, length, offset, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open(const path_type &p, std::error_code &ec) noexcept {
|
||||||
|
base::open(p, readonly, -1, 0, &ec);
|
||||||
|
}
|
||||||
|
void open(const path_type &p, mapmode flags, std::error_code &ec) noexcept {
|
||||||
|
base::open(p, flags, -1, 0, &ec);
|
||||||
|
}
|
||||||
|
void open(const path_type &p, mapmode flags, size_t length, std::error_code &ec) noexcept {
|
||||||
|
base::open(p, flags, length, 0, &ec);
|
||||||
|
}
|
||||||
|
void open(const path_type &p, mapmode flags, size_t length, size_t offset, std::error_code &ec) noexcept {
|
||||||
|
base::open(p, flags, length, offset, &ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
11
error.h
11
error.h
@@ -67,4 +67,15 @@ public:
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
these are used for flow-control.
|
||||||
|
they do not inherit from std::exception to prevent being caught
|
||||||
|
by normal handlers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct break_command_t {};
|
||||||
|
struct continue_command_t {};
|
||||||
|
struct exit_command_t { int value = 0; };
|
||||||
|
struct quit_command_t {};
|
||||||
|
|
||||||
#endif
|
#endif
|
@@ -49,3 +49,9 @@ set -e MWCIncludes "{CIncludes}"
|
|||||||
set -e ARMCIncludes "{MPW}Interfaces:ARMCIncludes:"
|
set -e ARMCIncludes "{MPW}Interfaces:ARMCIncludes:"
|
||||||
|
|
||||||
set -e AsmMatOpts "-t 4"
|
set -e AsmMatOpts "-t 4"
|
||||||
|
|
||||||
|
# customizations can go in the UserStartup file.
|
||||||
|
if `exists {MPW}UserStartup` != ""
|
||||||
|
execute {MPW}UserStartup
|
||||||
|
end
|
||||||
|
|
||||||
|
@@ -81,7 +81,7 @@ std::string utf8_to_macroman(const std::string &s) {
|
|||||||
rv.reserve(s.size());
|
rv.reserve(s.size());
|
||||||
|
|
||||||
unsigned cs = 0;
|
unsigned cs = 0;
|
||||||
uint16_t tmp;
|
uint16_t tmp = 0;
|
||||||
|
|
||||||
for (unsigned char c : s) {
|
for (unsigned char c : s) {
|
||||||
switch(cs) {
|
switch(cs) {
|
||||||
|
@@ -4,6 +4,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <system_error>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@@ -46,12 +48,25 @@
|
|||||||
action epush { /* epush */ ev.push_back(fc); }
|
action epush { /* epush */ ev.push_back(fc); }
|
||||||
action efinish1 {
|
action efinish1 {
|
||||||
/* efinish1 */
|
/* efinish1 */
|
||||||
|
|
||||||
|
/*
|
||||||
throw std::runtime_error("MPW Shell - `...` not yet supported.");
|
throw std::runtime_error("MPW Shell - `...` not yet supported.");
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::string s = subshell(ev, env, fds);
|
||||||
|
scratch.append(s);
|
||||||
|
|
||||||
fgoto *xcs;
|
fgoto *xcs;
|
||||||
}
|
}
|
||||||
action efinish2 {
|
action efinish2 {
|
||||||
/* efinish2 */
|
/* efinish2 */
|
||||||
throw std::runtime_error("MPW Shell - `...` not yet supported.");
|
|
||||||
|
std::string s = subshell(ev, env, fds);
|
||||||
|
for (auto c : s) {
|
||||||
|
if (c == '\'' || c == '"' ) scratch.push_back(escape);
|
||||||
|
scratch.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
fgoto *xcs;
|
fgoto *xcs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +125,48 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
%% write data;
|
%% write data;
|
||||||
|
|
||||||
|
|
||||||
|
std::string subshell(const std::string &s, Environment &env, const fdmask &fds) {
|
||||||
|
|
||||||
|
|
||||||
|
char temp[32] = "/tmp/mpw-shell-XXXXXXXX";
|
||||||
|
|
||||||
|
int fd = mkstemp(temp);
|
||||||
|
unlink(temp);
|
||||||
|
|
||||||
|
fdset new_fds;
|
||||||
|
new_fds.set(1, fd);
|
||||||
|
|
||||||
|
int rv = 0;
|
||||||
|
env.indent_and([&](){
|
||||||
|
|
||||||
|
rv = read_string(env, s, new_fds | fds);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
std::string tmp;
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
for(;;) {
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
ssize_t len = read(fd, buffer, sizeof(buffer));
|
||||||
|
if (len == 0) break;
|
||||||
|
if (len < 0) {
|
||||||
|
if (errno == EINTR) continue;
|
||||||
|
throw std::system_error(errno, std::system_category(), "read");
|
||||||
|
}
|
||||||
|
tmp.append(buffer, buffer + len);
|
||||||
|
}
|
||||||
|
std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](uint8_t x){
|
||||||
|
if (x == '\r' || x == '\n') x = ' ';
|
||||||
|
return x;
|
||||||
|
});
|
||||||
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string expand_vars(const std::string &s, const Environment &env) {
|
}
|
||||||
|
|
||||||
|
std::string expand_vars(const std::string &s, Environment &env, const fdmask &fds) {
|
||||||
if (s.find_first_of("{`", 0, 2) == s.npos) return s;
|
if (s.find_first_of("{`", 0, 2) == s.npos) return s;
|
||||||
|
|
||||||
int cs;
|
int cs;
|
||||||
|
@@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
|
|
||||||
'-' when eval
|
'-' when eval
|
||||||
%push_token => { tokens.emplace_back("+", '-'); };
|
%push_token => { tokens.emplace_back("-", '-'); };
|
||||||
|
|
||||||
'!' when eval
|
'!' when eval
|
||||||
%push_token => { tokens.emplace_back("!", '!'); };
|
%push_token => { tokens.emplace_back("!", '!'); };
|
||||||
|
289
mpw-shell.cpp
289
mpw-shell.cpp
@@ -2,6 +2,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <atomic>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -10,16 +12,15 @@
|
|||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
#include "mpw-shell.h"
|
#include "mpw-shell.h"
|
||||||
|
#include "mpw_parser.h"
|
||||||
|
|
||||||
#include "fdset.h"
|
#include "fdset.h"
|
||||||
|
|
||||||
#include "macroman.h"
|
#include "macroman.h"
|
||||||
|
|
||||||
#include "phase1.h"
|
|
||||||
#include "phase2.h"
|
|
||||||
#include "command.h"
|
|
||||||
|
|
||||||
#include "cxx/mapped_file.h"
|
#include "cxx/mapped_file.h"
|
||||||
#include "cxx/filesystem.h"
|
#include "cxx/filesystem.h"
|
||||||
#include "cxx/string_splitter.h"
|
#include "cxx/string_splitter.h"
|
||||||
@@ -33,7 +34,6 @@
|
|||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#include <sysexits.h>
|
#include <sysexits.h>
|
||||||
#include <paths.h>
|
#include <paths.h>
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
@@ -80,42 +80,73 @@ void init(Environment &env) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int read_file(phase1 &p, const std::string &file) {
|
|
||||||
const mapped_file mf(file, mapped_file::readonly);
|
|
||||||
|
|
||||||
p.process(mf.begin(), mf.end(), false);
|
int read_file(Environment &e, const std::string &file, const fdmask &fds) {
|
||||||
p.finish();
|
std::error_code ec;
|
||||||
return 0;
|
const mapped_file mf(file, mapped_file::readonly, ec);
|
||||||
|
if (ec) {
|
||||||
|
fprintf(stderr, "# Error reading %s: %s\n", file.c_str(), ec.message().c_str());
|
||||||
|
return e.status(-1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int read_fd(phase1 &p, int fd) {
|
|
||||||
|
mpw_parser p(e, fds);
|
||||||
|
e.status(0, false);
|
||||||
|
|
||||||
|
try {
|
||||||
|
p.parse(mf.begin(), mf.end());
|
||||||
|
p.finish();
|
||||||
|
} catch(const execution_of_input_terminated &ex) {
|
||||||
|
return ex.status();
|
||||||
|
}
|
||||||
|
return e.status();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int read_string(Environment &e, const std::string &s, const fdmask &fds) {
|
||||||
|
mpw_parser p(e, fds);
|
||||||
|
e.status(0, false);
|
||||||
|
try {
|
||||||
|
p.parse(s);
|
||||||
|
p.finish();
|
||||||
|
} catch(const execution_of_input_terminated &ex) {
|
||||||
|
return ex.status();
|
||||||
|
}
|
||||||
|
return e.status();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int read_fd(Environment &e, int fd, const fdmask &fds) {
|
||||||
|
|
||||||
unsigned char buffer[2048];
|
unsigned char buffer[2048];
|
||||||
ssize_t size;
|
ssize_t size;
|
||||||
|
|
||||||
|
mpw_parser p(e, fds);
|
||||||
|
e.status(0, false);
|
||||||
|
|
||||||
|
try {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
size = read(fd, buffer, sizeof(buffer));
|
size = read(fd, buffer, sizeof(buffer));
|
||||||
if (size < 0) {
|
if (size < 0) {
|
||||||
if (errno == EINTR) continue;
|
if (errno == EINTR) continue;
|
||||||
perror("read: ");
|
perror("read");
|
||||||
return -1;
|
e.status(-1, false);
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (size == 0) p.finish();
|
|
||||||
else p.process(buffer, buffer + size);
|
|
||||||
} catch(std::exception &ex) {
|
|
||||||
fprintf(stderr, "### %s\n", ex.what());
|
|
||||||
p.reset();
|
|
||||||
}
|
}
|
||||||
if (size == 0) break;
|
if (size == 0) break;
|
||||||
|
p.parse(buffer, buffer + size);
|
||||||
}
|
}
|
||||||
|
p.finish();
|
||||||
return 0;
|
} catch(const execution_of_input_terminated &ex) {
|
||||||
|
return ex.status();
|
||||||
|
}
|
||||||
|
return e.status();
|
||||||
}
|
}
|
||||||
|
|
||||||
void launch_mpw(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds);
|
void launch_mpw(const Environment &env, const std::vector<std::string> &argv, const fdmask &fds);
|
||||||
|
fs::path which(const Environment &env, const std::string &name);
|
||||||
|
|
||||||
int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::string> &argv) {
|
int read_make(Environment &env, const std::vector<std::string> &argv) {
|
||||||
|
|
||||||
int out[2];
|
int out[2];
|
||||||
int ok;
|
int ok;
|
||||||
@@ -147,10 +178,9 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::s
|
|||||||
}
|
}
|
||||||
|
|
||||||
close(out[1]);
|
close(out[1]);
|
||||||
int rv = read_fd(p1, out[0]);
|
int rv = read_fd(env, out[0]);
|
||||||
close(out[0]);
|
close(out[0]);
|
||||||
p1.finish();
|
|
||||||
p2.finish();
|
|
||||||
|
|
||||||
// check for make errors.
|
// check for make errors.
|
||||||
for(;;) {
|
for(;;) {
|
||||||
@@ -176,7 +206,7 @@ int read_make(phase1 &p1, phase2 &p2, Environment &env, const std::vector<std::s
|
|||||||
exit(EX_OSERR);
|
exit(EX_OSERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
return env.status();
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::atomic<int> control_c{0};
|
std::atomic<int> control_c{0};
|
||||||
@@ -190,7 +220,7 @@ void control_c_handler(int signal, siginfo_t *sinfo, void *context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
int interactive(Environment &env) {
|
||||||
|
|
||||||
std::string history_file = root();
|
std::string history_file = root();
|
||||||
history_file += ".history";
|
history_file += ".history";
|
||||||
@@ -207,18 +237,18 @@ int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
|||||||
|
|
||||||
sigaction(SIGINT, &act, &old_act);
|
sigaction(SIGINT, &act, &old_act);
|
||||||
|
|
||||||
|
mpw_parser p(env, true);
|
||||||
|
|
||||||
|
|
||||||
for(;;) {
|
for(;;) {
|
||||||
const char *prompt = "# ";
|
const char *prompt = "# ";
|
||||||
if (p1.continuation() || p2.continuation()) prompt = "> ";
|
if (p.continuation()) prompt = "> ";
|
||||||
char *cp = readline(prompt);
|
char *cp = readline(prompt);
|
||||||
if (!cp) {
|
if (!cp) {
|
||||||
if (control_c) {
|
if (control_c) {
|
||||||
control_c = 0;
|
control_c = 0;
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
p1.abort();
|
p.abort();
|
||||||
p2.abort();
|
|
||||||
env.status(-9, false);
|
env.status(-9, false);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -240,29 +270,17 @@ int interactive(Environment &env, phase1 &p1, phase2& p2) {
|
|||||||
s = utf8_to_macroman(s);
|
s = utf8_to_macroman(s);
|
||||||
|
|
||||||
s.push_back('\n');
|
s.push_back('\n');
|
||||||
try {
|
|
||||||
p1.process(s);
|
|
||||||
|
|
||||||
} catch(std::exception &ex) {
|
p.parse(s);
|
||||||
fprintf(stderr, "### %s\n", ex.what());
|
|
||||||
p1.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
p.finish();
|
||||||
try {
|
|
||||||
p1.finish();
|
|
||||||
} catch(std::exception &ex) {
|
|
||||||
fprintf(stderr, "### %s\n", ex.what());
|
|
||||||
p1.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
sigaction(SIGINT, &old_act, nullptr);
|
sigaction(SIGINT, &old_act, nullptr);
|
||||||
|
|
||||||
write_history(history_file.c_str());
|
write_history(history_file.c_str());
|
||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,6 +312,10 @@ void define(Environment &env, const std::string &s) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* todo: prevent -r and -s (don't generate shell code)
|
||||||
|
*/
|
||||||
void make_help(void) {
|
void make_help(void) {
|
||||||
|
|
||||||
#undef _
|
#undef _
|
||||||
@@ -329,59 +351,92 @@ int make(int argc, char **argv) {
|
|||||||
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
args.reserve(argc+1);
|
args.reserve(argc+1);
|
||||||
bool __ = false;
|
int c;
|
||||||
|
bool passthrough = false;
|
||||||
|
|
||||||
args.emplace_back("make");
|
static struct option longopts[] = {
|
||||||
|
{ "help", no_argument, nullptr, 'h' },
|
||||||
for (auto iter = argv; *iter; ++iter) {
|
{ "verbose", no_argument, nullptr, 'v' },
|
||||||
std::string tmp(*iter);
|
{ "test", no_argument, nullptr, 1 },
|
||||||
|
{ "dry-run", no_argument, nullptr, 2 },
|
||||||
if (!__) {
|
{ nullptr, 0, nullptr, 0},
|
||||||
if (tmp == "--")
|
|
||||||
{ __ = true; continue; }
|
|
||||||
|
|
||||||
if (tmp == "--help")
|
|
||||||
{ make_help(); exit(0); }
|
|
||||||
|
|
||||||
if (tmp == "--test" || tmp == "--dry-run")
|
|
||||||
{ e.set("test", "1"); continue; }
|
|
||||||
}
|
|
||||||
args.emplace_back(std::move(tmp));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
phase1 p1;
|
|
||||||
phase2 p2;
|
|
||||||
|
|
||||||
p1 >>= [&p2](std::string &&s) {
|
|
||||||
if (s.empty()) p2.finish();
|
|
||||||
else p2(std::move(s));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
p2 >>= [&e](command_ptr_vector &&v) {
|
args.push_back(""); // place-holder.
|
||||||
|
|
||||||
for (auto iter = v.begin(); iter != v.end(); ++iter) {
|
while ((c = getopt_long(argc, argv, "d:ef:i:prstuvwy", longopts, nullptr)) != -1) {
|
||||||
auto &ptr = *iter;
|
std::string flag = "-"; flag.push_back(c);
|
||||||
fdmask fds;
|
switch(c) {
|
||||||
try {
|
default:
|
||||||
ptr->execute(e, fds);
|
make_help();
|
||||||
} catch (execution_of_input_terminated &ex) {
|
return EX_USAGE;
|
||||||
control_c = 0;
|
|
||||||
fprintf(stderr, "### %s\n", ex.what());
|
case 'h':
|
||||||
if (e.exit())
|
make_help();
|
||||||
exit(ex.status());
|
return 0;
|
||||||
e.status(ex.status(), false);
|
|
||||||
|
case 1:
|
||||||
|
e.set("test", 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
passthrough = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
case 'f':
|
||||||
|
case 'i':
|
||||||
|
args.push_back(std::move(flag));
|
||||||
|
args.push_back(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
case 'p':
|
||||||
|
case 't':
|
||||||
|
case 'u':
|
||||||
|
case 'v':
|
||||||
|
case 'w':
|
||||||
|
case 'y':
|
||||||
|
args.push_back(std::move(flag));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
case 's':
|
||||||
|
args.push_back(std::move(flag));
|
||||||
|
passthrough = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
std::transform(argv, argv+argc, std::back_inserter(args), [](const char *cp){
|
||||||
|
return std::string(cp);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
e.startup(true);
|
e.startup(true);
|
||||||
read_file(p1, root() / "Startup");
|
read_file(e, root() / "Startup");
|
||||||
e.startup(false);
|
e.startup(false);
|
||||||
|
|
||||||
|
auto path = which(e, "Make");
|
||||||
|
if (path.empty()) {
|
||||||
|
fputs("### MPW Shell - Command \"Make\" was not found.\n", stderr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
e.set("command", path);
|
||||||
|
args[0] = path;
|
||||||
|
|
||||||
return read_make(p1, p2, e, args);
|
if (passthrough) {
|
||||||
|
|
||||||
|
launch_mpw(e, args, fdmask());
|
||||||
|
exit(EX_OSERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read_make(e, args);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,8 +513,11 @@ int main(int argc, char **argv) {
|
|||||||
mpw_path();
|
mpw_path();
|
||||||
|
|
||||||
fs::path self = fs::path(argv[0]).filename();
|
fs::path self = fs::path(argv[0]).filename();
|
||||||
if (self == "mpw-make") return make(argc - 1, argv + 1);
|
if (self == "mpw-make") return make(argc, argv);
|
||||||
if (self == "mpw-shell" && argc > 1 && !strcmp(argv[1],"make")) return make(argc - 2, argv + 2);
|
if (self == "mpw-shell" && argc > 1 && !strcmp(argv[1],"make")) {
|
||||||
|
argv[1] = (char *)"mpw-make";
|
||||||
|
return make(argc - 1, argv + 1);
|
||||||
|
}
|
||||||
|
|
||||||
Environment e;
|
Environment e;
|
||||||
init(e);
|
init(e);
|
||||||
@@ -499,59 +557,38 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
phase1 p1;
|
|
||||||
phase2 p2;
|
|
||||||
|
|
||||||
p1 >>= [&p2](std::string &&s) {
|
|
||||||
if (s.empty()) p2.finish();
|
|
||||||
else p2(std::move(s));
|
|
||||||
};
|
|
||||||
|
|
||||||
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) {
|
|
||||||
control_c = 0;
|
|
||||||
if (!(ptr->terminal() && ++iter == v.end())) {
|
|
||||||
fprintf(stderr, "### %s\n", ex.what());
|
|
||||||
}
|
|
||||||
e.status(ex.status(), false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!cflag) fprintf(stdout, "MPW Shell " VERSION "\n");
|
if (!cflag) fprintf(stdout, "MPW Shell " VERSION "\n");
|
||||||
if (!fflag) {
|
if (!fflag) {
|
||||||
fs::path startup = root() / "Startup";
|
fs::path startup = root() / "Startup";
|
||||||
e.startup(true);
|
e.startup(true);
|
||||||
|
mpw_parser p(e);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
read_file(p1, startup);
|
read_file(e, startup);
|
||||||
} catch (const std::system_error &ex) {
|
} catch (const std::system_error &ex) {
|
||||||
fprintf(stderr, "### %s: %s\n", startup.c_str(), ex.what());
|
fprintf(stderr, "### %s: %s\n", startup.c_str(), ex.what());
|
||||||
|
} catch (const quit_command_t &) {
|
||||||
}
|
}
|
||||||
|
|
||||||
e.startup(false);
|
e.startup(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
int rv = 0;
|
||||||
if (cflag) {
|
if (cflag) {
|
||||||
std::string s(cflag);
|
rv = read_string(e, cflag);
|
||||||
s.push_back('\n');
|
exit(rv);
|
||||||
p1.process(s, true);
|
|
||||||
p2.finish();
|
|
||||||
exit(e.status());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isatty(STDIN_FILENO))
|
if (isatty(STDIN_FILENO))
|
||||||
interactive(e, p1, p2);
|
rv = interactive(e);
|
||||||
else
|
else
|
||||||
read_fd(p1, STDIN_FILENO);
|
rv = read_fd(e, STDIN_FILENO);
|
||||||
p2.finish();
|
|
||||||
|
|
||||||
exit(e.status());
|
exit(rv);
|
||||||
|
}
|
||||||
|
catch (const quit_command_t &) {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
#include "environment.h"
|
#include "environment.h"
|
||||||
|
#include "fdset.h"
|
||||||
|
|
||||||
const unsigned char escape = 0xb6;
|
const unsigned char escape = 0xb6;
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
std::vector<token> tokenize(std::string &s, bool eval = false);
|
std::vector<token> tokenize(std::string &s, bool eval = false);
|
||||||
std::string expand_vars(const std::string &s, const class Environment &);
|
std::string expand_vars(const std::string &s, class Environment &, const fdmask &fds = fdmask());
|
||||||
|
|
||||||
//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);
|
||||||
@@ -67,4 +68,9 @@ int32_t evaluate_expression(const std::string &name, std::vector<token> &&tokens
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int read_file(Environment &e, const std::string &file, const fdmask &fds = fdmask());
|
||||||
|
int read_string(Environment &e, const std::string &s, const fdmask &fds = fdmask());
|
||||||
|
int read_fd(Environment &e, int fd, const fdmask &fds = fdmask());
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
106
mpw_parser.cpp
Normal file
106
mpw_parser.cpp
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "mpw_parser.h"
|
||||||
|
#include "phase3_parser.h"
|
||||||
|
|
||||||
|
#include "command.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
mpw_parser::mpw_parser(Environment &e, fdmask fds, bool interactive) : _env(e), _fds(fds), _interactive(interactive)
|
||||||
|
{
|
||||||
|
|
||||||
|
_p3 = phase3::make();
|
||||||
|
_p2.set_next([this](int type, std::string &&s){
|
||||||
|
_p3->parse(type, std::move(s));
|
||||||
|
});
|
||||||
|
|
||||||
|
_p1.set_next([this](std::string &&s){
|
||||||
|
_p2.parse(std::move(s));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mpw_parser::~mpw_parser() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool mpw_parser::continuation() const {
|
||||||
|
if (_p1.continuation()) return true;
|
||||||
|
if (_p2.continuation()) return true;
|
||||||
|
if (_p3->continuation()) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpw_parser::finish() {
|
||||||
|
_p1.finish();
|
||||||
|
_p2.finish();
|
||||||
|
_p3->parse(0, "");
|
||||||
|
|
||||||
|
// and now execute the commands...
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mpw_parser::reset() {
|
||||||
|
_p1.reset();
|
||||||
|
_p2.reset();
|
||||||
|
_p3->reset();
|
||||||
|
_abort = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void mpw_parser::abort() {
|
||||||
|
_p1.abort();
|
||||||
|
_p2.abort();
|
||||||
|
//_p3->abort();
|
||||||
|
_p3->reset();
|
||||||
|
_abort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mpw_parser::parse(const void *begin, const void *end) {
|
||||||
|
if (_abort) return;
|
||||||
|
_p1.parse((const unsigned char *)begin, (const unsigned char *)end);
|
||||||
|
|
||||||
|
// and execute...
|
||||||
|
execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void mpw_parser::execute() {
|
||||||
|
if (_abort) {
|
||||||
|
_p3->command_queue.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto commands = std::move(_p3->command_queue);
|
||||||
|
_p3->command_queue.clear();
|
||||||
|
|
||||||
|
|
||||||
|
std::reverse(commands.begin(), commands.end());
|
||||||
|
|
||||||
|
command_ptr cmd;
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (!commands.empty()) {
|
||||||
|
cmd = std::move(commands.back());
|
||||||
|
commands.pop_back();
|
||||||
|
cmd->execute(_env, _fds);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (execution_of_input_terminated &ex) {
|
||||||
|
|
||||||
|
_env.status(ex.status(), false);
|
||||||
|
|
||||||
|
if (_interactive) {
|
||||||
|
if (!cmd->terminal() || !commands.empty()) {
|
||||||
|
if (ex.status()) fprintf(stderr, "### %s\n", ex.what());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_abort = true;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
59
mpw_parser.h
Normal file
59
mpw_parser.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef __mpw_parser__
|
||||||
|
#define __mpw_parser__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "environment.h"
|
||||||
|
#include "fdset.h"
|
||||||
|
|
||||||
|
#include "phase1.h"
|
||||||
|
#include "phase2.h"
|
||||||
|
|
||||||
|
class mpw_parser {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
mpw_parser(Environment &e, fdmask fds = fdmask(), bool interactive = false);
|
||||||
|
mpw_parser(Environment &e, bool interactive) : mpw_parser(e, fdmask(), interactive)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~mpw_parser();
|
||||||
|
|
||||||
|
void parse(const void *begin, const void *end);
|
||||||
|
void parse(const std::string &s) {
|
||||||
|
parse(s.data(), s.data() + s.size());
|
||||||
|
}
|
||||||
|
void parse(const void *begin, size_t length) {
|
||||||
|
parse(begin, (const char *)begin + length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
void abort();
|
||||||
|
void finish();
|
||||||
|
|
||||||
|
bool continuation() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
mpw_parser& operator=(const mpw_parser &) = delete;
|
||||||
|
mpw_parser& operator=(mpw_parser &&) = delete;
|
||||||
|
|
||||||
|
mpw_parser(const mpw_parser &) = delete;
|
||||||
|
mpw_parser(mpw_parser &&) = delete;
|
||||||
|
|
||||||
|
void execute();
|
||||||
|
|
||||||
|
Environment &_env;
|
||||||
|
fdmask _fds;
|
||||||
|
bool _interactive = false;
|
||||||
|
bool _abort = false;
|
||||||
|
|
||||||
|
phase1 _p1;
|
||||||
|
phase2 _p2;
|
||||||
|
std::unique_ptr<class phase3> _p3;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
14
phase1.cpp
14
phase1.cpp
@@ -150,19 +150,10 @@ text:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void phase1::process(const std::string &s, bool final) {
|
void phase1::parse(const unsigned char *begin, const unsigned char *end) {
|
||||||
|
|
||||||
for (auto c : s) {
|
|
||||||
cs = process(c, cs);
|
|
||||||
}
|
|
||||||
if (final) finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
void phase1::process(const unsigned char *begin, const unsigned char *end, bool final) {
|
|
||||||
while (begin != end) {
|
while (begin != end) {
|
||||||
cs = process(*begin++, cs);
|
cs = process(*begin++, cs);
|
||||||
}
|
}
|
||||||
if (final) finish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase1::finish() {
|
void phase1::finish() {
|
||||||
@@ -181,7 +172,6 @@ void phase1::reset() {
|
|||||||
void phase1::flush() {
|
void phase1::flush() {
|
||||||
multiline = false;
|
multiline = false;
|
||||||
if (scratch.empty()) return;
|
if (scratch.empty()) return;
|
||||||
// strip trailing whitespace?
|
if (_then) _then(std::move(scratch));
|
||||||
if (pipe_to) pipe_to(std::move(scratch));
|
|
||||||
scratch.clear();
|
scratch.clear();
|
||||||
}
|
}
|
||||||
|
32
phase1.h
32
phase1.h
@@ -8,47 +8,33 @@
|
|||||||
class phase1 {
|
class phase1 {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(std::string &&)> pipe_function;
|
|
||||||
|
typedef std::function<void(std::string &&)> next_function_type;
|
||||||
|
|
||||||
phase1() = default;
|
phase1() = default;
|
||||||
|
|
||||||
void process(const unsigned char *begin, const unsigned char *end, bool final = false);
|
void parse(const unsigned char *begin, const unsigned char *end);
|
||||||
|
void parse(const std::string &s) { parse((const unsigned char *)s.data(), (const unsigned char *)s.data() + s.size()); }
|
||||||
void process(const char *begin, const char *end, bool final = false) {
|
void finish();
|
||||||
process((const unsigned char *)begin, (const unsigned char *)end, final);
|
|
||||||
}
|
|
||||||
|
|
||||||
void process(const std::string &s, bool final = false);// { process(s.data(), s.data() + s.size(), final); }
|
|
||||||
|
|
||||||
void finish();// { const char *tmp = "\n"; process(tmp, tmp+1, true); }
|
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void abort() { reset(); }
|
void abort() { reset(); }
|
||||||
|
|
||||||
|
|
||||||
phase1 &operator >>= (pipe_function f) { pipe_to = f; return *this; }
|
|
||||||
|
|
||||||
|
|
||||||
template<class F>
|
|
||||||
phase1 &operator >>= (F &f) {
|
|
||||||
using std::placeholders::_1;
|
|
||||||
pipe_to = std::bind(&F::operator(), &f, _1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool continuation() const { return multiline; }
|
bool continuation() const { return multiline; }
|
||||||
|
|
||||||
|
void set_next(next_function_type &&fx) { _then = std::move(fx); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
int process(unsigned char, int);
|
int process(unsigned char, int);
|
||||||
void flush();
|
void flush();
|
||||||
|
|
||||||
std::string scratch;
|
std::string scratch;
|
||||||
pipe_function pipe_to;
|
|
||||||
int line = 1;
|
int line = 1;
|
||||||
int cs = 0;
|
int cs = 0;
|
||||||
bool multiline = false;
|
bool multiline = false;
|
||||||
|
|
||||||
|
next_function_type _then;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
72
phase2.h
72
phase2.h
@@ -3,95 +3,41 @@
|
|||||||
#define __phase2_h__
|
#define __phase2_h__
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "command.h"
|
|
||||||
#include <lemon_base.h>
|
|
||||||
|
|
||||||
//typedef std::unique_ptr<struct command> command_ptr;
|
|
||||||
//typedef std::vector<command_ptr> command_ptr_vector;
|
|
||||||
|
|
||||||
class phase2_parser : public lemon_base<std::string> {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static std::unique_ptr<phase2_parser> make();
|
|
||||||
|
|
||||||
virtual void syntax_error(int yymajor, std::string &yyminor) override final;
|
|
||||||
virtual void parse_accept() override final;
|
|
||||||
virtual void parse_failure() override final;
|
|
||||||
|
|
||||||
bool continuation() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class phase2;
|
|
||||||
|
|
||||||
phase2_parser(const phase2_parser &) = delete;
|
|
||||||
phase2_parser(phase2_parser &&) = delete;
|
|
||||||
|
|
||||||
phase2_parser& operator=(const phase2_parser &) = delete;
|
|
||||||
phase2_parser& operator=(phase2_parser &&) = delete;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// these need to be accessible to the lemon-generated parser.
|
|
||||||
phase2_parser() = default;
|
|
||||||
|
|
||||||
command_ptr_vector command_queue;
|
|
||||||
bool error = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class phase2 {
|
class phase2 {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef std::function<void(command_ptr_vector &&)> pipe_function;
|
|
||||||
|
|
||||||
phase2();
|
typedef std::function<void(int, std::string &&)> next_function_type;
|
||||||
phase2(const phase2 &) = delete;
|
|
||||||
phase2(phase2 &&) = default;
|
|
||||||
|
|
||||||
phase2 & operator=(const phase2 &) = delete;
|
phase2() = default;
|
||||||
phase2 & operator=(phase2 &&) = default;
|
|
||||||
|
|
||||||
void operator()(const std::string &line) { process(line); }
|
void parse(std::string &&);
|
||||||
void process(const std::string &line);
|
|
||||||
void finish();
|
void finish();
|
||||||
|
|
||||||
phase2 &operator >>=(pipe_function f) { pipe_to = f; return *this; }
|
void reset();
|
||||||
|
void abort() { reset(); }
|
||||||
|
|
||||||
template<class F>
|
bool continuation() const { return false; }
|
||||||
phase2 &operator >>= (F &f) {
|
|
||||||
using std::placeholders::_1;
|
|
||||||
pipe_to = std::bind(&F::operator(), &f, _1);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool continuation() const {
|
void set_next(next_function_type &&fx) { _then = std::move(fx); }
|
||||||
return parser ? parser->continuation() : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void abort();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void parse(int, std::string &&);
|
void parse(int type, std::string &&s);
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<phase2_parser> parser;
|
|
||||||
|
|
||||||
std::string scratch;
|
std::string scratch;
|
||||||
int type = 0;
|
int type = 0;
|
||||||
int pcount = 0;
|
int pcount = 0;
|
||||||
|
|
||||||
pipe_function pipe_to;
|
|
||||||
|
|
||||||
void flush();
|
void flush();
|
||||||
bool special();
|
bool special();
|
||||||
int classify();
|
int classify();
|
||||||
void exec();
|
void exec();
|
||||||
|
|
||||||
|
next_function_type _then;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
99
phase2.rl
99
phase2.rl
@@ -4,9 +4,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "phase2-parser.h"
|
|
||||||
#include "phase2.h"
|
#include "phase2.h"
|
||||||
#include "command.h"
|
#include "phase3.h"
|
||||||
|
|
||||||
%%{
|
%%{
|
||||||
machine main;
|
machine main;
|
||||||
@@ -194,6 +193,7 @@ int phase2::classify() {
|
|||||||
_("else", ELSE)
|
_("else", ELSE)
|
||||||
_("end", END)
|
_("end", END)
|
||||||
_("evaluate", EVALUATE)
|
_("evaluate", EVALUATE)
|
||||||
|
_("exit", EXIT)
|
||||||
_("for", FOR)
|
_("for", FOR)
|
||||||
_("if", IF)
|
_("if", IF)
|
||||||
_("loop", LOOP)
|
_("loop", LOOP)
|
||||||
@@ -217,9 +217,7 @@ void phase2::flush() {
|
|||||||
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
while (!scratch.empty() && isspace(scratch.back())) scratch.pop_back();
|
||||||
|
|
||||||
|
|
||||||
if (!scratch.empty()) {
|
if (!scratch.empty()) parse(classify(), std::move(scratch));
|
||||||
parse(classify(), std::move(scratch));
|
|
||||||
}
|
|
||||||
|
|
||||||
type = 0;
|
type = 0;
|
||||||
pcount = 0;
|
pcount = 0;
|
||||||
@@ -236,20 +234,21 @@ bool phase2::special() {
|
|||||||
case EVALUATE:
|
case EVALUATE:
|
||||||
case BREAK:
|
case BREAK:
|
||||||
case CONTINUE:
|
case CONTINUE:
|
||||||
|
case EXIT:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::process(const std::string &line) {
|
void phase2::parse(int type, std::string &&s) {
|
||||||
|
if (_then) _then(type, std::move(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
void phase2::parse(std::string &&line) {
|
||||||
|
|
||||||
//fprintf(stderr, "-> %s\n", line.c_str());
|
//fprintf(stderr, "-> %s\n", line.c_str());
|
||||||
|
|
||||||
// still needed?
|
|
||||||
if (line.empty()) { finish(); return; }
|
|
||||||
|
|
||||||
|
|
||||||
int cs;
|
int cs;
|
||||||
const unsigned char *p = (const unsigned char *)line.data();
|
const unsigned char *p = (const unsigned char *)line.data();
|
||||||
const unsigned char *pe = p + line.size();
|
const unsigned char *pe = p + line.size();
|
||||||
@@ -264,81 +263,19 @@ void phase2::process(const std::string &line) {
|
|||||||
%% write exec;
|
%% write exec;
|
||||||
|
|
||||||
flush();
|
flush();
|
||||||
// 2 NLs to make the stack reduce. harmless if in a multi-line constuct.
|
if (_then) {
|
||||||
parse(NL, "");
|
_then(NL, "");
|
||||||
parse(NL, "");
|
_then(NL, "");
|
||||||
|
}
|
||||||
exec();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::finish() {
|
void phase2::finish() {
|
||||||
parse(0, "");
|
|
||||||
exec();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void phase2::parse(int token, std::string &&s) {
|
void phase2::reset() {
|
||||||
if (parser) parser->parse(token, std::move(s));
|
|
||||||
}
|
type = 0;
|
||||||
|
pcount = 0;
|
||||||
void phase2::exec() {
|
scratch.clear();
|
||||||
|
|
||||||
if (pipe_to && parser) {
|
|
||||||
command_ptr_vector tmp;
|
|
||||||
for (auto &p : parser->command_queue) {
|
|
||||||
if (p) tmp.emplace_back(std::move(p));
|
|
||||||
}
|
|
||||||
parser->command_queue.clear();
|
|
||||||
|
|
||||||
if (!tmp.empty()) pipe_to(std::move(tmp));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
phase2::phase2() {
|
|
||||||
parser = phase2_parser::make();
|
|
||||||
//parser->trace(stdout, " ] ");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void phase2::abort() {
|
|
||||||
parser = nullptr;
|
|
||||||
parser = phase2_parser::make();
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - phase2_parser
|
|
||||||
|
|
||||||
void phase2_parser::parse_accept() {
|
|
||||||
error = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void phase2_parser::parse_failure() {
|
|
||||||
error = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void phase2_parser::syntax_error(int yymajor, std::string &yyminor) {
|
|
||||||
/*
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,19 +6,19 @@
|
|||||||
|
|
||||||
%include {
|
%include {
|
||||||
|
|
||||||
#include "phase2.h"
|
#include "phase3_parser.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
#define LEMON_SUPER phase2_parser
|
#define LEMON_SUPER phase3
|
||||||
#include "phase2-parser.h"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
%code {
|
%code {
|
||||||
|
|
||||||
std::unique_ptr<phase2_parser> phase2_parser::make() {
|
std::unique_ptr<phase3> phase3::make() {
|
||||||
return std::make_unique<yypParser>();
|
return std::make_unique<yypParser>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool phase2_parser::continuation() const {
|
bool phase3::continuation() const {
|
||||||
yypParser *self = (yypParser *)this;
|
yypParser *self = (yypParser *)this;
|
||||||
|
|
||||||
for (const auto &e : *self) {
|
for (const auto &e : *self) {
|
||||||
@@ -36,6 +36,43 @@ bool phase2_parser::continuation() const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void phase3::parse_accept() {
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void phase3::parse_failure() {
|
||||||
|
error = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void phase3::syntax_error(int yymajor, std::string &yyminor) {
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
%left PIPE_PIPE AMP_AMP.
|
%left PIPE_PIPE AMP_AMP.
|
||||||
@@ -102,6 +139,7 @@ term(RV) ::= COMMAND(C). { RV = std::make_unique<simple_command>(std::move
|
|||||||
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
|
term(RV) ::= EVALUATE(C). { RV = std::make_unique<evaluate_command>(std::move(C)); }
|
||||||
term(RV) ::= BREAK(C). { RV = std::make_unique<break_command>(std::move(C)); }
|
term(RV) ::= BREAK(C). { RV = std::make_unique<break_command>(std::move(C)); }
|
||||||
term(RV) ::= CONTINUE(C). { RV = std::make_unique<continue_command>(std::move(C)); }
|
term(RV) ::= CONTINUE(C). { RV = std::make_unique<continue_command>(std::move(C)); }
|
||||||
|
term(RV) ::= EXIT(C). { RV = std::make_unique<exit_command>(std::move(C)); }
|
||||||
term(C) ::= if_command(C).
|
term(C) ::= if_command(C).
|
||||||
term(C) ::= begin_command(C).
|
term(C) ::= begin_command(C).
|
||||||
term(C) ::= paren_command(C).
|
term(C) ::= paren_command(C).
|
43
phase3_parser.h
Normal file
43
phase3_parser.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
#ifndef __phase3_h__
|
||||||
|
#define __phase3_h__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "command.h"
|
||||||
|
#include <lemon_base.h>
|
||||||
|
|
||||||
|
class phase3 : public lemon_base<std::string> {
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::unique_ptr<phase3> make();
|
||||||
|
|
||||||
|
virtual void syntax_error(int yymajor, std::string &yyminor) override final;
|
||||||
|
virtual void parse_accept() override final;
|
||||||
|
virtual void parse_failure() override final;
|
||||||
|
|
||||||
|
bool continuation() const;
|
||||||
|
|
||||||
|
//void parse(int type, std::string &&s) override final;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
phase3(const phase3 &) = delete;
|
||||||
|
phase3(phase3 &&) = delete;
|
||||||
|
|
||||||
|
phase3& operator=(const phase3 &) = delete;
|
||||||
|
phase3& operator=(phase3 &&) = delete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// these need to be accessible to the lemon-generated parser.
|
||||||
|
phase3() = default;
|
||||||
|
|
||||||
|
command_ptr_vector command_queue;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
friend class mpw_parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Reference in New Issue
Block a user