Commit da07be75 authored by Samuel Newbold's avatar Samuel Newbold

a thrown exception triggers .failed_substitution

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.
parent c843b411
......@@ -221,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()) global_stack.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,10 +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)) {
exceptions.add_error(Exception(Argm::Failed_substitution, str()));
return res;}
else 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;
......@@ -243,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 {
......
......@@ -24,12 +24,16 @@ call stack .source .try_catch_recursive(body)
.function_all_flags .vars {.internal_vars}
.source /etc/rwshrc-basic
.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 ... {
......@@ -109,7 +113,7 @@ call stack .source .try_catch_recursive(body)
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$}
......@@ -144,8 +148,10 @@ call stack .source .try_catch_recursive(body)
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$}
......@@ -157,8 +163,7 @@ call stack .source .try_catch_recursive(body)
.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$}}
......@@ -192,38 +197,42 @@ call stack .source .try_catch_recursive(body)
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}
if_only .var_exists stack {echo call stack: $stack$}}
.function_all_flags .sigcont {
echo received SIGCONT; .return -1}
echo received SIGCONT}
.function_all_flags .sigchld {
echo received SIGCHLD; .return -1}
echo received SIGCHLD}
.function_all_flags .sighup {
echo received SIGHUP; .return -1}
echo received SIGHUP}
.function_all_flags .sigint {
.throw echo $nl received SIGINT}
.function_all_flags .sigquit {
echo received SIGQUIT; .return -1}
echo received SIGQUIT}
.function_all_flags .sigpipe {
echo received SIGPIPE; .return -1}
echo received SIGPIPE}
.function_all_flags .sigterm {
.throw echo $nl received SIGTERM}
.function_all_flags .sigtstp {
echo received SIGTSTP; .return -1}
echo received SIGTSTP}
.function_all_flags .siginfo {
echo received SIGINFO; .return -1}
echo received SIGINFO}
.function_all_flags .sigusr1 {
echo received SIGUSR1; .return -1}
echo received SIGUSR1}
.function_all_flags .sigusr2 {
echo received SIGUSR2; .return -1}
echo received SIGUSR2}
.function_all_flags .sigunknown {
echo received unknown unix signal; .return -1}
echo received unknown unix signal}
.function_all_flags .shutdown -- args ... {
.nop $args
.combine $nl (now terminating normally) $nl}
......@@ -243,8 +252,7 @@ call stack .source .try_catch_recursive(body)
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$}
......@@ -278,10 +286,7 @@ call stack .source .try_catch_recursive(body)
.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 ... {
......@@ -310,6 +315,8 @@ fn ntimes n {
.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 ... {
......
......@@ -66,6 +66,7 @@ class Argm {
Illegal_variable_name,
Input_range,
Internal_error,
Interrupted_sleep,
Invalid_word_selection,
// Line_continuation,
Mismatched_brace,
......
......@@ -203,7 +203,7 @@ int b_fallback_handler(const Argm& argm, Error_list& exceptions) {
int b_for(const Argm& argm, Error_list& exceptions) {
if (argm.argc() < 2) throw Exception(Argm::Bad_argc, argm.argc()-1, 1, 0);
if (!argm.argfunction()) throw Exception(Argm::Missing_argfunction);
int ret = -1;
int ret = 0;
Argm body(argm.parent_map(), argm.input, argm.output, argm.error);
body.push_back(".mapped_argfunction");
body.push_back("");
......@@ -212,8 +212,8 @@ int b_for(const Argm& argm, Error_list& exceptions) {
body[1] = i;
ret = (*argm.argfunction())(body, exceptions);
(void) global_stack.remove_exceptions(".continue", exceptions);
if (global_stack.remove_exceptions(".break", exceptions)) return 0;
else if (global_stack.unwind_stack()) return -1;}
if (global_stack.remove_exceptions(".break", exceptions) ||
global_stack.unwind_stack()) return 0;}
else ret = 0;}
return ret;}
......@@ -222,7 +222,7 @@ int b_for(const Argm& argm, Error_list& exceptions) {
int b_for_each_line(const Argm& argm, Error_list& exceptions) {
if (argm.argc() != 1) throw Exception(Argm::Bad_argc, argm.argc()-1, 0, 0);
if (!argm.argfunction()) throw Exception(Argm::Missing_argfunction);
int ret = -1;
int ret = 0;
while(!argm.input.fail()) {
std::string line;
// shouldn't interfere with input being consumed by this builtin
......@@ -234,8 +234,8 @@ int b_for_each_line(const Argm& argm, Error_list& exceptions) {
std::bind2nd(std::equal_to<char>(), ' '));
ret = (*argm.argfunction())(body, exceptions);
(void) global_stack.remove_exceptions(".continue", exceptions);
if (global_stack.remove_exceptions(".break", exceptions)) return 0;
else if (global_stack.unwind_stack()) return -1;}
if (global_stack.remove_exceptions(".break", exceptions) ||
global_stack.unwind_stack()) return 0;}
return ret;}
int b_fork(const Argm& argm, Error_list& exceptions) {
......@@ -247,9 +247,12 @@ int b_fork(const Argm& argm, Error_list& exceptions) {
Argm lookup(argm.subrange(1), argm.argfunction(),
argm.parent_map(),
argm.input, argm.output.child_stream(), argm.error);
status = executable_map.base_run(lookup, exceptions);
status = executable_map.run(lookup, exceptions);
if (global_stack.unwind_stack()) {
global_stack.exception_handler(exceptions);
if (!global_stack.exit_value()) global_stack.request_exit(-1);}
executable_map.unused_var_check_at_exit();
std::exit(status);}
std::exit(global_stack.exit_value());}
else plumber.wait(&status);
if (WIFEXITED(status) && WEXITSTATUS(status))
exceptions.add_error(Exception(Argm::Return_code, WEXITSTATUS(status)));
......@@ -343,7 +346,7 @@ namespace {
int if_core(const Argm& argm, Error_list& exceptions,
Conditional_state& state, bool logic, bool is_else) {
if (!state.in_if_block) throw Exception(Argm::Else_without_if);
else if (state.successful_condition) return Variable_map::dollar_question;
else if (state.successful_condition) return 0;
else {
int ret = 0;
Argm lookup(argm.subrange(1), nullptr, argm.parent_map(),
......@@ -604,10 +607,8 @@ int b_scope(const Argm& argm, Error_list& exceptions) {
Prototype prototype(prototype_argm.begin(), prototype_argm.end(), false);
Argm invoking_argm(argm.subrange(0, 1), nullptr, argm.parent_map(),
argm.input, argm.output, argm.error);
int ret = (*argm.argfunction()).prototype_execute(invoking_argm, prototype,
exceptions);
if (global_stack.unwind_stack()) return -1;
else return ret;}
return (*argm.argfunction()).prototype_execute(invoking_argm, prototype,
exceptions);}
// modify variable $1 as a selection according to $2
int b_selection_set(const Argm& argm, Error_list& exceptions) {
......@@ -681,7 +682,6 @@ int b_set_max_nesting(const Argm& argm, Error_list& exceptions) {
// run the first argument as if it was a script, passing additional arguments
// to that script
// returns last return value from script, -1 if empty
int b_source(const Argm& argm, Error_list& exceptions) {
if (argm.argc() < 2) throw Exception(Argm::Bad_argc, argm.argc()-1, 1, 0);
if (argm.argfunction()) throw Exception(Argm::Excess_argfunction);
......@@ -694,12 +694,13 @@ int b_source(const Argm& argm, Error_list& exceptions) {
argm.input, argm.output.child_stream(), argm.error);
Command_stream command_stream(src, false);
Arg_script script("", 0, exceptions);
int ret = -1;
int ret = 0;
while (!command_stream.fail() && !global_stack.unwind_stack())
try {
command_stream.getline(script, exceptions);
if (command_stream.fail()) break;
Argm command(script.interpret(script_arg, exceptions));
if (global_stack.unwind_stack()) break;
ret = executable_map.run(command, exceptions);}
catch (Exception exception) {exceptions.add_error(exception);}
return ret;}
......@@ -719,15 +720,16 @@ int b_stepwise(const Argm& argm, Error_list& exceptions) {
Variable_map locals(f->arg_to_param(lookup));
Argm params(lookup.argv(), lookup.argfunction(), &locals,
lookup.input, lookup.output, lookup.error);
int ret = -1;
int ret = 0;
for (auto j: f->body) {
Argm body_i(j.interpret(params, exceptions));
if (global_stack.unwind_stack()) break;
Argm body(".mapped_argfunction", body_i.argv(), nullptr,
body_i.parent_map(), body_i.input, body_i.output, body_i.error);
ret = (*argm.argfunction())(body, exceptions);
(void) global_stack.remove_exceptions(".continue", exceptions);
if (global_stack.remove_exceptions(".break", exceptions)) break;
else if (global_stack.unwind_stack()) break;}
if (global_stack.remove_exceptions(".break", exceptions) ||
global_stack.unwind_stack()) break;}
f->unused_var_check(&locals, exceptions);
return ret;} // last return value from argfunction
......@@ -872,7 +874,7 @@ int b_throw(const Argm& argm, Error_list& exceptions) {
Variable_map::global_map, default_input, default_output,
default_error);
exceptions.add_error(new_exception);
return -1;}
return 0;}
// enable readline if disabled, disable if enabled
int b_toggle_readline(const Argm& argm, Error_list& exceptions) {
......@@ -902,9 +904,7 @@ int b_try_catch_recursive(const Argm& argm, Error_list& exceptions) {
argm.input, argm.output.child_stream(), argm.error);
mapped_argm.push_back(".try_catch_recursive(body)");
int ret = (*argm.argfunction())(mapped_argm, exceptions);
if (global_stack.unwind_stack()) {
global_stack.catch_blocks(argm, exceptions);
return -1;}
if (global_stack.unwind_stack()) global_stack.catch_blocks(argm, exceptions);
return ret;}
// print the type of executable with name $1 from executable map
......@@ -940,9 +940,15 @@ int b_usleep(const Argm& argm, Error_list& exceptions) {
if (argm.argc() != 2) throw Exception(Argm::Bad_argc, argm.argc()-1, 1, 0);
if (argm.argfunction()) throw Exception(Argm::Excess_argfunction);
try {
int delay = my_strtoi(argm[1], 0, INT_MAX);
sleep_requested += delay / 1000000.0;
return usleep(delay);}
int usec = my_strtoi(argm[1], 0, INT_MAX);
sleep_requested += usec / 1000000.0;
const long mega = (long)1E6;
struct timespec delay = {usec / mega, (usec % mega) * 1000};
if (!nanosleep(&delay, nullptr)) return 0;
else if (errno == EINTR) //not tested
throw Exception(Argm::Interrupted_sleep);
else throw Exception(Argm::Internal_error, //not tested
"nanosleep failed with code", errno);}
catch (E_generic) {throw Exception(Argm::Not_a_number, argm[1]);}
catch (E_nan) {throw Exception(Argm::Not_a_number, argm[1]);}
catch (E_range) {throw Exception(Argm::Input_range, argm[1]);}}
......@@ -1126,7 +1132,7 @@ int b_which_path(const Argm& argm, Error_list& exceptions) {
int b_while(const Argm& argm, Error_list& exceptions) {
if (argm.argc() < 2) throw Exception(Argm::Bad_argc, argm.argc()-1, 1, 0);
if (!argm.argfunction()) throw Exception(Argm::Missing_argfunction);
int ret = -1;
int ret = 0;
Argm lookup(argm.subrange(1), nullptr, argm.parent_map(),
argm.input, argm.output.child_stream(), argm.error);
while (executable_map.run_condition(lookup, exceptions)) {
......@@ -1135,6 +1141,6 @@ int b_while(const Argm& argm, Error_list& exceptions) {
mapped_argm.push_back(".mapped_argfunction");
ret = (*argm.argfunction())(mapped_argm, exceptions);
(void) global_stack.remove_exceptions(".continue", exceptions);
if (global_stack.remove_exceptions(".break", exceptions)) return 0;
else if (global_stack.unwind_stack()) return -1;}
if (global_stack.remove_exceptions(".break", exceptions) ||
global_stack.unwind_stack()) return 0;}
return ret;}
......@@ -68,7 +68,7 @@ int Call_stack::collect_errors_core(const Argm& argm, bool logic,
Argm blank(argm.parent_map(), argm.input, argm.output.child_stream(),
argm.error);
blank.push_back(".mapped_argfunction");
std::vector<std::string> exceptional(argm.begin()+1, argm.end());
std::vector<std::string> other(argm.begin()+1, argm.end());
int ret;
for (auto j: *argm.argfunction()) {
if (current_exception_count > max_collect) {
......@@ -79,13 +79,13 @@ int Call_stack::collect_errors_core(const Argm& argm, bool logic,
return ret;}
Error_list children;
Argm statement_argm = j.interpret(blank, children);
ret = executable_map.run(statement_argm, 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(exceptional.begin(), exceptional.end(),
(k)[0]) != exceptional.end()))
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;}
......
......@@ -4,9 +4,9 @@ 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 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
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 \
......
......@@ -26,7 +26,7 @@ int Base_executable::operator() (const Argm& argm,
Error_list& parent_exceptions) {
if (global_stack.global_nesting > global_stack.max_nesting+1)
parent_exceptions.add_error(Exception(Argm::Excessive_nesting));
if (global_stack.unwind_stack()) return Variable_map::dollar_question;
if (global_stack.unwind_stack()) return 0;
else ++executable_nesting, ++global_stack.global_nesting;
Error_list children;
struct timeval start_time;
......
......@@ -71,17 +71,15 @@ int Executable_map::base_run(Argm& argm, Error_list& exceptions) {
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()) {
if (global_stack.unwind_stack())
global_stack.exception_handler(exceptions);
return -1;}
else return ret;}
return ret;}
int Executable_map::run_handling_exceptions(Argm& argm, Error_list& exceptions){
int ret = run(argm, exceptions);
if (global_stack.unwind_stack()) {
if (global_stack.unwind_stack())
global_stack.exception_handler(exceptions);
return -1;}
else return ret;}
return ret;}
void Executable_map::unused_var_check_at_exit(void) {
Error_list exceptions;
......@@ -107,7 +105,7 @@ 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);
......@@ -121,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);}
......@@ -49,6 +49,7 @@ int Command_block::execute(const Argm& src_argm,
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 (global_stack.unwind_stack()) break;}
return ret;}
......
......@@ -34,7 +34,7 @@
const char* WSPACE = " \t";
struct timezone Clock::no_timezone_v = {0, 0};
std::string Argm::exception_names[Argm::Exception_count] = {
"no exception",
".nop",
".ambiguous_prototype_dash_dash",
".arguments_for_argfunction",
".autofunction",
......@@ -73,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",
......@@ -156,10 +157,11 @@ int main(int argc, char *argv[]) {
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],
......
.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 ... {
......@@ -84,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$}
......@@ -119,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$}
......@@ -132,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$}}
......@@ -167,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}
if_only .var_exists stack {echo call stack: $stack$}}
.function_all_flags .sigcont {
echo received SIGCONT; .return -1}
echo received SIGCONT}
.function_all_flags .sigchld {
echo received SIGCHLD; .return -1}
echo received SIGCHLD}
.function_all_flags .sighup {
echo received SIGHUP; .return -1}
echo received SIGHUP}
.function_all_flags .sigint {
.throw echo $nl received SIGINT}
.function_all_flags .sigquit {
echo received SIGQUIT; .return -1}
echo received SIGQUIT}
.function_all_flags .sigpipe {
echo received SIGPIPE; .return -1}
echo received SIGPIPE}
.function_all_flags .sigterm {
.throw echo $nl received SIGTERM}
.function_all_flags .sigtstp {
echo received SIGTSTP; .return -1}
echo received SIGTSTP}
.function_all_flags .siginfo {
echo received SIGINFO; .return -1}
echo received SIGINFO}
.function_all_flags .sigusr1 {
echo received SIGUSR1; .return -1}
echo received SIGUSR1}
.function_all_flags .sigusr2 {
echo received SIGUSR2; .return -1}
echo received SIGUSR2}
.function_all_flags .sigunknown {
echo received unknown unix signal; .return -1}
echo received unknown unix signal}
.function_all_flags .shutdown -- args ... {
.nop $args
.combine $nl (now terminating normally) $nl}
......@@ -218,8 +227,7 @@
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$}
......@@ -253,10 +261,7 @@
.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 ... {
......@@ -285,6 +290,8 @@ fn ntimes n {
.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 ... {
......
......@@ -174,7 +174,6 @@ fn unselect {.set s ()}
.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
......
This diff is collapsed.
echo before error
echo the error ${.throw echo nonexistent} was there
echo after error
This diff is collapsed.
......@@ -33,6 +33,7 @@ line continuation (or it was supposed to be)
.source test_files/unclosed_parenthesis_newline.rwsh
.source test_files/unclosed_parenthesis.rwsh
.source test_files/multiple_errors.rwsh
.source test_files/bad_substitution.rwsh
.nop multiple statements \; on a line
.whence_function .argfunction {.multiple_argfunctions} {}
.whence_function .argfunction {.argfunction with text args}
......@@ -110,8 +111,8 @@ true
fals
/bin/true add the binary by itself
true add the function using existing binary
.whence_function /bin/false
false add the function and the binary at once
.whence_function /usr/bin/env
env false add the function and the binary at once
.whence_function true
/bin/echo this will add /bin/echo to the hash table
/bin/echo this will use the existing /bin/echo reference
......@@ -120,7 +121,8 @@ echo this function is already defined
.whence_function ./rwsh
./rwsh -c (echo in a subshell)
false
./rwsh -c (false)
if_only_not false {echo false throwing a .false exception}
./rwsh -c (env false)
./rwsh -c (.get_pid)
./rwsh /non-existent/file/to/test/failure <test_files/pause_hello.rwsh
./rwsh test_files/pause_hello.rwsh <test_files/pause_hello.rwsh
......@@ -252,15 +254,21 @@ whence .argfunction {e ${e $A}}
echo &{.echo $A} &&{.echo $A} &&&{.echo $A} ${.echo $A} $A}}
.scope not_bin A {e &{.echo &A $A} &&{.echo &A &&A} ${.echo &A $A} $nl}
sa &{.echo $A} {echo $args &1$}
sa &{.return 1} {}
sa &{.throw .nop} {}
sa ${.throw .nop} {}
sa ${.return 1} {}
se {e &{.return 1}}
se {e &&{.return 1}; e after}
fn .failed_substitution [args ...] {.nop $args$; e $Z}
.throw sa {echo even from $args$ 7 is a number}
.fallback_handler sa {echo even from $args$ 7 is a number}
.throw .failed_substitution sa {echo even from $args$ 7 is a number}
se {e &&{.return 1}; e after}
se {e &{.throw .nop}}
se {e &&{.throw .nop}; e after}
se {e &&{.throw .nop}; e after}
.collect_errors_except .nop {
${.throw echo exception from inside substitution}
echo after failed substitution}
.collect_errors_only .failed_substitution echo {
${.throw echo exception from inside substitution}
echo after failed substitution}
.collect_errors_only .nop {
${.throw echo exception from inside substitution}
echo after failed substitution}
e x{e bad argfunction style}
e x&&&{e x}
e $+{e x}
......@@ -327,6 +335,8 @@ se {se >outfile {e line 1 $nl; e line 2 longer $nl; .echo $nl; echo ending}}
.throw .continue}}
.throw .continue
echo not printed}
.for_each_line <outfile {
e line of $# \( $* \) $nl; .throw echo exception in for_each_line}
.for_each_line <outfile {e line of $# \( $* \) $nl}
/bin/rm outfile
......@@ -394,8 +404,8 @@ x {x {x {x {
# .exec .fork Binary Old_argv_t .binary_not_found
.fork
.fork echo text
.fork .return 127
.fork sa 126 {sa $args$ {echo about to return $args$; .return $args$}}
.fork .exit 127
.fork sa 126 {sa $args$ {echo about to return $args$; .exit $args$}}
.exec
.exec something {excess argfunc}
.exec /bin/ech does not exist
......@@ -430,11 +440,15 @@ x {x {x {x {
.set_fallback_message (alternate fallback message: )
.fallback_handler a third
.set_fallback_message $original_message
.throw sa .throw {echo even from $args$ 7 is a number}
.fallback_handler .throw sa .throw {echo even from $args$ 7 is a number}
.throw .throw sa {echo even from $args$ 7 is a number}
# .for
.for {echo no arguments $1}
.for no_argfunction