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