]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* I can't stand it anymore! Please can't we just write the |
2 | whole Unix system in lisp or something? */ | |
3 | ||
7117c2d2 | 4 | /* Copyright (C) 1987-2002 Free Software Foundation, Inc. |
726f6388 JA |
5 | |
6 | This file is part of GNU Bash, the Bourne Again SHell. | |
7 | ||
8 | Bash is free software; you can redistribute it and/or modify it under | |
9 | the terms of the GNU General Public License as published by the Free | |
bb70624e | 10 | Software Foundation; either version 2, or (at your option) any later |
726f6388 JA |
11 | version. |
12 | ||
13 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY | |
14 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
15 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License along | |
19 | with Bash; see the file COPYING. If not, write to the Free Software | |
bb70624e | 20 | Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ |
726f6388 JA |
21 | |
22 | /* **************************************************************** */ | |
23 | /* */ | |
24 | /* Unwind Protection Scheme for Bash */ | |
25 | /* */ | |
26 | /* **************************************************************** */ | |
726f6388 | 27 | #include "config.h" |
ccc6cda3 JA |
28 | |
29 | #include "bashtypes.h" | |
d166f048 JA |
30 | #include "bashansi.h" |
31 | ||
ccc6cda3 JA |
32 | #if defined (HAVE_UNISTD_H) |
33 | # include <unistd.h> | |
34 | #endif | |
35 | ||
f73dda09 JA |
36 | #if STDC_HEADERS |
37 | # include <stddef.h> | |
38 | #endif | |
39 | ||
40 | #ifndef offsetof | |
41 | # define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) | |
42 | #endif | |
43 | ||
726f6388 JA |
44 | #include "command.h" |
45 | #include "general.h" | |
46 | #include "unwind_prot.h" | |
47 | #include "quit.h" | |
ccc6cda3 | 48 | #include "sig.h" |
726f6388 | 49 | |
f73dda09 | 50 | /* Structure describing a saved variable and the value to restore it to. */ |
d166f048 | 51 | typedef struct { |
f73dda09 | 52 | char *variable; |
d166f048 | 53 | int size; |
f73dda09 | 54 | char desired_setting[1]; /* actual size is `size' */ |
d166f048 JA |
55 | } SAVED_VAR; |
56 | ||
f73dda09 JA |
57 | /* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to. |
58 | If HEAD.CLEANUP is restore_variable, then SV.V contains the saved | |
59 | variable. Otherwise, call HEAD.CLEANUP (ARG.V) to clean up. */ | |
60 | typedef union uwp { | |
61 | struct uwp_head { | |
62 | union uwp *next; | |
63 | Function *cleanup; | |
64 | } head; | |
65 | struct { | |
66 | struct uwp_head uwp_head; | |
67 | char *v; | |
68 | } arg; | |
69 | struct { | |
70 | struct uwp_head uwp_head; | |
71 | SAVED_VAR v; | |
72 | } sv; | |
73 | } UNWIND_ELT; | |
74 | ||
7117c2d2 | 75 | |
f73dda09 JA |
76 | static void without_interrupts __P((VFunction *, char *, char *)); |
77 | static void unwind_frame_discard_internal __P((char *, char *)); | |
78 | static void unwind_frame_run_internal __P((char *, char *)); | |
79 | static void add_unwind_protect_internal __P((Function *, char *)); | |
80 | static void remove_unwind_protect_internal __P((char *, char *)); | |
81 | static void run_unwind_protects_internal __P((char *, char *)); | |
82 | static void clear_unwind_protects_internal __P((char *, char *)); | |
83 | static inline void restore_variable __P((SAVED_VAR *)); | |
84 | static void unwind_protect_mem_internal __P((char *, char *)); | |
726f6388 JA |
85 | |
86 | static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL; | |
87 | ||
7117c2d2 JA |
88 | #define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT)) |
89 | #define uwpfree(elt) free(elt) | |
726f6388 JA |
90 | |
91 | /* Run a function without interrupts. This relies on the fact that the | |
92 | FUNCTION cannot change the value of interrupt_immediately. (I.e., does | |
93 | not call QUIT (). */ | |
94 | static void | |
95 | without_interrupts (function, arg1, arg2) | |
96 | VFunction *function; | |
97 | char *arg1, *arg2; | |
98 | { | |
99 | int old_interrupt_immediately; | |
100 | ||
101 | old_interrupt_immediately = interrupt_immediately; | |
102 | interrupt_immediately = 0; | |
103 | ||
104 | (*function)(arg1, arg2); | |
105 | ||
106 | interrupt_immediately = old_interrupt_immediately; | |
107 | } | |
108 | ||
109 | /* Start the beginning of a region. */ | |
110 | void | |
111 | begin_unwind_frame (tag) | |
112 | char *tag; | |
113 | { | |
114 | add_unwind_protect ((Function *)NULL, tag); | |
115 | } | |
116 | ||
117 | /* Discard the unwind protects back to TAG. */ | |
118 | void | |
119 | discard_unwind_frame (tag) | |
120 | char *tag; | |
121 | { | |
122 | if (unwind_protect_list) | |
123 | without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL); | |
124 | } | |
125 | ||
126 | /* Run the unwind protects back to TAG. */ | |
127 | void | |
128 | run_unwind_frame (tag) | |
129 | char *tag; | |
130 | { | |
131 | if (unwind_protect_list) | |
132 | without_interrupts (unwind_frame_run_internal, tag, (char *)NULL); | |
133 | } | |
134 | ||
135 | /* Add the function CLEANUP with ARG to the list of unwindable things. */ | |
136 | void | |
137 | add_unwind_protect (cleanup, arg) | |
138 | Function *cleanup; | |
139 | char *arg; | |
140 | { | |
141 | without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg); | |
142 | } | |
143 | ||
144 | /* Remove the top unwind protect from the list. */ | |
145 | void | |
146 | remove_unwind_protect () | |
147 | { | |
148 | if (unwind_protect_list) | |
149 | without_interrupts | |
150 | (remove_unwind_protect_internal, (char *)NULL, (char *)NULL); | |
151 | } | |
152 | ||
153 | /* Run the list of cleanup functions in unwind_protect_list. */ | |
154 | void | |
155 | run_unwind_protects () | |
156 | { | |
157 | if (unwind_protect_list) | |
158 | without_interrupts | |
159 | (run_unwind_protects_internal, (char *)NULL, (char *)NULL); | |
160 | } | |
161 | ||
28ef6c31 JA |
162 | /* Erase the unwind-protect list. If flags is 1, free the elements. */ |
163 | void | |
164 | clear_unwind_protect_list (flags) | |
165 | int flags; | |
166 | { | |
f73dda09 JA |
167 | char *flag; |
168 | ||
28ef6c31 | 169 | if (unwind_protect_list) |
f73dda09 JA |
170 | { |
171 | flag = flags ? "" : (char *)NULL; | |
172 | without_interrupts | |
173 | (clear_unwind_protects_internal, flag, (char *)NULL); | |
174 | } | |
28ef6c31 JA |
175 | } |
176 | ||
726f6388 JA |
177 | /* **************************************************************** */ |
178 | /* */ | |
28ef6c31 | 179 | /* The Actual Functions */ |
726f6388 JA |
180 | /* */ |
181 | /* **************************************************************** */ | |
182 | ||
183 | static void | |
184 | add_unwind_protect_internal (cleanup, arg) | |
185 | Function *cleanup; | |
186 | char *arg; | |
187 | { | |
188 | UNWIND_ELT *elt; | |
189 | ||
7117c2d2 | 190 | uwpalloc (elt); |
f73dda09 JA |
191 | elt->head.next = unwind_protect_list; |
192 | elt->head.cleanup = cleanup; | |
193 | elt->arg.v = arg; | |
726f6388 JA |
194 | unwind_protect_list = elt; |
195 | } | |
196 | ||
197 | static void | |
28ef6c31 JA |
198 | remove_unwind_protect_internal (ignore1, ignore2) |
199 | char *ignore1, *ignore2; | |
726f6388 | 200 | { |
d166f048 | 201 | UNWIND_ELT *elt; |
726f6388 | 202 | |
d166f048 | 203 | elt = unwind_protect_list; |
726f6388 JA |
204 | if (elt) |
205 | { | |
f73dda09 | 206 | unwind_protect_list = unwind_protect_list->head.next; |
7117c2d2 | 207 | uwpfree (elt); |
726f6388 JA |
208 | } |
209 | } | |
210 | ||
211 | static void | |
28ef6c31 JA |
212 | run_unwind_protects_internal (ignore1, ignore2) |
213 | char *ignore1, *ignore2; | |
726f6388 | 214 | { |
f73dda09 | 215 | unwind_frame_run_internal ((char *) NULL, (char *) NULL); |
726f6388 JA |
216 | } |
217 | ||
218 | static void | |
28ef6c31 JA |
219 | clear_unwind_protects_internal (flag, ignore) |
220 | char *flag, *ignore; | |
221 | { | |
f73dda09 | 222 | if (flag) |
28ef6c31 JA |
223 | { |
224 | while (unwind_protect_list) | |
225 | remove_unwind_protect_internal ((char *)NULL, (char *)NULL); | |
226 | } | |
227 | unwind_protect_list = (UNWIND_ELT *)NULL; | |
228 | } | |
229 | ||
230 | static void | |
231 | unwind_frame_discard_internal (tag, ignore) | |
232 | char *tag, *ignore; | |
726f6388 JA |
233 | { |
234 | UNWIND_ELT *elt; | |
235 | ||
236 | while (elt = unwind_protect_list) | |
237 | { | |
f73dda09 JA |
238 | unwind_protect_list = unwind_protect_list->head.next; |
239 | if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag))) | |
726f6388 | 240 | { |
7117c2d2 | 241 | uwpfree (elt); |
726f6388 JA |
242 | break; |
243 | } | |
244 | else | |
7117c2d2 | 245 | uwpfree (elt); |
726f6388 JA |
246 | } |
247 | } | |
248 | ||
f73dda09 JA |
249 | /* Restore the value of a variable, based on the contents of SV. |
250 | sv->desired_setting is a block of memory SIZE bytes long holding the | |
251 | value itself. This block of memory is copied back into the variable. */ | |
252 | static inline void | |
253 | restore_variable (sv) | |
254 | SAVED_VAR *sv; | |
255 | { | |
256 | FASTCOPY (sv->desired_setting, sv->variable, sv->size); | |
257 | } | |
258 | ||
726f6388 | 259 | static void |
28ef6c31 JA |
260 | unwind_frame_run_internal (tag, ignore) |
261 | char *tag, *ignore; | |
726f6388 JA |
262 | { |
263 | UNWIND_ELT *elt; | |
264 | ||
265 | while (elt = unwind_protect_list) | |
266 | { | |
f73dda09 | 267 | unwind_protect_list = elt->head.next; |
726f6388 JA |
268 | |
269 | /* If tag, then compare. */ | |
f73dda09 | 270 | if (!elt->head.cleanup) |
726f6388 | 271 | { |
f73dda09 | 272 | if (tag && STREQ (elt->arg.v, tag)) |
726f6388 | 273 | { |
7117c2d2 | 274 | uwpfree (elt); |
726f6388 JA |
275 | break; |
276 | } | |
726f6388 JA |
277 | } |
278 | else | |
279 | { | |
f73dda09 JA |
280 | if (elt->head.cleanup == (Function *) restore_variable) |
281 | restore_variable (&elt->sv.v); | |
282 | else | |
283 | (*(elt->head.cleanup)) (elt->arg.v); | |
726f6388 | 284 | } |
726f6388 | 285 | |
7117c2d2 | 286 | uwpfree (elt); |
f73dda09 | 287 | } |
d166f048 | 288 | } |
726f6388 | 289 | |
726f6388 | 290 | static void |
f73dda09 JA |
291 | unwind_protect_mem_internal (var, psize) |
292 | char *var; | |
293 | char *psize; | |
726f6388 | 294 | { |
f73dda09 JA |
295 | int size, allocated; |
296 | UNWIND_ELT *elt; | |
726f6388 | 297 | |
f73dda09 JA |
298 | size = *(int *) psize; |
299 | allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]); | |
300 | elt = (UNWIND_ELT *)xmalloc (allocated); | |
301 | elt->head.next = unwind_protect_list; | |
302 | elt->head.cleanup = (Function *) restore_variable; | |
303 | elt->sv.v.variable = var; | |
304 | elt->sv.v.size = size; | |
305 | FASTCOPY (var, elt->sv.v.desired_setting, size); | |
306 | unwind_protect_list = elt; | |
726f6388 JA |
307 | } |
308 | ||
309 | /* Save the value of a variable so it will be restored when unwind-protects | |
f73dda09 JA |
310 | are run. VAR is a pointer to the variable. SIZE is the size in |
311 | bytes of VAR. */ | |
726f6388 | 312 | void |
f73dda09 JA |
313 | unwind_protect_mem (var, size) |
314 | char *var; | |
726f6388 JA |
315 | int size; |
316 | { | |
f73dda09 | 317 | without_interrupts (unwind_protect_mem_internal, var, (char *) &size); |
726f6388 | 318 | } |