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