]>
Commit | Line | Data |
---|---|---|
2e4498b3 CR |
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 | ||
d267701a | 6 | /* Copyright (C) 1987-2023 Free Software Foundation, Inc. |
726f6388 | 7 | |
2e4498b3 | 8 | This file is part of GNU Bash, the Bourne Again SHell. |
726f6388 | 9 | |
2e4498b3 CR |
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 | |
2e4498b3 CR |
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 | |
2e4498b3 CR |
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 | ||
a67031fe | 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" |
83509ab7 | 50 | #include "quit.h" |
701f36c2 | 51 | #include "bashintl.h" /* for _() */ |
adc6cff5 | 52 | #include "error.h" /* for internal_warning */ |
34ec1876 | 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; | |
877ff726 | 68 | sh_uwfunc_t *cleanup; |
f73dda09 JA |
69 | } head; |
70 | struct { | |
71 | struct uwp_head uwp_head; | |
877ff726 | 72 | void *v; |
f73dda09 JA |
73 | } arg; |
74 | struct { | |
75 | struct uwp_head uwp_head; | |
76 | SAVED_VAR v; | |
77 | } sv; | |
78 | } UNWIND_ELT; | |
79 | ||
d267701a CR |
80 | static void unwind_frame_discard_internal (char *); |
81 | static void unwind_frame_run_internal (char *); | |
877ff726 | 82 | static void add_unwind_protect_internal (sh_uwfunc_t *, void *); |
d267701a CR |
83 | static void remove_unwind_protect_internal (void); |
84 | static void run_unwind_protects_internal (void); | |
85 | static void clear_unwind_protects_internal (int); | |
877ff726 CR |
86 | static inline void restore_variable (void *); |
87 | static void unwind_protect_mem_internal (void *, int); | |
726f6388 JA |
88 | |
89 | static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL; | |
90 | ||
34ec1876 CR |
91 | /* Allocating from a cache of unwind-protect elements */ |
92 | #define UWCACHESIZE 128 | |
93 | ||
94 | sh_obj_cache_t uwcache = {0, 0, 0}; | |
95 | ||
96 | #if 0 | |
7117c2d2 JA |
97 | #define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT)) |
98 | #define uwpfree(elt) free(elt) | |
34ec1876 CR |
99 | #else |
100 | #define uwpalloc(elt) ocache_alloc (uwcache, UNWIND_ELT, elt) | |
101 | #define uwpfree(elt) ocache_free (uwcache, UNWIND_ELT, elt) | |
102 | #endif | |
103 | ||
104 | void | |
a61ffa78 | 105 | uwp_init (void) |
34ec1876 CR |
106 | { |
107 | ocache_create (uwcache, UNWIND_ELT, UWCACHESIZE); | |
108 | } | |
726f6388 | 109 | |
726f6388 JA |
110 | /* Start the beginning of a region. */ |
111 | void | |
a61ffa78 | 112 | begin_unwind_frame (char *tag) |
726f6388 | 113 | { |
d267701a | 114 | add_unwind_protect (NULL, tag); |
726f6388 JA |
115 | } |
116 | ||
117 | /* Discard the unwind protects back to TAG. */ | |
118 | void | |
a61ffa78 | 119 | discard_unwind_frame (char *tag) |
726f6388 JA |
120 | { |
121 | if (unwind_protect_list) | |
d267701a | 122 | unwind_frame_discard_internal (tag); |
726f6388 JA |
123 | } |
124 | ||
125 | /* Run the unwind protects back to TAG. */ | |
126 | void | |
a61ffa78 | 127 | run_unwind_frame (char *tag) |
726f6388 JA |
128 | { |
129 | if (unwind_protect_list) | |
d267701a | 130 | unwind_frame_run_internal (tag); |
726f6388 JA |
131 | } |
132 | ||
133 | /* Add the function CLEANUP with ARG to the list of unwindable things. */ | |
134 | void | |
877ff726 | 135 | add_unwind_protect (sh_uwfunc_t *cleanup, void *arg) |
726f6388 | 136 | { |
d267701a | 137 | add_unwind_protect_internal (cleanup, arg); |
726f6388 JA |
138 | } |
139 | ||
140 | /* Remove the top unwind protect from the list. */ | |
141 | void | |
a61ffa78 | 142 | remove_unwind_protect (void) |
726f6388 JA |
143 | { |
144 | if (unwind_protect_list) | |
d267701a | 145 | remove_unwind_protect_internal (); |
726f6388 JA |
146 | } |
147 | ||
148 | /* Run the list of cleanup functions in unwind_protect_list. */ | |
149 | void | |
a61ffa78 | 150 | run_unwind_protects (void) |
726f6388 JA |
151 | { |
152 | if (unwind_protect_list) | |
d267701a | 153 | run_unwind_protects_internal (); |
726f6388 JA |
154 | } |
155 | ||
28ef6c31 JA |
156 | /* Erase the unwind-protect list. If flags is 1, free the elements. */ |
157 | void | |
a61ffa78 | 158 | clear_unwind_protect_list (int flags) |
28ef6c31 JA |
159 | { |
160 | if (unwind_protect_list) | |
d267701a | 161 | clear_unwind_protects_internal (flags); |
28ef6c31 JA |
162 | } |
163 | ||
1fa6db60 | 164 | int |
a61ffa78 | 165 | have_unwind_protects (void) |
1fa6db60 CR |
166 | { |
167 | return (unwind_protect_list != 0); | |
168 | } | |
169 | ||
fbbc416f | 170 | int |
a61ffa78 | 171 | unwind_protect_tag_on_stack (const char *tag) |
fbbc416f CR |
172 | { |
173 | UNWIND_ELT *elt; | |
174 | ||
175 | elt = unwind_protect_list; | |
176 | while (elt) | |
177 | { | |
178 | if (elt->head.cleanup == 0 && STREQ (elt->arg.v, tag)) | |
179 | return 1; | |
180 | elt = elt->head.next; | |
181 | } | |
182 | return 0; | |
183 | } | |
184 | ||
726f6388 JA |
185 | /* **************************************************************** */ |
186 | /* */ | |
28ef6c31 | 187 | /* The Actual Functions */ |
726f6388 JA |
188 | /* */ |
189 | /* **************************************************************** */ | |
190 | ||
191 | static void | |
877ff726 | 192 | add_unwind_protect_internal (sh_uwfunc_t *cleanup, void *arg) |
726f6388 JA |
193 | { |
194 | UNWIND_ELT *elt; | |
195 | ||
7117c2d2 | 196 | uwpalloc (elt); |
f73dda09 JA |
197 | elt->head.next = unwind_protect_list; |
198 | elt->head.cleanup = cleanup; | |
199 | elt->arg.v = arg; | |
726f6388 JA |
200 | unwind_protect_list = elt; |
201 | } | |
202 | ||
203 | static void | |
d267701a | 204 | remove_unwind_protect_internal (void) |
726f6388 | 205 | { |
d166f048 | 206 | UNWIND_ELT *elt; |
726f6388 | 207 | |
d166f048 | 208 | elt = unwind_protect_list; |
726f6388 JA |
209 | if (elt) |
210 | { | |
f73dda09 | 211 | unwind_protect_list = unwind_protect_list->head.next; |
7117c2d2 | 212 | uwpfree (elt); |
726f6388 JA |
213 | } |
214 | } | |
215 | ||
216 | static void | |
d267701a | 217 | run_unwind_protects_internal (void) |
726f6388 | 218 | { |
d267701a | 219 | unwind_frame_run_internal (NULL); |
726f6388 JA |
220 | } |
221 | ||
222 | static void | |
d267701a | 223 | clear_unwind_protects_internal (int flag) |
28ef6c31 | 224 | { |
f73dda09 | 225 | if (flag) |
28ef6c31 JA |
226 | { |
227 | while (unwind_protect_list) | |
d267701a | 228 | remove_unwind_protect_internal (); |
28ef6c31 JA |
229 | } |
230 | unwind_protect_list = (UNWIND_ELT *)NULL; | |
231 | } | |
232 | ||
233 | static void | |
d267701a | 234 | unwind_frame_discard_internal (char *tag) |
726f6388 JA |
235 | { |
236 | UNWIND_ELT *elt; | |
048b249e | 237 | int found; |
726f6388 | 238 | |
048b249e | 239 | found = 0; |
726f6388 JA |
240 | while (elt = unwind_protect_list) |
241 | { | |
f73dda09 JA |
242 | unwind_protect_list = unwind_protect_list->head.next; |
243 | if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag))) | |
726f6388 | 244 | { |
7117c2d2 | 245 | uwpfree (elt); |
048b249e | 246 | found = 1; |
726f6388 JA |
247 | break; |
248 | } | |
249 | else | |
7117c2d2 | 250 | uwpfree (elt); |
726f6388 | 251 | } |
048b249e CR |
252 | |
253 | if (found == 0) | |
701f36c2 | 254 | internal_warning (_("unwind_frame_discard: %s: frame not found"), tag); |
726f6388 JA |
255 | } |
256 | ||
f73dda09 JA |
257 | /* Restore the value of a variable, based on the contents of SV. |
258 | sv->desired_setting is a block of memory SIZE bytes long holding the | |
259 | value itself. This block of memory is copied back into the variable. */ | |
260 | static inline void | |
877ff726 | 261 | restore_variable (void *arg) |
f73dda09 | 262 | { |
877ff726 CR |
263 | SAVED_VAR *sv; |
264 | sv = arg; | |
f73dda09 JA |
265 | FASTCOPY (sv->desired_setting, sv->variable, sv->size); |
266 | } | |
267 | ||
726f6388 | 268 | static void |
d267701a | 269 | unwind_frame_run_internal (char *tag) |
726f6388 JA |
270 | { |
271 | UNWIND_ELT *elt; | |
048b249e | 272 | int found; |
726f6388 | 273 | |
048b249e | 274 | found = 0; |
726f6388 JA |
275 | while (elt = unwind_protect_list) |
276 | { | |
f73dda09 | 277 | unwind_protect_list = elt->head.next; |
726f6388 JA |
278 | |
279 | /* If tag, then compare. */ | |
048b249e | 280 | if (elt->head.cleanup == 0) |
726f6388 | 281 | { |
f73dda09 | 282 | if (tag && STREQ (elt->arg.v, tag)) |
726f6388 | 283 | { |
7117c2d2 | 284 | uwpfree (elt); |
048b249e | 285 | found = 1; |
726f6388 JA |
286 | break; |
287 | } | |
726f6388 JA |
288 | } |
289 | else | |
290 | { | |
877ff726 | 291 | if (elt->head.cleanup == restore_variable) |
f73dda09 JA |
292 | restore_variable (&elt->sv.v); |
293 | else | |
294 | (*(elt->head.cleanup)) (elt->arg.v); | |
726f6388 | 295 | } |
726f6388 | 296 | |
7117c2d2 | 297 | uwpfree (elt); |
f73dda09 | 298 | } |
048b249e | 299 | if (tag && found == 0) |
701f36c2 | 300 | internal_warning (_("unwind_frame_run: %s: frame not found"), tag); |
d166f048 | 301 | } |
726f6388 | 302 | |
726f6388 | 303 | static void |
877ff726 | 304 | unwind_protect_mem_internal (void *var, int size) |
726f6388 | 305 | { |
2e725f73 | 306 | size_t allocated; |
f73dda09 | 307 | UNWIND_ELT *elt; |
726f6388 | 308 | |
f73dda09 | 309 | allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]); |
dd4e3abc CR |
310 | if (allocated < sizeof (UNWIND_ELT)) |
311 | allocated = sizeof (UNWIND_ELT); | |
f73dda09 JA |
312 | elt = (UNWIND_ELT *)xmalloc (allocated); |
313 | elt->head.next = unwind_protect_list; | |
877ff726 | 314 | elt->head.cleanup = restore_variable; |
f73dda09 JA |
315 | elt->sv.v.variable = var; |
316 | elt->sv.v.size = size; | |
317 | FASTCOPY (var, elt->sv.v.desired_setting, size); | |
318 | unwind_protect_list = elt; | |
726f6388 JA |
319 | } |
320 | ||
321 | /* Save the value of a variable so it will be restored when unwind-protects | |
f73dda09 JA |
322 | are run. VAR is a pointer to the variable. SIZE is the size in |
323 | bytes of VAR. */ | |
726f6388 | 324 | void |
a61ffa78 | 325 | unwind_protect_mem (char *var, int size) |
726f6388 | 326 | { |
877ff726 | 327 | unwind_protect_mem_internal (var, size); |
726f6388 | 328 | } |
048b249e CR |
329 | |
330 | #if defined (DEBUG) | |
06dff54a CR |
331 | #include <stdio.h> |
332 | ||
048b249e | 333 | void |
a61ffa78 | 334 | print_unwind_protect_tags (void) |
048b249e CR |
335 | { |
336 | UNWIND_ELT *elt; | |
337 | ||
338 | elt = unwind_protect_list; | |
339 | while (elt) | |
340 | { | |
048b249e | 341 | if (elt->head.cleanup == 0) |
877ff726 | 342 | fprintf(stderr, "tag: %s\n", (char *)elt->arg.v); |
fbbc416f | 343 | elt = elt->head.next; |
048b249e CR |
344 | } |
345 | } | |
346 | #endif |