rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

commit 31f0826614c17ef69f3cc654f9ede3ca9452522e
parent b12d41f95452f06360d4879bc466469686d167a8
Author: tgoodwin <tgoodwin>
Date:   Thu,  5 Feb 1998 14:51:54 +0000

Initial revision

Diffstat:
Aexcept.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 140 insertions(+), 0 deletions(-)

diff --git a/except.c b/except.c @@ -0,0 +1,140 @@ +#include <setjmp.h> +#include <signal.h> +#include "rc.h" +#include "jbwrap.h" + +/* + A return goes back stack frames to the last return. A break does + not. A signal goes to the last interactive level. (see below) +*/ + +bool nl_on_intr = TRUE; + +static Estack *estack; + +/* add an exception to the input stack. */ + +extern void except(ecodes e, Edata data, Estack *ex) { + ex->prev = estack; + estack = ex; + estack->e = e; + estack->data = data; + if (e == eError || e == eBreak || e == eReturn) + estack->interactive = interactive; +} + +/* remove an exception, restore last interactive value */ + +extern void unexcept() { + switch (estack->e) { + default: + break; + case eError: + interactive = estack->interactive; + break; + case eArena: + restoreblock(estack->data.b); + break; + case eFifo: + unlink(estack->data.name); + break; + case eFd: + close(estack->data.fd); + break; + } + estack = estack->prev; +} + +/* + Raise an exception. The rules are pretty complicated: you can return + from a loop inside a function, but you can't break from a function + inside of a loop. On errors, rc_raise() goes back to the LAST + INTERACTIVE stack frame. If no such frame exists, then rc_raise() + exits the shell. This is what happens, say, when there is a syntax + error in a noninteractive shell script. While traversing the + exception stack backwards, rc_raise() also removes input sources + (closing file-descriptors, etc.) and pops instances of variables + that have been pushed onto the variable stack (e.g., for a function + call (for $*) or a local assignment). +*/ + +extern void rc_raise(ecodes e) { + if (e == eError && rc_pid != getpid()) + exit(1); /* child processes exit on an error/signal */ + for (; estack != NULL; estack = estack->prev) + if (estack->e != e) { + if (e == eBreak && estack->e != eArena) + rc_error("break outside of loop"); + else if (e == eReturn && estack->e == eError) /* can return from loops inside functions */ + rc_error("return outside of function"); + switch (estack->e) { + default: + break; + case eVarstack: + varrm(estack->data.name, TRUE); + break; + case eArena: + restoreblock(estack->data.b); + break; + case eFifo: + unlink(estack->data.name); + break; + case eFd: + close(estack->data.fd); + break; + } + } else { + if (e == eError && !estack->interactive) { + popinput(); + } else { + Jbwrap *j = estack->data.jb; + + interactive = estack->interactive; + estack = estack->prev; + longjmp(j->j, 1); + } + } + rc_exit(1); /* top of exception stack */ +} + +extern bool outstanding_cmdarg() { + return estack->e == eFifo || estack->e == eFd; +} + +extern void pop_cmdarg(bool remove) { + for (; estack != NULL; estack = estack->prev) + switch (estack->e) { + case eFifo: + if (remove) + unlink(estack->data.name); + break; + case eFd: + if (remove) + close(estack->data.fd); + break; + default: + return; + } +} + +/* exception handlers */ + +extern void rc_error(char *s) { + pr_error(s); + set(FALSE); + redirq = NULL; + cond = FALSE; /* no longer inside conditional */ + rc_raise(eError); +} + +extern void sigint(int s) { + if (s != SIGINT) + panic("s != SIGINT in sigint catcher"); + /* this is the newline you see when you hit ^C while typing a command */ + if (interactive && nl_on_intr) + fprint(2, "\n"); + nl_on_intr = TRUE; + redirq = NULL; + cond = FALSE; + rc_raise(eError); +}