]> git.ipfire.org Git - thirdparty/bash.git/blame - unwind_prot.c
Imported from ../bash-4.0-rc1.tar.gz.
[thirdparty/bash.git] / unwind_prot.c
CommitLineData
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
3185942a 6/* Copyright (C) 1987-2009 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
f73dda09
JA
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
726f6388
JA
46#include "command.h"
47#include "general.h"
48#include "unwind_prot.h"
49#include "quit.h"
ccc6cda3 50#include "sig.h"
726f6388 51
f73dda09 52/* Structure describing a saved variable and the value to restore it to. */
d166f048 53typedef struct {
f73dda09 54 char *variable;
d166f048 55 int size;
f73dda09 56 char desired_setting[1]; /* actual size is `size' */
d166f048
JA
57} SAVED_VAR;
58
f73dda09
JA
59/* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to.
60 If HEAD.CLEANUP is restore_variable, then SV.V contains the saved
61 variable. Otherwise, call HEAD.CLEANUP (ARG.V) to clean up. */
62typedef union uwp {
63 struct uwp_head {
64 union uwp *next;
65 Function *cleanup;
66 } head;
67 struct {
68 struct uwp_head uwp_head;
69 char *v;
70 } arg;
71 struct {
72 struct uwp_head uwp_head;
73 SAVED_VAR v;
74 } sv;
75} UNWIND_ELT;
76
7117c2d2 77
f73dda09
JA
78static void without_interrupts __P((VFunction *, char *, char *));
79static void unwind_frame_discard_internal __P((char *, char *));
80static void unwind_frame_run_internal __P((char *, char *));
81static void add_unwind_protect_internal __P((Function *, char *));
82static void remove_unwind_protect_internal __P((char *, char *));
83static void run_unwind_protects_internal __P((char *, char *));
84static void clear_unwind_protects_internal __P((char *, char *));
85static inline void restore_variable __P((SAVED_VAR *));
86static void unwind_protect_mem_internal __P((char *, char *));
726f6388
JA
87
88static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;
89
7117c2d2
JA
90#define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT))
91#define uwpfree(elt) free(elt)
726f6388
JA
92
93/* Run a function without interrupts. This relies on the fact that the
94 FUNCTION cannot change the value of interrupt_immediately. (I.e., does
95 not call QUIT (). */
96static void
97without_interrupts (function, arg1, arg2)
98 VFunction *function;
99 char *arg1, *arg2;
100{
101 int old_interrupt_immediately;
102
103 old_interrupt_immediately = interrupt_immediately;
104 interrupt_immediately = 0;
105
106 (*function)(arg1, arg2);
107
108 interrupt_immediately = old_interrupt_immediately;
109}
110
111/* Start the beginning of a region. */
112void
113begin_unwind_frame (tag)
114 char *tag;
115{
116 add_unwind_protect ((Function *)NULL, tag);
117}
118
119/* Discard the unwind protects back to TAG. */
120void
121discard_unwind_frame (tag)
122 char *tag;
123{
124 if (unwind_protect_list)
125 without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL);
126}
127
128/* Run the unwind protects back to TAG. */
129void
130run_unwind_frame (tag)
131 char *tag;
132{
133 if (unwind_protect_list)
134 without_interrupts (unwind_frame_run_internal, tag, (char *)NULL);
135}
136
137/* Add the function CLEANUP with ARG to the list of unwindable things. */
138void
139add_unwind_protect (cleanup, arg)
140 Function *cleanup;
141 char *arg;
142{
143 without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg);
144}
145
146/* Remove the top unwind protect from the list. */
147void
148remove_unwind_protect ()
149{
150 if (unwind_protect_list)
151 without_interrupts
152 (remove_unwind_protect_internal, (char *)NULL, (char *)NULL);
153}
154
155/* Run the list of cleanup functions in unwind_protect_list. */
156void
157run_unwind_protects ()
158{
159 if (unwind_protect_list)
160 without_interrupts
161 (run_unwind_protects_internal, (char *)NULL, (char *)NULL);
162}
163
28ef6c31
JA
164/* Erase the unwind-protect list. If flags is 1, free the elements. */
165void
166clear_unwind_protect_list (flags)
167 int flags;
168{
f73dda09
JA
169 char *flag;
170
28ef6c31 171 if (unwind_protect_list)
f73dda09
JA
172 {
173 flag = flags ? "" : (char *)NULL;
174 without_interrupts
175 (clear_unwind_protects_internal, flag, (char *)NULL);
176 }
28ef6c31
JA
177}
178
3185942a
JA
179int
180have_unwind_protects ()
181{
182 return (unwind_protect_list != 0);
183}
184
726f6388
JA
185/* **************************************************************** */
186/* */
28ef6c31 187/* The Actual Functions */
726f6388
JA
188/* */
189/* **************************************************************** */
190
191static void
192add_unwind_protect_internal (cleanup, arg)
193 Function *cleanup;
194 char *arg;
195{
196 UNWIND_ELT *elt;
197
7117c2d2 198 uwpalloc (elt);
f73dda09
JA
199 elt->head.next = unwind_protect_list;
200 elt->head.cleanup = cleanup;
201 elt->arg.v = arg;
726f6388
JA
202 unwind_protect_list = elt;
203}
204
205static void
28ef6c31
JA
206remove_unwind_protect_internal (ignore1, ignore2)
207 char *ignore1, *ignore2;
726f6388 208{
d166f048 209 UNWIND_ELT *elt;
726f6388 210
d166f048 211 elt = unwind_protect_list;
726f6388
JA
212 if (elt)
213 {
f73dda09 214 unwind_protect_list = unwind_protect_list->head.next;
7117c2d2 215 uwpfree (elt);
726f6388
JA
216 }
217}
218
219static void
28ef6c31
JA
220run_unwind_protects_internal (ignore1, ignore2)
221 char *ignore1, *ignore2;
726f6388 222{
f73dda09 223 unwind_frame_run_internal ((char *) NULL, (char *) NULL);
726f6388
JA
224}
225
226static void
28ef6c31
JA
227clear_unwind_protects_internal (flag, ignore)
228 char *flag, *ignore;
229{
f73dda09 230 if (flag)
28ef6c31
JA
231 {
232 while (unwind_protect_list)
233 remove_unwind_protect_internal ((char *)NULL, (char *)NULL);
234 }
235 unwind_protect_list = (UNWIND_ELT *)NULL;
236}
237
238static void
239unwind_frame_discard_internal (tag, ignore)
240 char *tag, *ignore;
726f6388
JA
241{
242 UNWIND_ELT *elt;
243
244 while (elt = unwind_protect_list)
245 {
f73dda09
JA
246 unwind_protect_list = unwind_protect_list->head.next;
247 if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag)))
726f6388 248 {
7117c2d2 249 uwpfree (elt);
726f6388
JA
250 break;
251 }
252 else
7117c2d2 253 uwpfree (elt);
726f6388
JA
254 }
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. */
260static inline void
261restore_variable (sv)
262 SAVED_VAR *sv;
263{
264 FASTCOPY (sv->desired_setting, sv->variable, sv->size);
265}
266
726f6388 267static void
28ef6c31
JA
268unwind_frame_run_internal (tag, ignore)
269 char *tag, *ignore;
726f6388
JA
270{
271 UNWIND_ELT *elt;
272
273 while (elt = unwind_protect_list)
274 {
f73dda09 275 unwind_protect_list = elt->head.next;
726f6388
JA
276
277 /* If tag, then compare. */
f73dda09 278 if (!elt->head.cleanup)
726f6388 279 {
f73dda09 280 if (tag && STREQ (elt->arg.v, tag))
726f6388 281 {
7117c2d2 282 uwpfree (elt);
726f6388
JA
283 break;
284 }
726f6388
JA
285 }
286 else
287 {
f73dda09
JA
288 if (elt->head.cleanup == (Function *) restore_variable)
289 restore_variable (&elt->sv.v);
290 else
291 (*(elt->head.cleanup)) (elt->arg.v);
726f6388 292 }
726f6388 293
7117c2d2 294 uwpfree (elt);
f73dda09 295 }
d166f048 296}
726f6388 297
726f6388 298static void
f73dda09
JA
299unwind_protect_mem_internal (var, psize)
300 char *var;
301 char *psize;
726f6388 302{
f73dda09
JA
303 int size, allocated;
304 UNWIND_ELT *elt;
726f6388 305
f73dda09
JA
306 size = *(int *) psize;
307 allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]);
308 elt = (UNWIND_ELT *)xmalloc (allocated);
309 elt->head.next = unwind_protect_list;
310 elt->head.cleanup = (Function *) restore_variable;
311 elt->sv.v.variable = var;
312 elt->sv.v.size = size;
313 FASTCOPY (var, elt->sv.v.desired_setting, size);
314 unwind_protect_list = elt;
726f6388
JA
315}
316
317/* Save the value of a variable so it will be restored when unwind-protects
f73dda09
JA
318 are run. VAR is a pointer to the variable. SIZE is the size in
319 bytes of VAR. */
726f6388 320void
f73dda09
JA
321unwind_protect_mem (var, size)
322 char *var;
726f6388
JA
323 int size;
324{
f73dda09 325 without_interrupts (unwind_protect_mem_internal, var, (char *) &size);
726f6388 326}