]> git.ipfire.org Git - thirdparty/bash.git/blob - unwind_prot.c
17144dc70c4a95a6396260c81230915234f6d1dc
[thirdparty/bash.git] / unwind_prot.c
1 /* I can't stand it anymore! Please can't we just write the
2 whole Unix system in lisp or something? */
3
4 /* Copyright (C) 1987,1989 Free Software Foundation, Inc.
5
6 This file is part of GNU Bash, the Bourne Again SHell.
7
8 Bash is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 1, or (at your option) any later
11 version.
12
13 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with Bash; see the file COPYING. If not, write to the Free Software
20 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21
22 /* **************************************************************** */
23 /* */
24 /* Unwind Protection Scheme for Bash */
25 /* */
26 /* **************************************************************** */
27 #include "config.h"
28
29 #include "bashtypes.h"
30 #include "bashansi.h"
31
32 #if defined (HAVE_UNISTD_H)
33 # include <unistd.h>
34 #endif
35
36 #include "command.h"
37 #include "general.h"
38 #include "unwind_prot.h"
39 #include "quit.h"
40 #include "sig.h"
41
42 /* If CLEANUP is null, then ARG contains a tag to throw back to. */
43 typedef struct _uwp {
44 struct _uwp *next;
45 Function *cleanup;
46 char *arg;
47 } UNWIND_ELT;
48
49 /* Structure describing a saved variable and the value to restore it to.
50 If a cleanup function is set to restore_variable, the `arg' pointer
51 points to this. */
52 typedef struct {
53 int *variable;
54 char *desired_setting;
55 int size;
56 } SAVED_VAR;
57
58 static void unwind_frame_discard_internal (), unwind_frame_run_internal ();
59 static void add_unwind_protect_internal (), remove_unwind_protect_internal ();
60 static void run_unwind_protects_internal (), without_interrupts ();
61
62 static void restore_variable ();
63 static void discard_saved_var ();
64
65 static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;
66
67 extern int interrupt_immediately;
68
69 /* Run a function without interrupts. This relies on the fact that the
70 FUNCTION cannot change the value of interrupt_immediately. (I.e., does
71 not call QUIT (). */
72 static void
73 without_interrupts (function, arg1, arg2)
74 VFunction *function;
75 char *arg1, *arg2;
76 {
77 int old_interrupt_immediately;
78
79 old_interrupt_immediately = interrupt_immediately;
80 interrupt_immediately = 0;
81
82 (*function)(arg1, arg2);
83
84 interrupt_immediately = old_interrupt_immediately;
85 }
86
87 /* Start the beginning of a region. */
88 void
89 begin_unwind_frame (tag)
90 char *tag;
91 {
92 add_unwind_protect ((Function *)NULL, tag);
93 }
94
95 /* Discard the unwind protects back to TAG. */
96 void
97 discard_unwind_frame (tag)
98 char *tag;
99 {
100 if (unwind_protect_list)
101 without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL);
102 }
103
104 /* Run the unwind protects back to TAG. */
105 void
106 run_unwind_frame (tag)
107 char *tag;
108 {
109 if (unwind_protect_list)
110 without_interrupts (unwind_frame_run_internal, tag, (char *)NULL);
111 }
112
113 /* Add the function CLEANUP with ARG to the list of unwindable things. */
114 void
115 add_unwind_protect (cleanup, arg)
116 Function *cleanup;
117 char *arg;
118 {
119 without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg);
120 }
121
122 /* Remove the top unwind protect from the list. */
123 void
124 remove_unwind_protect ()
125 {
126 if (unwind_protect_list)
127 without_interrupts
128 (remove_unwind_protect_internal, (char *)NULL, (char *)NULL);
129 }
130
131 /* Run the list of cleanup functions in unwind_protect_list. */
132 void
133 run_unwind_protects ()
134 {
135 if (unwind_protect_list)
136 without_interrupts
137 (run_unwind_protects_internal, (char *)NULL, (char *)NULL);
138 }
139
140 /* **************************************************************** */
141 /* */
142 /* The Actual Functions */
143 /* */
144 /* **************************************************************** */
145
146 static void
147 add_unwind_protect_internal (cleanup, arg)
148 Function *cleanup;
149 char *arg;
150 {
151 UNWIND_ELT *elt;
152
153 elt = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT));
154 elt->cleanup = cleanup;
155 elt->arg = arg;
156 elt->next = unwind_protect_list;
157 unwind_protect_list = elt;
158 }
159
160 static void
161 remove_unwind_protect_internal ()
162 {
163 UNWIND_ELT *elt;
164
165 elt = unwind_protect_list;
166 if (elt)
167 {
168 unwind_protect_list = unwind_protect_list->next;
169 if (elt->cleanup && elt->cleanup == (Function *)restore_variable)
170 discard_saved_var ((SAVED_VAR *)elt->arg);
171 free (elt);
172 }
173 }
174
175 static void
176 run_unwind_protects_internal ()
177 {
178 UNWIND_ELT *t, *elt = unwind_protect_list;
179
180 while (elt)
181 {
182 /* This function can be run at strange times, like when unwinding
183 the entire world of unwind protects. Thus, we may come across
184 an element which is simply a label for a catch frame. Don't call
185 the non-existant function. */
186 if (elt->cleanup)
187 (*(elt->cleanup)) (elt->arg);
188
189 t = elt;
190 elt = elt->next;
191 free (t);
192 }
193 unwind_protect_list = elt;
194 }
195
196 static void
197 unwind_frame_discard_internal (tag)
198 char *tag;
199 {
200 UNWIND_ELT *elt;
201
202 while (elt = unwind_protect_list)
203 {
204 unwind_protect_list = unwind_protect_list->next;
205 if (elt->cleanup == 0 && (STREQ (elt->arg, tag)))
206 {
207 free (elt);
208 break;
209 }
210 else if (elt->cleanup && elt->cleanup == (Function *)restore_variable)
211 {
212 discard_saved_var ((SAVED_VAR *)elt->arg);
213 free (elt);
214 }
215 else
216 free (elt);
217 }
218 }
219
220 static void
221 unwind_frame_run_internal (tag)
222 char *tag;
223 {
224 UNWIND_ELT *elt;
225
226 while (elt = unwind_protect_list)
227 {
228 unwind_protect_list = elt->next;
229
230 /* If tag, then compare. */
231 if (!elt->cleanup)
232 {
233 if (STREQ (elt->arg, tag))
234 {
235 free (elt);
236 break;
237 }
238 free (elt);
239 continue;
240 }
241 else
242 {
243 (*(elt->cleanup)) (elt->arg);
244 free (elt);
245 }
246 }
247 }
248
249 static void
250 discard_saved_var (sv)
251 SAVED_VAR *sv;
252 {
253 if (sv->size != sizeof (int))
254 free (sv->desired_setting);
255 free (sv);
256 }
257
258 /* Restore the value of a variable, based on the contents of SV. If
259 sv->size is greater than sizeof (int), sv->desired_setting points to
260 a block of memory SIZE bytes long holding the value, rather than the
261 value itself. This block of memory is copied back into the variable. */
262 static void
263 restore_variable (sv)
264 SAVED_VAR *sv;
265 {
266 if (sv->size != sizeof (int))
267 {
268 FASTCOPY ((char *)sv->desired_setting, (char *)sv->variable, sv->size);
269 free (sv->desired_setting);
270 }
271 else
272 *(sv->variable) = (int)sv->desired_setting;
273
274 free (sv);
275 }
276
277 /* Save the value of a variable so it will be restored when unwind-protects
278 are run. VAR is a pointer to the variable. VALUE is the value to be
279 saved. SIZE is the size in bytes of VALUE. If SIZE is bigger than what
280 can be saved in an int, memory will be allocated and the value saved
281 into that using bcopy (). */
282 void
283 unwind_protect_var (var, value, size)
284 int *var;
285 char *value;
286 int size;
287 {
288 SAVED_VAR *s = (SAVED_VAR *)xmalloc (sizeof (SAVED_VAR));
289
290 s->variable = var;
291 if (size != sizeof (int))
292 {
293 /* There is a problem here when VALUE is 0. This tries to copy the
294 first SIZE bytes starting at memory location 0 into
295 s->desired_setting. There is no guarantee that these bytes are
296 0, or make a valid null pointer. We can try to bzero the space,
297 or just save it as 0 (or (void *)0). If we do the latter, make
298 sure restore_variable is changed to understand it. */
299 s->desired_setting = (char *)xmalloc (size);
300 if (value == 0)
301 bzero ((char *)s->desired_setting, size);
302 else
303 FASTCOPY (value, (char *)s->desired_setting, size);
304 }
305 else
306 s->desired_setting = value;
307 s->size = size;
308 add_unwind_protect ((Function *)restore_variable, (char *)s);
309 }