rc

[fork] interactive rc shell
git clone https://hhvn.uk/rc
git clone git://hhvn.uk/rc
Log | Files | Refs | README | LICENSE

except.c (3576B)


      1 #include "rc.h"
      2 
      3 #include <setjmp.h>
      4 #include <signal.h>
      5 
      6 #include "input.h"
      7 #include "jbwrap.h"
      8 
      9 /*
     10    A return goes back stack frames to the last return. A break does
     11    not. A signal goes to the last interactive level. (see below)
     12 */
     13 
     14 bool nl_on_intr = TRUE;
     15 
     16 static Estack *estack;
     17 
     18 /* add an exception to the input stack. */
     19 
     20 extern void except(ecodes e, Edata data, Estack *ex) {
     21 	ex->prev = estack;
     22 	estack = ex;
     23 	estack->e = e;
     24 	estack->data = data;
     25 	if (e == eError || e == eBreak || e == eReturn || e == eContinue)
     26 		estack->interactive = interactive;
     27 }
     28 
     29 /* remove an exception, restore last interactive value */
     30 
     31 extern void unexcept(ecodes e) {
     32 	assert(e == estack->e);
     33 	switch (estack->e) {
     34 	default:
     35 		break;
     36 	case eError:
     37 		interactive = estack->interactive;
     38 		break;
     39 	case eArena:
     40 		restoreblock(estack->data.b);
     41 		break;
     42 	case eFifo:
     43 		unlink(estack->data.name);
     44 		break;
     45 	case eFd:
     46 		close(estack->data.fd);
     47 		break;
     48 	}
     49 	estack = estack->prev;
     50 }
     51 
     52 /*
     53    Raise an exception. The rules are pretty complicated: you can return
     54    from a loop inside a function, but you can't break from a function
     55    inside of a loop. On errors, rc_raise() goes back to the LAST
     56    INTERACTIVE stack frame. If no such frame exists, then rc_raise()
     57    exits the shell.  This is what happens, say, when there is a syntax
     58    error in a noninteractive shell script. While traversing the
     59    exception stack backwards, rc_raise() also removes input sources
     60    (closing file-descriptors, etc.) and pops instances of variables
     61    that have been pushed onto the variable stack (e.g., for a function
     62    call (for $*) or a local assignment).
     63 */
     64 
     65 extern void rc_raise(ecodes e) {
     66 	if (e == eError && rc_pid != getpid())
     67 		exit(1); /* child processes exit on an error/signal */
     68 	for (; estack != NULL; estack = estack->prev)
     69 		if (estack->e != e) {
     70 			if (e == eBreak && (estack->e != eArena && estack->e != eVarstack && estack->e != eContinue))
     71 				rc_error("break outside of loop");
     72 			else if (e == eContinue && (estack->e != eVarstack))
     73 				rc_error("continue outside of loop");
     74 			else if (e == eReturn && estack->e == eError) /* can return from loops inside functions */
     75 				rc_error("return outside of function");
     76 			switch (estack->e) {
     77 			default:
     78 				break;
     79 			case eVarstack:
     80 				varrm(estack->data.name, TRUE);
     81 				break;
     82 			case eArena:
     83 				restoreblock(estack->data.b);
     84 				break;
     85 			case eFifo:
     86 				unlink(estack->data.name);
     87 				break;
     88 			case eFd:
     89 				close(estack->data.fd);
     90 				break;
     91 			}
     92 		} else {
     93 			if (e == eError && !estack->interactive) {
     94 				popinput();
     95 			} else {
     96 				Jbwrap *j = estack->data.jb;
     97 
     98 				interactive = estack->interactive;
     99 				estack = estack->prev;
    100 				siglongjmp(j->j, 1);
    101 			}
    102 		}
    103 	rc_exit(1); /* top of exception stack */
    104 }
    105 
    106 extern bool outstanding_cmdarg() {
    107 	return estack->e == eFifo || estack->e == eFd;
    108 }
    109 
    110 extern void pop_cmdarg(bool remove) {
    111 	for (; estack != NULL; estack = estack->prev)
    112 		switch (estack->e) {
    113 		case eFifo:
    114 			if (remove)
    115 				unlink(estack->data.name);
    116 			break;
    117 		case eFd:
    118 			if (remove)
    119 				close(estack->data.fd);
    120 			break;
    121 		default:
    122 			return;
    123 		}
    124 }
    125 
    126 /* exception handlers */
    127 
    128 extern void rc_error(char *s) {
    129 	pr_error(s, -1);
    130 	set(FALSE);
    131 	redirq = NULL;
    132 	cond = FALSE; /* no longer inside conditional */
    133 	rc_raise(eError);
    134 }
    135 
    136 extern void sigint(int s) {
    137 	if (s != SIGINT)
    138 		panic("s != SIGINT in sigint catcher");
    139 	/* this is the newline you see when you hit ^C while typing a command */
    140 	if (interactive && nl_on_intr)
    141 		fprint(2, "\n");
    142 	nl_on_intr = TRUE;
    143 	redirq = NULL;
    144 	cond = FALSE;
    145 	rc_raise(eError);
    146 }