]> git.ipfire.org Git - thirdparty/bash.git/blob - unwind_prot.c
Bash-5.2 patch 26: fix typo when specifying readline's custom color prefix
[thirdparty/bash.git] / unwind_prot.c
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 "sig.h"
50 #include "quit.h"
51 #include "error.h" /* for internal_warning */
52 #include "ocache.h"
53
54 /* Structure describing a saved variable and the value to restore it to. */
55 typedef struct {
56 char *variable;
57 int size;
58 char desired_setting[1]; /* actual size is `size' */
59 } SAVED_VAR;
60
61 /* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to.
62 If HEAD.CLEANUP is restore_variable, then SV.V contains the saved
63 variable. Otherwise, call HEAD.CLEANUP (ARG.V) to clean up. */
64 typedef union uwp {
65 struct uwp_head {
66 union uwp *next;
67 Function *cleanup;
68 } head;
69 struct {
70 struct uwp_head uwp_head;
71 char *v;
72 } arg;
73 struct {
74 struct uwp_head uwp_head;
75 SAVED_VAR v;
76 } sv;
77 } UNWIND_ELT;
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 /* 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
97 #define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT))
98 #define uwpfree(elt) free(elt)
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
105 uwp_init ()
106 {
107 ocache_create (uwcache, UNWIND_ELT, UWCACHESIZE);
108 }
109
110 /* Run a function without interrupts. This relies on the fact that the
111 FUNCTION cannot change the value of interrupt_immediately. (I.e., does
112 not call QUIT (). */
113 static void
114 without_interrupts (function, arg1, arg2)
115 VFunction *function;
116 char *arg1, *arg2;
117 {
118 int old_interrupt_immediately;
119
120 old_interrupt_immediately = interrupt_immediately;
121 interrupt_immediately = 0;
122
123 (*function)(arg1, arg2);
124
125 interrupt_immediately = old_interrupt_immediately;
126 }
127
128 /* Start the beginning of a region. */
129 void
130 begin_unwind_frame (tag)
131 char *tag;
132 {
133 add_unwind_protect ((Function *)NULL, tag);
134 }
135
136 /* Discard the unwind protects back to TAG. */
137 void
138 discard_unwind_frame (tag)
139 char *tag;
140 {
141 if (unwind_protect_list)
142 without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL);
143 }
144
145 /* Run the unwind protects back to TAG. */
146 void
147 run_unwind_frame (tag)
148 char *tag;
149 {
150 if (unwind_protect_list)
151 without_interrupts (unwind_frame_run_internal, tag, (char *)NULL);
152 }
153
154 /* Add the function CLEANUP with ARG to the list of unwindable things. */
155 void
156 add_unwind_protect (cleanup, arg)
157 Function *cleanup;
158 char *arg;
159 {
160 without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg);
161 }
162
163 /* Remove the top unwind protect from the list. */
164 void
165 remove_unwind_protect ()
166 {
167 if (unwind_protect_list)
168 without_interrupts
169 (remove_unwind_protect_internal, (char *)NULL, (char *)NULL);
170 }
171
172 /* Run the list of cleanup functions in unwind_protect_list. */
173 void
174 run_unwind_protects ()
175 {
176 if (unwind_protect_list)
177 without_interrupts
178 (run_unwind_protects_internal, (char *)NULL, (char *)NULL);
179 }
180
181 /* Erase the unwind-protect list. If flags is 1, free the elements. */
182 void
183 clear_unwind_protect_list (flags)
184 int flags;
185 {
186 char *flag;
187
188 if (unwind_protect_list)
189 {
190 flag = flags ? "" : (char *)NULL;
191 without_interrupts
192 (clear_unwind_protects_internal, flag, (char *)NULL);
193 }
194 }
195
196 int
197 have_unwind_protects ()
198 {
199 return (unwind_protect_list != 0);
200 }
201
202 int
203 unwind_protect_tag_on_stack (tag)
204 const char *tag;
205 {
206 UNWIND_ELT *elt;
207
208 elt = unwind_protect_list;
209 while (elt)
210 {
211 if (elt->head.cleanup == 0 && STREQ (elt->arg.v, tag))
212 return 1;
213 elt = elt->head.next;
214 }
215 return 0;
216 }
217
218 /* **************************************************************** */
219 /* */
220 /* The Actual Functions */
221 /* */
222 /* **************************************************************** */
223
224 static void
225 add_unwind_protect_internal (cleanup, arg)
226 Function *cleanup;
227 char *arg;
228 {
229 UNWIND_ELT *elt;
230
231 uwpalloc (elt);
232 elt->head.next = unwind_protect_list;
233 elt->head.cleanup = cleanup;
234 elt->arg.v = arg;
235 unwind_protect_list = elt;
236 }
237
238 static void
239 remove_unwind_protect_internal (ignore1, ignore2)
240 char *ignore1, *ignore2;
241 {
242 UNWIND_ELT *elt;
243
244 elt = unwind_protect_list;
245 if (elt)
246 {
247 unwind_protect_list = unwind_protect_list->head.next;
248 uwpfree (elt);
249 }
250 }
251
252 static void
253 run_unwind_protects_internal (ignore1, ignore2)
254 char *ignore1, *ignore2;
255 {
256 unwind_frame_run_internal ((char *) NULL, (char *) NULL);
257 }
258
259 static void
260 clear_unwind_protects_internal (flag, ignore)
261 char *flag, *ignore;
262 {
263 if (flag)
264 {
265 while (unwind_protect_list)
266 remove_unwind_protect_internal ((char *)NULL, (char *)NULL);
267 }
268 unwind_protect_list = (UNWIND_ELT *)NULL;
269 }
270
271 static void
272 unwind_frame_discard_internal (tag, ignore)
273 char *tag, *ignore;
274 {
275 UNWIND_ELT *elt;
276 int found;
277
278 found = 0;
279 while (elt = unwind_protect_list)
280 {
281 unwind_protect_list = unwind_protect_list->head.next;
282 if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag)))
283 {
284 uwpfree (elt);
285 found = 1;
286 break;
287 }
288 else
289 uwpfree (elt);
290 }
291
292 if (found == 0)
293 internal_warning ("unwind_frame_discard: %s: frame not found", tag);
294 }
295
296 /* Restore the value of a variable, based on the contents of SV.
297 sv->desired_setting is a block of memory SIZE bytes long holding the
298 value itself. This block of memory is copied back into the variable. */
299 static inline void
300 restore_variable (sv)
301 SAVED_VAR *sv;
302 {
303 FASTCOPY (sv->desired_setting, sv->variable, sv->size);
304 }
305
306 static void
307 unwind_frame_run_internal (tag, ignore)
308 char *tag, *ignore;
309 {
310 UNWIND_ELT *elt;
311 int found;
312
313 found = 0;
314 while (elt = unwind_protect_list)
315 {
316 unwind_protect_list = elt->head.next;
317
318 /* If tag, then compare. */
319 if (elt->head.cleanup == 0)
320 {
321 if (tag && STREQ (elt->arg.v, tag))
322 {
323 uwpfree (elt);
324 found = 1;
325 break;
326 }
327 }
328 else
329 {
330 if (elt->head.cleanup == (Function *) restore_variable)
331 restore_variable (&elt->sv.v);
332 else
333 (*(elt->head.cleanup)) (elt->arg.v);
334 }
335
336 uwpfree (elt);
337 }
338 if (tag && found == 0)
339 internal_warning ("unwind_frame_run: %s: frame not found", tag);
340 }
341
342 static void
343 unwind_protect_mem_internal (var, psize)
344 char *var;
345 char *psize;
346 {
347 int size, allocated;
348 UNWIND_ELT *elt;
349
350 size = *(int *) psize;
351 allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]);
352 elt = (UNWIND_ELT *)xmalloc (allocated);
353 elt->head.next = unwind_protect_list;
354 elt->head.cleanup = (Function *) restore_variable;
355 elt->sv.v.variable = var;
356 elt->sv.v.size = size;
357 FASTCOPY (var, elt->sv.v.desired_setting, size);
358 unwind_protect_list = elt;
359 }
360
361 /* Save the value of a variable so it will be restored when unwind-protects
362 are run. VAR is a pointer to the variable. SIZE is the size in
363 bytes of VAR. */
364 void
365 unwind_protect_mem (var, size)
366 char *var;
367 int size;
368 {
369 without_interrupts (unwind_protect_mem_internal, var, (char *) &size);
370 }
371
372 #if defined (DEBUG)
373 #include <stdio.h>
374
375 void
376 print_unwind_protect_tags ()
377 {
378 UNWIND_ELT *elt;
379
380 elt = unwind_protect_list;
381 while (elt)
382 {
383 if (elt->head.cleanup == 0)
384 fprintf(stderr, "tag: %s\n", elt->arg.v);
385 elt = elt->head.next;
386 }
387 }
388 #endif