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