...
 
Commits (4)
  • Samuel Newbold's avatar
    break&continue with outer variants, sigint throws · e1013777
    Samuel Newbold authored
    The weight of a commit is not measured by the number of lines changed.
    e1013777
  • Samuel Newbold's avatar
    d75a1534
  • Samuel Newbold's avatar
    if and else must be in same block · c843b411
    Samuel Newbold authored
    Also, exit handled as an exception and includes the return value for the
    shell, exceptions don't need to return -1, zero returns are not printed
    in the test suite, static members of executable moved to new call_stack
    class.
    c843b411
  • Samuel Newbold's avatar
    a thrown exception triggers .failed_substitution · da07be75
    Samuel Newbold authored
    Also:
    * fixing a recently introduced bug where the statement that contained a failed
      substitution would still run.
    * the intentionally broken .failed_substitution handler is tested only where
      useful.
    * test and false functions wrap /usr/bin/test and /bin/false, and throw .false
      exceptions for their return codes so that they can be used for control flow.
    * replacing the excessively precise but deprecated usleep() with the even more
      excessively precise nanosleep.
    * .store_output identifies error by a thrown exception rather than a return
      code.
    * .fork returns exit_code rather than dollar_question
    * removing several more -1 returns.
    da07be75
......@@ -21,6 +21,7 @@
#include "argm.h"
#include "arg_script.h"
#include "call_stack.h"
#include "executable.h"
#include "file_stream.h"
#include "prototype.h"
......@@ -134,7 +135,8 @@ std::string::size_type Arg_script::constructor(const std::string& src,
else if (args.front().str() == ".argfunction") argfunction_level = 2;
else if (args.front().str() == ".escaped_argfunction")
argfunction_level = 3;
else std::abort(); // unhandled argfunction level
else std::abort(); // unhandled argfunction level
else;
if (src[point] == '}') terminator = *"";
else terminator = src[point];
return point;}
......@@ -185,7 +187,8 @@ Arg_script& Arg_script::operator=(const Arg_script& src) {
output = src.output;
error = src.error;
indent = src.indent;
terminator = src.terminator;}
terminator = src.terminator;
return *this;}
Arg_script::~Arg_script(void) {
delete argfunction;}
......@@ -218,11 +221,6 @@ std::string Arg_script::str(void) const {
else if (argfunction_level == 3) return ".super_escaped_argfunction";
else {std::abort(); return "";}} // unhandled argfunction_level
Argm Arg_script::base_interpret(const Argm& src, Error_list& errors) const {
Argm ret = interpret(src, errors);
if (errors.size()) Base_executable::exception_handler(errors);
return ret;}
// produce a destination Argm from the source Argm according to this script
Argm Arg_script::interpret(const Argm& src, Error_list& exceptions) const {
Argm result(src.parent_map(),
......
......@@ -39,7 +39,6 @@ class Arg_script {
std::back_insert_iterator<std::vector<Arg_script> > res,
Error_list& current_exceptions) const;
std::string str(void) const;
Argm base_interpret(const Argm& src, Error_list& errors) const;
Argm interpret(const Argm& src, Error_list& exceptions) const;
void promote_soons(unsigned);
bool is_argfunction(void) const {return argfunction_level == 1;}; };
......
......@@ -21,6 +21,7 @@
#include "argm.h"
#include "arg_script.h"
#include "call_stack.h"
#include "executable.h"
#include "executable_map.h"
#include "pipe_stream.h"
......@@ -193,7 +194,7 @@ Out Arg_spec::evaluate_expansion(const std::string& value, Out res)
if (word_selection == -1)
for (unsigned j=0; j<intermediate.size(); ++j)
tokenize_words(intermediate[j], res);
else if (word_selection >= intermediate.size())
else if (word_selection >= (int)intermediate.size())
throw Exception(Argm::Undefined_variable, str());
else *res++ = intermediate[word_selection];}
return res;}
......@@ -204,9 +205,13 @@ Out Arg_spec::evaluate_substitution(const Argm& src, Out res,
Substitution_stream override_stream;
Argm temp_argm(src);
temp_argm.output = override_stream.child_stream();
if ((*substitution)(temp_argm, exceptions))
throw Exception(Argm::Failed_substitution, str());
return evaluate_expansion(override_stream.value(), res);}
int ret = (*substitution)(temp_argm, exceptions);
if (global_stack.unwind_stack())
exceptions.add_error(Exception(Argm::Failed_substitution, str()));
else if (ret) exceptions.add_error(Exception(Argm::Internal_error,
"return-based failed substitution: " + str()));
else return evaluate_expansion(override_stream.value(), res);
return res;}
template<class Out> Out Arg_spec::evaluate_var(const Argm& src, Out res) const {
std::string focus = text;
......@@ -242,7 +247,9 @@ void Arg_spec::promote_soons(unsigned nesting) {
case SUBSTITUTION: case SOON_SUBSTITUTION:
soon_level += nesting;
substitution->promote_soons(nesting);
break;}}
break;
case FIXED: case REFERENCE: case SELECTION: case SELECT_VAR:
case SELECT_STAR_VAR: case STAR_REF:;}} // not relevant
// create a string from Arg_spec. inverse of constructor.
std::string Arg_spec::str(void) const {
......
This diff is collapsed.
......@@ -68,5 +68,5 @@ fw {.mapped_argfunction {.function_all_flags $name {.argfunction}}}
.function_all_flags .shutdown -- args ... {
.nop $args
.combine $nl (now terminating normally) $nl}
.exit
.exit 0
......@@ -18,6 +18,7 @@
#include "argm.h"
#include "arg_script.h"
#include "call_stack.h"
#include "executable.h"
#include "prototype.h"
......@@ -228,11 +229,15 @@ Exception::Exception(Exception_t exception_i, const Argm& src) :
void Error_list::reset(void) {
clear();
Base_executable::reset();}
global_stack.reset();}
void Error_list::add_error(const Argm& error){
push_back(error);
Base_executable::add_error();}
global_stack.add_error();}
void Error_list::replace_error(const Argm& error){
push_back(error);
global_stack.replace_error();}
Old_argv::Old_argv(const Argm::Argv& src) : argc_v(src.size()) {
focus = new char*[src.size()+1];
......
......@@ -37,6 +37,8 @@ class Argm {
Bad_if_nest,
Binary_does_not_exist,
Binary_not_found,
Break,
Continue,
Dash_dash_argument,
Dash_star_argument,
Directory_not_found,
......@@ -64,6 +66,7 @@ class Argm {
Illegal_variable_name,
Input_range,
Internal_error,
Interrupted_sleep,
Invalid_word_selection,
// Line_continuation,
Mismatched_brace,
......@@ -74,6 +77,7 @@ class Argm {
Not_a_directory,
Not_a_function,
Not_a_number,
Not_catching_exception,
Not_executable,
Not_soon_enough,
Raw_command,
......@@ -99,6 +103,7 @@ class Argm {
Unchecked_variable,
Undeclared_variable,
Undefined_variable,
Unfinished_if_block,
Unreadable_dir,
Unrecognized_flag,
Unused_before_set,
......@@ -158,6 +163,7 @@ class Argm {
struct Error_list : public std::list<Argm> {
void add_error(const Argm& error);
void replace_error(const Argm& error);
void reset(void); };
struct Exception : public Argm {
......
This diff is collapsed.
......@@ -43,6 +43,7 @@ int b_local(const Argm& argm, Error_list& exceptions);
int b_ls(const Argm& argm, Error_list& exceptions);
int b_nop(const Argm& argm, Error_list& exceptions);
int b_return(const Argm& argm, Error_list& exceptions);
int b_replace_exception(const Argm& argm, Error_list& exceptions);
int b_rm_executable(const Argm& argm, Error_list& exceptions);
int b_scope(const Argm& argm, Error_list& exceptions);
int b_selection_set(const Argm& argm, Error_list& exceptions);
......
// The definition of the Call_stack and Conditional_state classes. The former
// encapsulates the variables that had been static within Executable classes,
// and the latter encapsulates the state used by if_core
//
// Copyright (C) 2019 Samuel Newbold
#include <algorithm>
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "arg_spec.h"
#include "rwsh_stream.h"
#include "variable_map.h"
#include "argm.h"
#include "arg_script.h"
#include "builtin.h"
#include "call_stack.h"
#include "executable.h"
#include "executable_map.h"
#include "prototype.h"
#include "function.h"
void Call_stack::add_error(void) {
unwind_stack_v = true;
++current_exception_count;}
// code to call exception handlers when requested within a function
void Call_stack::catch_blocks(const Argm& argm, Error_list& exceptions) {
unsigned dropped_catches = 0;
for (auto focus = exceptions.begin(); focus != exceptions.end();)
if (find(argm.begin() + 1, argm.end(), (*focus)[0]) != argm.end()) {
if (dropped_catches >= max_extra) {
if (!execution_handler_excess_thrown)
exceptions.add_error(Exception(Argm::Excessive_exceptions_in_catch,
max_extra));
execution_handler_excess_thrown = true;
return;}
unsigned previous_count = current_exception_count;
in_exception_handler_v = true;
unwind_stack_v = false;
executable_map.run(*focus, exceptions);
in_exception_handler_v = false;
dropped_catches += current_exception_count - previous_count;
if (!unwind_stack()) {
focus = exceptions.erase(focus);
--current_exception_count;}
else focus++;}
else focus++;
if (current_exception_count || exit_requested) unwind_stack_v = true;
else unwind_stack_v = false;
in_exception_handler_v = false;}
// run one exception handler restoring unwind_stack afterwards
void Call_stack::catch_one(Argm& argm, Error_list& exceptions) {
in_exception_handler_v = true;
unwind_stack_v = false;
executable_map.run(argm, exceptions);
in_exception_handler_v = false;
unwind_stack_v = true;}
int Call_stack::collect_errors_core(const Argm& argm, bool logic,
Error_list& parent) {
Argm blank(argm.parent_map(), argm.input, argm.output.child_stream(),
argm.error);
blank.push_back(".mapped_argfunction");
std::vector<std::string> other(argm.begin()+1, argm.end());
int ret;
for (auto j: *argm.argfunction()) {
if (current_exception_count > max_collect) {
if (!collect_excess_thrown)
parent.add_error(Exception(Argm::Excessive_exceptions_collected,
max_collect));
unwind_stack_v = collect_excess_thrown = true;
return ret;}
Error_list children;
Argm statement_argm = j.interpret(blank, children);
if (!global_stack.unwind_stack())
ret = executable_map.run(statement_argm, children);
if (children.size()) {
unwind_stack_v = false;
for (auto k: children) {
parent.push_back(k);
if (logic == (find(other.begin(), other.end(), k[0]) != other.end()))
unwind_stack_v = true;}}} // will cause subsequent j to not run
if (parent.size()) unwind_stack_v = true;
return ret;}
/* code to call exception handlers, separated out of operator() for clarity.
The requirements for stack unwinding to work properly are as
follows: any code that runs more than one other executable must test
unwind_stack() in between each executable, which must be of an Executable
rather than a direct call to the function that implements a builtin (the
code below to run the fallback_handler is the only exception to this rule).
main does not need to do this handling, because anything that it calls
directly will have an initial nesting of 0.*/
void Call_stack::exception_handler(Error_list& exceptions) {
in_exception_handler_v = true;
unwind_stack_v = false;
std::set<std::string> failed_handlers;
for(;exceptions.size(); exceptions.pop_front(), --current_exception_count){
Argm& focus(exceptions.front());
if (failed_handlers.find(focus[0]) == failed_handlers.end()) {
unsigned previous_count = current_exception_count;
executable_map.run(focus, exceptions);
if (unwind_stack() || current_exception_count > previous_count) {
failed_handlers.insert(focus[0]);
exceptions.insert(++exceptions.begin(), exceptions.back());
exceptions.pop_back();
unwind_stack_v = false;}}
if (failed_handlers.find(focus[0]) != failed_handlers.end())
b_fallback_handler(Argm(".fallback_handler", focus.argv(),
focus.argfunction(), Variable_map::global_map,
focus.input, focus.output, focus.error),
exceptions);}
collect_excess_thrown = execution_handler_excess_thrown = false;
in_exception_handler_v = false;}
bool Call_stack::remove_exceptions(const std::string &name,
Error_list& exceptions) {
bool ret = false;
bool unwind_stack_before = unwind_stack_v;
for (auto focus = exceptions.begin(); focus != exceptions.end();)
if ((*focus)[0] == name) {
ret = true;
focus = exceptions.erase(focus);
--current_exception_count;}
else focus++;
if (unwind_stack_before && !exceptions.size()) unwind_stack_v = false;
return ret;}
void Call_stack::replace_error(void) {++current_exception_count;}
void Call_stack::request_exit(int exit_val) {
exit_requested = true;
if (!in_exception_handler()) unwind_stack_v = true;
exit_v += exit_val;}
void Call_stack::reset(void) {
unwind_stack_v = false;
current_exception_count = 0;}
Conditional_state::Conditional_state(void) : in_if_block(false),
successful_condition(false), exception_thrown(false) {}
// Copyright (C) 2019 Samuel Newbold
class Call_stack {
bool collect_excess_thrown;
bool execution_handler_excess_thrown;
bool in_exception_handler_v;
bool unwind_stack_v;
int exit_v;
public:
Argm::Exception_t caught_signal;
unsigned current_exception_count;
bool exit_requested;
int global_nesting;
unsigned max_collect;
unsigned max_extra;
int max_nesting;
Call_stack(void) : caught_signal(Argm::No_exception),
collect_excess_thrown(false), current_exception_count(0),
execution_handler_excess_thrown(false), exit_requested(false),
global_nesting(0), in_exception_handler_v(false), exit_v(0),
max_collect(1), max_extra(1), max_nesting(0), unwind_stack_v(false) {}
void add_error(void);
void catch_blocks(const Argm& argm, Error_list& exceptions);
void catch_one(Argm& argm, Error_list& exceptions);
int collect_errors_core(const Argm& argm, bool logic, Error_list& parent);
void exception_handler(Error_list& exceptions);
int exit_value(void) {return exit_v;}
bool in_exception_handler(void) {return in_exception_handler_v;}
bool remove_exceptions(const std::string &name, Error_list& exceptions);
void replace_error(void);
void request_exit(int exit_val);
void reset(void);
bool unwind_stack(void) {return unwind_stack_v;}
};
extern Call_stack global_stack;
struct Conditional_state {
bool in_if_block;
bool successful_condition;
bool exception_thrown;
Conditional_state(void);};
extern Conditional_state gc_state;
......@@ -19,6 +19,7 @@
#include "argm.h"
#include "arg_script.h"
#include "call_stack.h"
#include "clock.h"
#include "command_stream.h"
#include "executable.h"
......@@ -48,15 +49,15 @@ Command_stream& Command_stream::getline(Arg_script& dest, Error_list& errors) {
if (fail())
if (cmd.size()) { // EOF without a complete command
Exception raw_command(Argm::Raw_command, cmd);
executable_map.base_run(raw_command, errors);
executable_map.run_handling_exceptions(raw_command, errors);
Arg_script(cmd, 0, errors);} // this will throw the right exception
else return *this;
else cmd += line;
try {
dest = Arg_script(cmd, 0, errors);
if (Named_executable::unwind_stack()) {
if (global_stack.unwind_stack()) {
Exception raw_command(Argm::Raw_command, cmd);
Base_executable::catch_one(raw_command, errors);
global_stack.catch_one(raw_command, errors);
return *this;}
cmd_is_incomplete = false;}
catch (Unclosed_parenthesis exception) {
......@@ -64,9 +65,9 @@ Command_stream& Command_stream::getline(Arg_script& dest, Error_list& errors) {
catch (Unclosed_brace exception) {
cmd += '\n';}}
Exception raw_command(Argm::Raw_command, cmd);
executable_map.base_run(raw_command, errors);
executable_map.run_handling_exceptions(raw_command, errors);
return *this;}
// returns true if the last command could not be read
bool Command_stream::fail() const {
return Variable_map::exit_requested || src.fail();}
return global_stack.exit_requested || src.fail();}
argm.o: argm.cc arg_spec.h rwsh_stream.h rwshlib.h variable_map.h argm.h \
arg_script.h call_stack.h executable.h prototype.h function.h
arg_script.o: arg_script.cc arg_spec.h rwsh_stream.h variable_map.h \
argm.h arg_script.h call_stack.h executable.h file_stream.h prototype.h \
function.h
arg_spec.o: arg_spec.cc arg_spec.h rwsh_stream.h variable_map.h argm.h \
arg_script.h call_stack.h executable.h executable_map.h pipe_stream.h \
prototype.h read_dir.cc rwshlib.h selection.h substitution_stream.h \
tokenize.cc function.h argm_star_var.cc selection_read.cc
builtin.o: builtin.cc arg_spec.h rwsh_stream.h variable_map.h argm.h \
arg_script.h builtin.h call_stack.h clock.h command_stream.h \
executable.h executable_map.h file_stream.h pipe_stream.h plumber.h \
prototype.h read_dir.cc rwshlib.h selection.h substitution_stream.h \
tokenize.cc function.h
call_stack.o: call_stack.cc arg_spec.h rwsh_stream.h variable_map.h \
argm.h arg_script.h builtin.h call_stack.h executable.h executable_map.h \
prototype.h function.h
clock.o: clock.cc clock.h
command_stream.o: command_stream.cc arg_spec.h rwsh_stream.h \
variable_map.h argm.h arg_script.h call_stack.h clock.h command_stream.h \
executable.h executable_map.h prototype.h function.h
default_stream.o: default_stream.cc rwsh_stream.h default_stream.h
executable.o: executable.cc rwsh_stream.h variable_map.h argm.h \
call_stack.h clock.h executable.h executable_map.h plumber.h
executable_map.o: executable_map.cc arg_spec.h rwsh_stream.h \
variable_map.h argm.h arg_script.h call_stack.h executable.h \
executable_map.h prototype.h tokenize.cc function.h
file_stream.o: file_stream.cc rwsh_stream.h variable_map.h argm.h \
file_stream.h
function.o: function.cc arg_spec.h rwsh_stream.h variable_map.h argm.h \
arg_script.h call_stack.h clock.h executable.h executable_map.h \
prototype.h function.h
plumber.o: plumber.cc rwsh_stream.h variable_map.h argm.h call_stack.h \
clock.h plumber.h
rwsh.o: rwsh.cc arg_spec.h rwsh_stream.h variable_map.h argm.h \
arg_script.h call_stack.h clock.h command_stream.h default_stream.h \
executable.h executable_map.h plumber.h prototype.h rwsh_init.h \
selection.h function.h
rwsh_stream.o: rwsh_stream.cc rwsh_stream.h
rwshlib.o: rwshlib.cc rwshlib.h
pipe_stream.o: pipe_stream.cc rwsh_stream.h variable_map.h argm.h \
pipe_stream.h
prototype.o: prototype.cc arg_spec.h rwsh_stream.h variable_map.h argm.h \
arg_script.h executable.h prototype.h
selection.o: selection.cc rwsh_stream.h variable_map.h argm.h selection.h \
tokenize.cc
substitution_stream.o: substitution_stream.cc rwsh_stream.h plumber.h \
pipe_stream.h substitution_stream.h
variable_map.o: variable_map.cc rwsh_stream.h variable_map.h argm.h \
executable.h
rwsh_init.o: rwsh_init.cc arg_spec.h rwsh_stream.h variable_map.h \
arg_script.h argm.h rwsh_init.h builtin.h call_stack.h executable.h \
executable_map.h prototype.h function.h
......@@ -2,15 +2,12 @@
// external programs, the latter executes commands that are implemented by
// functions in builtin.cc.
//
// Copyright (C) 2005-2017 Samuel Newbold
// Copyright (C) 2005-2019 Samuel Newbold
#include <algorithm>
#include <cstdlib>
#include <list>
#include <map>
#include <set>
#include <string>
#include <signal.h>
#include <sys/time.h>
#include <unistd.h>
#include <vector>
......@@ -19,34 +16,18 @@
#include "variable_map.h"
#include "argm.h"
#include "builtin.h"
#include "call_stack.h"
#include "clock.h"
#include "executable.h"
#include "executable_map.h"
#include "plumber.h"
namespace {
Argm::Exception_t unix2rwsh(int sig) {
switch (sig) {
case SIGHUP: return Argm::Sighup;
case SIGINT: return Argm::Sigint;
case SIGQUIT: return Argm::Sigquit;
case SIGPIPE: return Argm::Sigpipe;
case SIGTERM: return Argm::Sigterm;
case SIGTSTP: return Argm::Sigtstp;
case SIGCONT: return Argm::Sigcont;
case SIGCHLD: return Argm::Sigchld;
case SIGUSR1: return Argm::Sigusr1;
case SIGUSR2: return Argm::Sigusr2;
default: return Argm::Sigunknown;}}
} // end unnamed namespace
int Base_executable::operator() (const Argm& argm,
Error_list& parent_exceptions) {
if (global_nesting > max_nesting+1)
if (global_stack.global_nesting > global_stack.max_nesting+1)
parent_exceptions.add_error(Exception(Argm::Excessive_nesting));
if (unwind_stack()) return Variable_map::dollar_question;
else ++executable_nesting, ++global_nesting;
if (global_stack.unwind_stack()) return 0;
else ++executable_nesting, ++global_stack.global_nesting;
Error_list children;
struct timeval start_time;
gettimeofday(&start_time, rwsh_clock.no_timezone);
......@@ -61,10 +42,10 @@ int Base_executable::operator() (const Argm& argm,
Clock::timeval_add(total_execution_time_v, last_execution_time_v);
++execution_count_v;
Variable_map::dollar_question = last_return = ret;
--global_nesting;
if (caught_signal) {
Exception focus(caught_signal);
caught_signal = Argm::No_exception;
--global_stack.global_nesting;
if (global_stack.caught_signal) {
Exception focus(global_stack.caught_signal);
global_stack.caught_signal = Argm::No_exception;
executable_map.run(focus, children);}
if (children.size()) {
last_exception_v = "";
......@@ -77,95 +58,6 @@ int Base_executable::operator() (const Argm& argm,
if (del_on_term && !executable_nesting) delete this;
return ret;}
void Base_executable::add_error(void) {
unwind_stack_v = true;
++current_exception_count;}
void Base_executable::reset(void) {
unwind_stack_v = false;
current_exception_count = 0;}
void Base_executable::unix_signal_handler(int sig) {
caught_signal = unix2rwsh(sig);}
/* code to call exception handlers, separated out of operator() for clarity.
The requirements for stack unwinding to work properly are as
follows: any code that runs more than one other executable must test
unwind_stack() in between each executable, which must be of an Executable
rather than a direct call to the function that implements a builtin (the
code below to run the fallback_handler is the only exception to this rule).
main does not need to do this handling, because anything that it calls
directly will have an initial nesting of 0.*/
void Base_executable::exception_handler(Error_list& exceptions) {
in_exception_handler = true;
unwind_stack_v = false;
std::set<std::string> failed_handlers;
for(;exceptions.size(); exceptions.pop_front(), --current_exception_count){
Argm& focus(exceptions.front());
if (failed_handlers.find(focus[0]) == failed_handlers.end()) {
executable_map.run(focus, exceptions);
if (unwind_stack()) {
failed_handlers.insert(focus[0]);
exceptions.insert(++exceptions.begin(), exceptions.back());
exceptions.pop_back();
unwind_stack_v = false;}}
if (failed_handlers.find(focus[0]) != failed_handlers.end())
b_fallback_handler(Argm(".fallback_handler", focus.argv(),
focus.argfunction(), Variable_map::global_map,
focus.input, focus.output, focus.error),
exceptions);}
Variable_map::dollar_question = -1;
dropped_catches = 0;
collect_excess_thrown = execution_handler_excess_thrown = false;
in_exception_handler = false;}
// code to call exception handlers when requested within a function
void Base_executable::catch_blocks(const Argm& argm, Error_list& exceptions) {
for (auto focus = exceptions.begin(); focus != exceptions.end();)
if (find(argm.begin() + 1, argm.end(), (*focus)[0]) != argm.end()) {
if (dropped_catches >= max_extra) {
if (!execution_handler_excess_thrown)
exceptions.add_error(
Exception(Argm::Excessive_exceptions_in_catch, max_extra));
execution_handler_excess_thrown = true;
return;}
unsigned previous_count = current_exception_count;
in_exception_handler = true;
unwind_stack_v = false;
executable_map.run(*focus, exceptions);
in_exception_handler = false;
dropped_catches += current_exception_count - previous_count;
if (!unwind_stack()) {
focus = exceptions.erase(focus);
--current_exception_count;}
else focus++;}
else focus++;
if (current_exception_count) unwind_stack_v = true;
else unwind_stack_v = false;
Variable_map::dollar_question = -1;
in_exception_handler = false;}
bool Base_executable::remove_exceptions(const std::string &name,
Error_list& exceptions) {
bool ret = false;
bool unwind_stack_before = unwind_stack_v;
for (auto focus = exceptions.begin(); focus != exceptions.end();)
if ((*focus)[0] == name) {
ret = true;
focus = exceptions.erase(focus);
--current_exception_count;}
else focus++;
if (unwind_stack_before && !exceptions.size()) unwind_stack_v = false;
return ret;}
// run one exception handler restoring unwind_stack afterwards
void Base_executable::catch_one(Argm& argm, Error_list& exceptions) {
in_exception_handler = true;
unwind_stack_v = false;
executable_map.run(argm, exceptions);
in_exception_handler = false;
unwind_stack_v = true;}
Binary::Binary(const std::string& impl) : implementation(impl) {}
// run the given binary
......@@ -185,7 +77,7 @@ int Binary::execute(const Argm& argm_i, Error_list& exceptions) const {
argm_i.export_env(env);
ret = execve(implementation.c_str(), argv.argv(), &env[0]);
exceptions.add_error(Exception(Argm::Binary_does_not_exist, argm_i[0]));
Named_executable::exception_handler(exceptions);
global_stack.exception_handler(exceptions);
executable_map.unused_var_check_at_exit();
exit(ret);}
else plumber.wait(&ret);
......
// Copyright (C) 2005-2017 Samuel Newbold
// Copyright (C) 2005-2018 Samuel Newbold
class Base_executable {
unsigned executable_nesting;
protected:
static int global_nesting;
static bool in_exception_handler;
static bool unwind_stack_v;
static bool collect_excess_thrown;
static unsigned current_exception_count;
static unsigned dropped_catches;
static bool execution_handler_excess_thrown;
public:
static Argm::Exception_t caught_signal;
bool del_on_term;
unsigned execution_count_v;
int last_return;
std::string last_exception_v;
struct timeval last_execution_time_v;
static unsigned max_collect;
static unsigned max_extra;
static int max_nesting;
struct timeval total_execution_time_v;
Base_executable(void) : executable_nesting(0), del_on_term(false),
......@@ -32,22 +19,13 @@ class Base_executable {
total_execution_time_v.tv_usec = 0;};
virtual ~Base_executable(void) {}
bool is_running(void) const {return !!executable_nesting;};
static void exception_handler(Error_list& exceptions);
static void catch_blocks(const Argm& argm, Error_list& exceptions);
static void catch_one(Argm& argm, Error_list& exceptions);
static bool remove_exceptions(const std::string &name,
Error_list& exceptions);
static void add_error(void);
unsigned execution_count(void) const {return execution_count_v;};
const std::string& last_exception(void) const {return last_exception_v;};
struct timeval last_execution_time(void) const {
return last_execution_time_v;};
int last_ret(void) const {return last_return;};
static void reset(void);
struct timeval total_execution_time(void) const {
return total_execution_time_v;};
static void unix_signal_handler(int sig);
static bool unwind_stack(void) {return unwind_stack_v;}
int operator() (const Argm& argm, Error_list& parent_exceptions);
virtual int execute(const Argm& argm, Error_list& exceptions) const = 0;
......
......@@ -17,6 +17,7 @@
#include "argm.h"
#include "arg_script.h"
#include "call_stack.h"
#include "executable.h"
#include "executable_map.h"
#include "prototype.h"
......@@ -59,23 +60,32 @@ bool Executable_map::run_if_exists(const std::string& key, Argm& argm_i) {
if (i) {
Error_list exceptions;
(*i)(temp_argm, exceptions);
if (exceptions.size()) Base_executable::exception_handler(exceptions);
if (global_stack.unwind_stack())
global_stack.exception_handler(exceptions);
return true;}
else {
return false;}}
int Executable_map::base_run(Argm& argm, Error_list& exceptions) {
int ret = run(argm, exceptions);
if (exceptions.size()) {
Base_executable::exception_handler(exceptions);
return -1;}
else return ret;}
if (gc_state.in_if_block && !gc_state.exception_thrown) {
gc_state.in_if_block = false;
exceptions.add_error(Exception(Argm::Unfinished_if_block));}
if (global_stack.unwind_stack())
global_stack.exception_handler(exceptions);
return ret;}
int Executable_map::run_handling_exceptions(Argm& argm, Error_list& exceptions){
int ret = run(argm, exceptions);
if (global_stack.unwind_stack())
global_stack.exception_handler(exceptions);
return ret;}
void Executable_map::unused_var_check_at_exit(void) {
Error_list exceptions;
Prototype shell_invocation(true);
shell_invocation.unused_var_check(Variable_map::global_map, exceptions);
if (exceptions.size()) Base_executable::exception_handler(exceptions);}
if (exceptions.size()) global_stack.exception_handler(exceptions);}
int Executable_map::run(Argm& argm, Error_list& exceptions) {
try {
......@@ -95,11 +105,11 @@ int Executable_map::run(Argm& argm, Error_list& exceptions) {
else not_found(argm, exceptions);}}
catch (Exception error) {
exceptions.add_error(error);
return -1;}}
return 0;}}
bool Executable_map::run_condition(Argm& argm, Error_list& exceptions) {
run(argm, exceptions);
return !Base_executable::remove_exceptions(".false", exceptions) &&
return !global_stack.remove_exceptions(".false", exceptions) &&
!exceptions.size();} // optional
int Executable_map::not_found(Argm& argm_i, Error_list& exceptions) {
......@@ -109,6 +119,6 @@ int Executable_map::not_found(Argm& argm_i, Error_list& exceptions) {
set(new Function(Argm::exception_names[Argm::Function_not_found],
prototype_argm.begin(), prototype_argm.end(), false,
"{.echo $cmd (: command not found) \\( $cmd $args$ \\);"
" .echo (\n)\n.return -1}", exceptions));}
" .echo (\n)}", exceptions));}
throw Exception(Argm::Function_not_found, argm_i);}
......@@ -20,6 +20,7 @@ class Executable_map : private std::map<std::string, Base_executable*> {
int run(Argm& argm, Error_list& exceptions); // doesn't catch unwind
bool run_condition(Argm& argm, Error_list& exceptions); // only .false
int base_run(Argm& argm, Error_list& exceptions); // catches unwind
int run_handling_exceptions(Argm& argm, Error_list& exceptions); // ditto
void unused_var_check_at_exit(void); // catches unwind
bool run_if_exists(const std::string& key, Argm& argm); // catches unwind
......
......@@ -19,6 +19,7 @@
#include "argm.h"
#include "arg_script.h"
#include "call_stack.h"
#include "clock.h"
#include "executable.h"
#include "executable_map.h"
......@@ -43,37 +44,14 @@ Command_block* Command_block::apply(const Argm& argm, unsigned nesting,
result->trailing = trailing;
return result;}}
int Command_block::collect_errors_core(const Argm& src_argm,
const std::vector<std::string>& exceptional,
bool logic, Error_list& parent_exceptions) {
int ret;
for (auto j: *this) {
if (current_exception_count > max_collect) {
if (!collect_excess_thrown)
parent_exceptions.add_error(
Exception(Argm::Excessive_exceptions_collected, max_collect));
unwind_stack_v = collect_excess_thrown = true;
return ret;}
Error_list children;
Argm statement_argm = j.interpret(src_argm, children);
ret = executable_map.run(statement_argm, children);
if (children.size()) {
unwind_stack_v = false;
for (auto k: children) {
parent_exceptions.push_back(k);
if (logic == (find(exceptional.begin(), exceptional.end(),
(k)[0]) != exceptional.end()))
unwind_stack_v = true;}}} // will cause subsequent j to not run
if (parent_exceptions.size()) unwind_stack_v = true;
return ret;}
int Command_block::execute(const Argm& src_argm,
Error_list& exceptions) const {
int ret;
for (auto j: *this) {
Argm statement_argm = j.interpret(src_argm, exceptions);
if (global_stack.unwind_stack()) break;
ret = executable_map.run(statement_argm, exceptions);
if (unwind_stack()) break;}
if (global_stack.unwind_stack()) break;}
return ret;}
int Command_block::prototype_execute(const Argm& argm,
......
# makefile for rwsh
objects = argm.o arg_script.o arg_spec.o builtin.o clock.o command_stream.o \
default_stream.o executable.o executable_map.o file_stream.o \
function.o plumber.o rwsh.o rwsh_stream.o rwshlib.o pipe_stream.o \
prototype.o selection.o substitution_stream.o variable_map.o
objects = argm.o arg_script.o arg_spec.o builtin.o call_stack.o clock.o \
command_stream.o default_stream.o executable.o executable_map.o \
file_stream.o function.o plumber.o rwsh.o rwsh_stream.o rwshlib.o \
pipe_stream.o prototype.o selection.o substitution_stream.o \
variable_map.o
local_objects = rwsh_init.o
CXX = g++-7
......@@ -16,51 +17,13 @@ rwsh: $(objects) $(local_objects)
$(CXX) $^ $(CXXFLAGS) $(LDLIBS) -o $@
librwsh.a: $(objects)
ar -rv $@ $(objects)
deps.mk: $(objects:o=cc) $(local_objects:o=cc)
gcc >$@ -MM $^
arg_script.o: arg_spec.h rwsh_stream.h argm.h arg_script.h executable.h \
file_stream.h function.h prototype.h variable_map.h
arg_spec.o: arg_spec.h rwsh_stream.h argm.h argm_star_var.cc arg_script.h \
executable.h executable_map.h function.h pipe_stream.h prototype.h \
read_dir.cc selection.h selection_read.cc substitution_stream.h \
tokenize.cc prototype.h variable_map.h
argm.o: arg_spec.h rwsh_stream.h argm.h arg_script.h executable.h function.h \
prototype.h variable_map.h
builtin.o: arg_spec.h rwsh_stream.h argm.h argm_star_var.cc arg_script.h \
builtin.h clock.h command_stream.h executable.h executable_map.h \
file_stream.h function.h plumber.cc pipe_stream.h prototype.h read_dir.cc \
selection.h substitution_stream.h tokenize.cc variable_map.h
clock.o: clock.h
command_stream.o: arg_spec.h rwsh_stream.h argm.h arg_script.h clock.h \
command_stream.h executable.h executable_map.h function.h prototype.h \
variable_map.h
default_stream.o: rwsh_stream.h default_stream.h
executable.o: rwsh_stream.h argm.h builtin.h clock.o executable.h \
executable_map.h plumber.h variable_map.h
executable_map.o: arg_spec.h rwsh_stream.h argm.h arg_script.h executable.h \
executable_map.h function.h prototype.h
file_stream.o: rwsh_stream.h argm.h file_stream.h variable_map.h
function.o: arg_spec.h rwsh_stream.h argm.h arg_script.h clock.h executable.h \
executable_map.h function.h prototype.h variable_map.h
plumber.o: rwsh_stream.h argm.h clock.h executable.h plumber.h variable_map.h
prototype.o: arg_spec.h rwsh_stream.h argm.h arg_script.h executable.h \
prototype.h variable_map.h
rwsh.o: arg_spec.h rwsh_stream.h argm.h argm_star_var.cc arg_script.h \
clock.o command_stream.h default_stream.h executable.h \
executable_map.h function.h plumber.h prototype.h rwsh_init.h \
selection.h variable_map.h
rwsh_init.o: arg_spec.h rwsh_stream.h arg_script.h argm.h rwsh_init.h \
builtin.h executable.h executable_map.h function.h prototype.h
rwsh_stream.o: rwsh_stream.h
rwshlib.o: rwshlib.h
selection.o: rwsh_stream.h argm.h selection.h tokenize.cc variable_map.h
substitution_stream.o: argm.h rwsh_stream.h pipe_stream.h plumber.h \
substitution_stream.h variable_map.h
pipe_stream.o: argm.h rwsh_stream.h variable_map.h
variable_map.o: arg_spec.h rwsh_stream.h argm.h arg_script.h executable.h \
variable_map.h
include deps.mk
.PHONY: all clean dist
all: rwsh librwsh.a
all: rwsh librwsh.a deps.mk
clean:
rm *.o
dist:
......
......@@ -16,8 +16,8 @@
#include "variable_map.h"
#include "argm.h"
#include "call_stack.h"
#include "clock.h"
#include "executable.h"
#include "plumber.h"
class read_handler :
......@@ -30,7 +30,7 @@ class read_handler :
buffer(buffer_i), buffer_size(buffer_size_i) {};
bool operator()(const std::pair<Rwsh_istream*, Rwsh_ostream*>& focus) {
int n = focus.first->read(buffer, buffer_size);
if (n <= 0 || Named_executable::unwind_stack()) {
if (n <= 0 || global_stack.unwind_stack()) {
focus.first->close();
return true;}
else {
......@@ -59,7 +59,7 @@ void Plumber::wait(int *ret) {
output_handlers.end(),
read_handler(buffer, sizeof buffer)));
output_handlers.clear();
if (Named_executable::unwind_stack()) return;
if (global_stack.unwind_stack()) return;
struct timeval before, after;
gettimeofday(&before, rwsh_clock.no_timezone);
int wait_return = ::wait(ret);
......
......@@ -7,7 +7,6 @@
#include <list>
#include <map>
#include <set>
#include <signal.h>
#include <string>
#include <sys/time.h>
#include <vector>
......@@ -18,6 +17,7 @@
#include "argm.h"
#include "arg_script.h"
#include "call_stack.h"
#include "clock.h"
#include "command_stream.h"
#include "default_stream.h"
......@@ -32,18 +32,9 @@
// static initializers of basic types
const char* WSPACE = " \t";
bool Base_executable::collect_excess_thrown = false;
unsigned Base_executable::max_collect = 1;
unsigned Base_executable::max_extra = 1;
bool Base_executable::execution_handler_excess_thrown = false;
struct timezone Clock::no_timezone_v = {0, 0};
int Base_executable::global_nesting(0);
bool Base_executable::unwind_stack_v(false);
bool Base_executable::in_exception_handler(false);
unsigned Base_executable::current_exception_count(0);
unsigned Base_executable::dropped_catches(0);
std::string Argm::exception_names[Argm::Exception_count] = {
"no exception",
".nop",
".ambiguous_prototype_dash_dash",
".arguments_for_argfunction",
".autofunction",
......@@ -53,6 +44,8 @@ std::string Argm::exception_names[Argm::Exception_count] = {
".bad_if_nest",
".binary_does_not_exist",
".binary_not_found",
".break",
".continue",
".dash_dash_argument",
".dash_star_argument",
".directory_not_found",
......@@ -80,6 +73,7 @@ std::string Argm::exception_names[Argm::Exception_count] = {
".illegal_variable_name",
".input_range",
".internal_error",
".interrupted_sleep",
".invalid_word_selection",
// ".line_continuation",
".mismatched_brace",
......@@ -90,6 +84,7 @@ std::string Argm::exception_names[Argm::Exception_count] = {
".not_a_directory",
".not_a_function",
".not_a_number",
".not_catching_exception",
".not_executable",
".not_soon_enough",
".raw_command",
......@@ -115,6 +110,7 @@ std::string Argm::exception_names[Argm::Exception_count] = {
".unchecked_variable",
".undeclared_variable",
".undefined_variable",
".unfinished_if_block",
".unreadable_dir",
".unrecognized_flag",
".unused_before_set",
......@@ -122,35 +118,20 @@ std::string Argm::exception_names[Argm::Exception_count] = {
".variable_already_exists",
".version_incompatible"};
bool readline_enabled = false;
Variable_map root_variable_map(nullptr);
int Base_executable::max_nesting = 0;
int Variable_map::dollar_question = -1;
bool Variable_map::exit_requested = false;
// static initializers without dependancies
Call_stack global_stack;
Clock rwsh_clock;
Conditional_state gc_state;
Executable_map executable_map;
Plumber plumber;
Rwsh_istream_p default_input(new Default_istream(0), true, true);
Rwsh_ostream_p default_output(new Default_ostream(1), true, true),
default_error(new Default_ostream(2), true, true);
Variable_map root_variable_map(nullptr);
Variable_map* Variable_map::global_map = &root_variable_map;
// static initializers with cross-component dependancies
Argm::Exception_t Base_executable::caught_signal = Argm::No_exception;
namespace {
void register_signals(void) {
signal(SIGHUP, Named_executable::unix_signal_handler);
signal(SIGINT, Named_executable::unix_signal_handler);
signal(SIGQUIT, Named_executable::unix_signal_handler);
signal(SIGPIPE, Named_executable::unix_signal_handler);
signal(SIGTERM, Named_executable::unix_signal_handler);
signal(SIGTSTP, Named_executable::unix_signal_handler);
signal(SIGUSR1, Named_executable::unix_signal_handler);
signal(SIGUSR2, Named_executable::unix_signal_handler);}
} // end unnamed namespace
int main(int argc, char *argv[]) {
Error_list exceptions;
try {internal_init(exceptions);} // catch blocks untestable
......@@ -173,13 +154,14 @@ int main(int argc, char *argv[]) {
try {
command_stream.getline(script, exceptions);
if (exceptions.size()) {
Base_executable::exception_handler(exceptions);
global_stack.exception_handler(exceptions);
continue;}
else if (command_stream.fail()) break;
else command = script.base_interpret(script.argm(), exceptions);}
else command = script.interpret(script.argm(), exceptions);}
catch (Exception exception) {command = exception;}
executable_map.run_if_exists(".before_command", command);
if (!executable_map.run_if_exists(".run_logic", command))
if (exceptions.size()) global_stack.exception_handler(exceptions);
else if (!executable_map.run_if_exists(".run_logic", command))
executable_map.base_run(command, exceptions);
executable_map.run_if_exists(".after_command", command);}
Argm shutdown_command(Argm::exception_names[Argm::Shutdown],
......@@ -187,4 +169,4 @@ int main(int argc, char *argv[]) {
default_input, default_output, default_error);
executable_map.base_run(shutdown_command, exceptions);
executable_map.unused_var_check_at_exit();
return Variable_map::dollar_question;}
return global_stack.exit_value();}
......@@ -4,6 +4,7 @@
#include <list>
#include <map>
#include <set>
#include <signal.h>
#include <string>
#include <vector>
......@@ -15,6 +16,7 @@
#include "argm.h"
#include "rwsh_init.h"
#include "builtin.h"
#include "call_stack.h"
#include "executable.h"
#include "executable_map.h"
#include "prototype.h"
......@@ -83,15 +85,11 @@ void internal_init(Error_list& exceptions) {
" .else {.echo .help not defined (\n)}}", exceptions));
executable_map.set(new Function(".internal_features",
empty_prototype.begin(), empty_prototype.end(), false,
"{.if .test_number_equal $# 1 {"
".echo .after_command .before_command .run_logic}; "
".else {.echo wrong argument count; .return -1}}", exceptions));
"{.echo (.after_command .before_command .run_logic\n)}", exceptions));
executable_map.set(new Builtin(".internal_functions", b_internal_functions));
executable_map.set(new Function(".internal_vars",
empty_prototype.begin(), empty_prototype.end(), false,
"{.if .test_number_equal $# 1 {"
".echo FIGNORE ?}; "
".else {.echo wrong argument count; .return -1}}", exceptions));
"{.echo (FIGNORE ?\n)}", exceptions));
executable_map.set(new Builtin(".is_default_input", b_is_default_input));
executable_map.set(new Builtin(".is_default_output", b_is_default_output));
executable_map.set(new Builtin(".is_default_error", b_is_default_error));
......@@ -105,6 +103,7 @@ void internal_init(Error_list& exceptions) {
executable_map.set(new Builtin(".local", b_local));
executable_map.set(new Builtin(".ls", b_ls));
executable_map.set(new Builtin(".nop", b_nop));
executable_map.set(new Builtin(".replace_exception", b_replace_exception));
executable_map.set(new Builtin(".return", b_return));
executable_map.set(new Builtin(".rm_executable", b_rm_executable));
executable_map.set(new Builtin(".scope", b_scope));
......@@ -117,6 +116,9 @@ void internal_init(Error_list& exceptions) {
executable_map.set(new Builtin(".set_max_extra_exceptions",
b_set_max_extra_exceptions));
executable_map.set(new Builtin(".set_max_nesting", b_set_max_nesting));
executable_map.set(new Function(".shutdown",
any_args.begin(), any_args.end(), false,
"{.nop $args; .exit 10}", exceptions));
executable_map.set(new Builtin(".source", b_source));
executable_map.set(new Builtin(".stepwise", b_stepwise));
executable_map.set(new Builtin(".store_output", b_store_output));
......@@ -155,3 +157,29 @@ void internal_init(Error_list& exceptions) {
executable_map.set(new Builtin(".version", b_version));
executable_map.set(new Builtin(".version_compatible", b_version_compatible));}
inline Argm::Exception_t unix2rwsh(int sig) {
switch (sig) {
case SIGHUP: return Argm::Sighup;
case SIGINT: return Argm::Sigint;
case SIGQUIT: return Argm::Sigquit;
case SIGPIPE: return Argm::Sigpipe;
case SIGTERM: return Argm::Sigterm;
case SIGTSTP: return Argm::Sigtstp;
case SIGCONT: return Argm::Sigcont;
case SIGCHLD: return Argm::Sigchld;
case SIGUSR1: return Argm::Sigusr1;
case SIGUSR2: return Argm::Sigusr2;
default: return Argm::Sigunknown;}}
void unix_signal_handler(int sig) {
global_stack.caught_signal = unix2rwsh(sig);}
void register_signals(void) {
signal(SIGHUP, unix_signal_handler);
signal(SIGINT, unix_signal_handler);
signal(SIGQUIT, unix_signal_handler);
signal(SIGPIPE, unix_signal_handler);
signal(SIGTERM, unix_signal_handler);
signal(SIGTSTP, unix_signal_handler);
signal(SIGUSR1, unix_signal_handler);
signal(SIGUSR2, unix_signal_handler);}
// Copyright (C) 2005-2018 Samuel Newbold
void internal_init(Error_list& exceptions);
void register_signals(void);
......@@ -16,7 +16,7 @@
.global SHELL /bin/rwsh
.global TESTABILITY (an=external var(iable))
.exec ./rwsh test_init.sh
.exit}
.exit 0}
.else_if .test_number_equal $# 3 {
.nop .source /etc/rwshrc-default
.source &2}
......@@ -29,11 +29,13 @@
.nop $$var}}}
.source /etc/rwshrc-basic
.nop $FIGNORE
.function_all_flags .if_before_else stack ... {.nop $stack}
.function_all_flags .shutdown -- args ... {.nop $args; .return 0}
.function_all_flags .shutdown -- [args ...] {.nop $args}
.function_all_flags .return_code code cmd [add_stack ...] {
echo $cmd returned $code : call stack $cmd $add_stack$
.exit $code}
.collect_errors_except .nop {
.mapped_argfunction &3 {$1$}
.exit}}
.exit 0}}
.else {
.echo unexpected command line: &* (
)
......
.function_all_flags # -- [ignored ...] {.nop $ignored$}
.function_all_flags fn -- prototype ... {
.function_all_flags $prototype$ {.argfunction}}
# set up environment
.set_max_collectible_exceptions 10
.set_max_extra_exceptions 5
.global nl (
)
.if .var_exists ?? {}
.else {.global ?? (); .nop $??}
# internal functions
.function_all_flags .ambiguous_prototype_dash_dash prototype stack ... {
......@@ -37,6 +41,12 @@
.function_all_flags .binary_not_found name path stack ... {
echo $name : binary not found in $path
echo call stack: $stack$}
.function_all_flags .break stack ... {
echo .break thrown outside of control flow body
echo call stack: $stack$}
.function_all_flags .continue stack ... {
echo .continue thrown outside of control flow body
echo call stack: $stack$}
.function_all_flags .dash_dash_argument args stack ... {
echo optional -- parameter cannot take arguments \( $args \) with call stack: $stack$}
.function_all_flags .dash_star_argument args stack ... {
......@@ -78,7 +88,7 @@
echo call stack $stack}
.function_all_flags .failed_substitution substitution [stack ...] {
echo substitution $substitution failed
echo call stack $stack$}
if_only .var_exists stack {echo call stack: $stack$}}
.function_all_flags .false test [stack ...] {
echo assertion $test failed
echo call stack $stack$}
......@@ -113,8 +123,10 @@
echo value $value is out of usable numeric range
echo call stack $stack$}
.function_all_flags .internal_error -- [args ...] {
echo internal error: $args$
.return -1}
echo internal error: $args$}
.function_all_flags .interrupted_sleep [stack ...] {
echo sleep was interrupted
echo call stack $stack$}
.function_all_flags .invalid_word_selection selection [stack ...] {
echo $selection is not a valid word selection
echo call stack $stack$}
......@@ -126,8 +138,7 @@
.function .mismatched_parenthesis {
.echo signal triggered: $0 \( $* \); .combine $nl
echo mismatched parenthesis: $1
echo call stack: $*2
.return -1}
echo call stack: $*2}
.function_all_flags .missing_argfunction cmd [add_stack ...] {
echo $cmd requires an argfunction
if_only .var_exists add_stack {echo stack $add_stack$}}
......@@ -142,6 +153,9 @@
.function_all_flags .not_a_number -- nan stack ... {
echo $nan is not a number
echo call stack: $stack$}
.function_all_flags .not_catching_exception stack ... {
echo .replace_exception called outside of an exception handler
echo call stack: $stack$}
.function_all_flags .not_executable file stack ... {
echo $file is not executable
echo call stack: $stack$}
......@@ -158,38 +172,42 @@
echo result of $op with arguments $lhs and $rhs is out of range
echo call stack: $op $stack$}
.function_all_flags .return_code code cmd [add_stack ...] {
echo $cmd returned $code : call stack $cmd $add_stack$}
.if .test_in $cmd /usr/bin/test /bin/false {
.replace_exception .false $cmd
.nop $code $add_stack$}
.else {
.set ?? $cmd returned $code
echo $??$ : call stack $cmd $add_stack$}}
.function_all_flags .selection_not_found not_found selection [stack ...] {
.echo no file matching pattern $not_found
if_only .test_string_unequal $not_found $selection {
.echo () for selection $selection}
.echo $nl
if_only .var_exists stack {echo call stack: $stack$}
.return -1}
.function_all_flags .sigcont [args ...] {
echo received SIGCONT: $args$; .return -1}
.function_all_flags .sigchld [args ...] {
echo received SIGCHLD: $args$; .return -1}
.function_all_flags .sighup [args ...] {
echo received SIGHUP: $args$; .return -1}
.function_all_flags .sigint [args ...] {
echo received SIGINT: $args$; .return -1}
.function_all_flags .sigquit [args ...] {
echo received SIGQUIT: $args$; .return -1}
.function_all_flags .sigpipe [args ...] {
echo received SIGPIPE: $args$; .return -1}
.function_all_flags .sigterm [args ...] {
echo received SIGTERM: $args$; .return -1}
.function_all_flags .sigtstp [args ...] {
echo received SIGTSTP: $args$; .return -1}
.function_all_flags .siginfo [args ...] {
echo received SIGINFO: $args$; .return -1}
.function_all_flags .sigusr1 [args ...] {
echo received SIGUSR1: $args$; .return -1}
.function_all_flags .sigusr2 [args ...] {
echo received SIGUSR2: $args$; .return -1}
.function_all_flags .sigunknown [args ...] {
echo received unknown unix signal: $args$; .return -1}
if_only .var_exists stack {echo call stack: $stack$}}
.function_all_flags .sigcont {
echo received SIGCONT}
.function_all_flags .sigchld {
echo received SIGCHLD}
.function_all_flags .sighup {
echo received SIGHUP}
.function_all_flags .sigint {
.throw echo $nl received SIGINT}
.function_all_flags .sigquit {
echo received SIGQUIT}
.function_all_flags .sigpipe {
echo received SIGPIPE}
.function_all_flags .sigterm {
.throw echo $nl received SIGTERM}
.function_all_flags .sigtstp {
echo received SIGTSTP}
.function_all_flags .siginfo {
echo received SIGINFO}
.function_all_flags .sigusr1 {
echo received SIGUSR1}
.function_all_flags .sigusr2 {
echo received SIGUSR2}
.function_all_flags .sigunknown {
echo received unknown unix signal}
.function_all_flags .shutdown -- args ... {
.nop $args
.combine $nl (now terminating normally) $nl}
......@@ -204,10 +222,12 @@
.function_all_flags .undefined_variable -- name [stack ...] {
echo optional variable $name was not defined
echo call stack: $stack$}
.function_all_flags .unfinished_if_block [stack ...] {
echo conditional not finished within preceeding block
if_only .var_exists stack {echo call stack: $stack$}}
.function_all_flags .unreadable_dir dir errno [stack ...] {
.combine (cannot read directory ) $dir \ \(errno\ $errno \) $nl
echo call stack: $stack$
.return -1}
echo call stack: $stack$}
.function_all_flags .unrecognized_flag -- flag cmd [add_stack ...] {
echo $flag flag is unrecognized for $cmd
echo call stack: $cmd $add_stack$}
......@@ -230,16 +250,18 @@
.function_all_flags af -- [argv ...] {.argfunction}
.function_all_flags c -- text ... {.combine $text$}
.function_all_flags e -- text ... {.echo $text}
.function_all_flags do_while argv ... {
.try_catch_recursive .break {
.nop $argv
.try_catch_recursive .continue {.argfunction}
.while $argv$ {.argfunction}}}
.function_all_flags echo -- text ... {.echo $text; .combine $nl}
.function_all_flags echo-comments cmd ... {
.stepwise $cmd$ {if_only .test_in $1 .nop # #! ## {echo &&&*}}}
.function_all_flags exec -- argv ... {.exec $argv$}
.function_all_flags elif command ... {.else_if $command$ {.argfunction}}
.function_all_flags else {.else {.argfunction}}
.function_all_flags fn -- prototype ... {
.function_all_flags $prototype$ {.argfunction}}
.function_all_flags fni -- name args ... {
.function_all_flags $name -- $args$ {.argfunction}}
fn false {.try_catch_recursive .return_code {${whichp false}}}
.function_all_flags fns -- name args ... {
.function_all_flags $name [-*] $args$ {.argfunction}}
.function_all_flags if_only -- args ... {
......@@ -250,22 +272,44 @@ fn ntimes n {
.while test_var_greater n 0 {
.mapped_argfunction {.argfunction}
.var_subtract n 1}}
.function_all_flags outer_break stack ... {
.replace_exception .break $stack$}
.function_all_flags outer_continue stack ... {
.replace_exception .continue $stack$}
.function_all_flags scope_for var vals ... {
.for $vals$ {
.scope $1 $var {.argfunction}}}
.function_all_flags outer_for var vals ... {
.for $vals$ {
.scope $1 $var {
.try_catch_recursive outer_break outer_continue {.argfunction}}}}
.function_all_flags outer_while argv ... {
.while $argv$ {
.try_catch_recursive outer_break outer_continue {.argfunction}}}
.function_all_flags setf var value {
.if .var_exists $var {.set $var $value}
.else {.global $var $value}}
fn single -- args ... {.scope ${$args$}$ result {echo $result}}
fn test -- args ... {
.try_catch_recursive .return_code {${whichp test} $args$}}
fn test_var_not_equal var value {.test_string_unequal $$var $value}
fn test_var_greater -- var value {.test_greater $$var $value}
fn type [-t] args ... {
.for $args$ {
.try_catch_recursive .function_not_found {
if_only_not .var_exists -t {.echo &&&1 is ()}
.combine &&&&{.type &&&1 {.argfunction}} $nl}}}
.type &&&1 {.argfunction}}}}
fn whence command {
.try_catch_recursive .function_not_found {
.combine ${.whence_function $command {.argfunction}} $nl}}
.whence_function $command {.argfunction}}}
fn whichp binary {.which_path $binary $PATH}
.function_all_flags while_and_one_more argv ... {
.try_catch_recursive .break {
.while $argv$ {.argfunction}
.try_catch_recursive .continue {.argfunction}}}
.function_all_flags var_exists -- var {.var_exists $var}
.function_all_flags var_in var set ... {.test_in $$var $set$}
.function_all_flags var_less var N {.test_less $$var $N}
.function_all_flags var_val -- [var ...] {
.if .var_exists var {.for $var$ {.combine $1 \( $$1 \) \ }}
.else {.nop}}
......
......@@ -170,11 +170,10 @@ fn unselect {.set s ()}
.function_all_flags . -- command ... {.source $command$}
.function_all_flags ex_count -- cmd {.execution_count $cmd; c $nl}
.function_all_flags exec -- command ... {.exec $command$ }
.function_all_flags exit {.exit}
.function_all_flags exit value {.exit $value}
.function_all_flags last_time cmd {.last_execution_time $cmd; c $nl}
fn local {. &{c $HOME /.rwshrc}}
fn mark {ntimes 4 {.echo ####################}; c $nl}
fn ret {.return $*}
.function_all_flags timings cmd {
.echo last time (); .last_execution_time $cmd; c $nl
.echo total time (); .total_execution_time $cmd; c $nl
......@@ -188,23 +187,22 @@ fn ret {.return $*}
echo for user ${.waiting_for_user}
echo for binary ${.waiting_for_binary}
echo for shell ${.waiting_for_shell}}
fn ist [-?] [-nc] {
fn st-core [-?] [-nc] binary filter {
if_only .var_exists -nc {.set -? $-?$ --suppress-common-lines}
.collect_errors_except .nop {
/bin/rwsh -to <test_main.sh >last.test.result
$binary -to <test_main.sh >last.test.result
.if .test_not_empty $-? {
&{whichp diff} $-?$ test.result last.test.result}
.else {&{whichp diff} -c test.result last.test.result}}}
fn st [-?] [-nc] {
if_only .var_exists -nc {.set -? $-?$ --suppress-common-lines}
.collect_errors_except .nop {
to <test_main.sh >last.test.result
.if .test_not_empty $-? {
&{whichp diff} $-?$ test.result last.test.result}
.else {&{whichp diff} -c test.result last.test.result}}}
fn bless-t {&{whichp cp} last.test.result test.result}
fn to {./rwsh -to}
&{whichp diff} $-?$ test.result last.test.result >last.test.result.diff}
.else {
&{whichp diff} -c test.result last.test.result >last.test.result.diff}
$filter$ last.test.result.diff}}
fn mst [-?] {make; st $-*$}
fn to {./rwsh -to}
fn st [-?] {st-core $-*$ ./rwsh /bin/cat}
fn ist [-?] {st-core $-*$ /bin/rwsh /bin/cat}
fn st-diffstat [-?] {st-core $-*$ ./rwsh &{whichp diffstat}}
fn st-vis [-?] {st-core $-*$ ./rwsh (/bin/cat -A)}
fn bless-t {&{whichp cp} last.test.result test.result}
fn ti [-?] [-nc] {
if_only .var_exists -nc {.set -? $-?$ --suppress-common-lines}
.collect_errors_except .nop {
......
......@@ -54,7 +54,7 @@ bool Simple_pattern::match(const std::string& s) const {
if (j == std::string::npos) return false;
j += cur_term.length();}
auto lstart = s.size() - last.size();
if (only_text && j != lstart ||
if ((only_text && j != lstart) ||
j + last.size() > s.size() ||
s.substr(lstart) != last) return false; // C++20 ends_with
else return true;}
......@@ -73,18 +73,18 @@ Entry_pattern::Entry_pattern(const std::string& src) {
for (auto j: temp) options.push_back(Simple_pattern(j));
only_text = options.size() == 1 && options[0].is_only_text();}
// you still can't encapsulate outer_for in a function....
class OuterContinue : public std::exception {};
bool Entry_pattern::match_with_ignore(const Entry_pattern& ignore_pattern,
const std::string& s) const {
for (auto j: options) if (j.match(s))
if (ignore_pattern.specific_match(s, j)) continue;
else return true;
return false;}
// My kingdom for a better continue!
bool Entry_pattern::specific_match(const std::string& s,
const Simple_pattern& competition) const {
for (auto j: options) if (j.match(s) && j.isnt_contained(competition))
return true;
for (auto competition: options) if (competition.match(s))
try {
for (auto j: ignore_pattern.options)
if (j.match(s) && j.isnt_contained(competition)) throw OuterContinue();
return true;}
catch (OuterContinue) {}
else;
return false;}
std::string Entry_pattern::str(void) const {
......@@ -103,8 +103,7 @@ void str_to_entry_pattern_list(const std::string& src,
if (src.empty());
else if (*i == "") {
res.clear();
res.push_back(*i);
;}
res.push_back(*i);}
else if (*i == "..")
if (res.size() > 1) {res.pop_back(); res.pop_back();}
else {res.clear(); ++updir;}
......
This diff is collapsed.
echo before error
echo the error ${.throw echo nonexistent} was there
echo after error
#! /bin/rwsh
.global nl (
)
.for &{.internal_functions}$ {.nop
.function_all_flags $1 -- [args ...] {.echo signal triggered: $0 \( $args$ \); .combine $nl; .return -1}}
.function_all_flags .shutdown -- [args ...] {.nop $args; .exit 255}
This diff is collapsed.
.source /etc/rwshrc-basic
.global PATH /bin:/usr/bin
.getpid
.getppid
.scope () {.getpid; .echo $nl}
.scope () {.getppid; .echo $nl}
.function_all_flags timings focus {
.combine $focus (: last\() ${.last_execution_time $focus} \) \
.combine (total\() ${.total_execution_time $focus} \) \
......
......@@ -20,7 +20,8 @@
.function_all_flags .after_command -- args ... {
.nop $args
.set last_command_return $?
.combine $nl $last_command_return $nl}
if_only .test_string_unequal $last_command_return 0 {