628845c5e347b121e1b19233595a5dc1a57aa9c6
[platform/upstream/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 2, 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, 59 Temple Place, Suite 330, Boston, MA 02111 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 without_interrupts ();
59 static void unwind_frame_discard_internal ();
60 static void unwind_frame_run_internal ();
61 static void add_unwind_protect_internal ();
62 static void remove_unwind_protect_internal ();
63 static void run_unwind_protects_internal ();
64 static void clear_unwind_protects_internal ();
65 static void restore_variable ();
66 static void discard_saved_var ();
67
68 static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;
69
70 extern int interrupt_immediately;
71
72 /* Run a function without interrupts.  This relies on the fact that the
73    FUNCTION cannot change the value of interrupt_immediately.  (I.e., does
74    not call QUIT (). */
75 static void
76 without_interrupts (function, arg1, arg2)
77      VFunction *function;
78      char *arg1, *arg2;
79 {
80   int old_interrupt_immediately;
81
82   old_interrupt_immediately = interrupt_immediately;
83   interrupt_immediately = 0;
84
85   (*function)(arg1, arg2);
86
87   interrupt_immediately = old_interrupt_immediately;
88 }
89
90 /* Start the beginning of a region. */
91 void
92 begin_unwind_frame (tag)
93      char *tag;
94 {
95   add_unwind_protect ((Function *)NULL, tag);
96 }
97
98 /* Discard the unwind protects back to TAG. */
99 void
100 discard_unwind_frame (tag)
101      char *tag;
102 {
103   if (unwind_protect_list)
104     without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL);
105 }
106
107 /* Run the unwind protects back to TAG. */
108 void
109 run_unwind_frame (tag)
110      char *tag;
111 {
112   if (unwind_protect_list)
113     without_interrupts (unwind_frame_run_internal, tag, (char *)NULL);
114 }
115
116 /* Add the function CLEANUP with ARG to the list of unwindable things. */
117 void
118 add_unwind_protect (cleanup, arg)
119      Function *cleanup;
120      char *arg;
121 {
122   without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg);
123 }
124
125 /* Remove the top unwind protect from the list. */
126 void
127 remove_unwind_protect ()
128 {
129   if (unwind_protect_list)
130     without_interrupts
131       (remove_unwind_protect_internal, (char *)NULL, (char *)NULL);
132 }
133
134 /* Run the list of cleanup functions in unwind_protect_list. */
135 void
136 run_unwind_protects ()
137 {
138   if (unwind_protect_list)
139     without_interrupts
140       (run_unwind_protects_internal, (char *)NULL, (char *)NULL);
141 }
142
143 /* Erase the unwind-protect list.  If flags is 1, free the elements. */
144 void
145 clear_unwind_protect_list (flags)
146      int flags;
147 {
148   if (unwind_protect_list)
149     without_interrupts
150       (clear_unwind_protects_internal, (char *)flags, (char *)NULL);
151 }
152
153 /* **************************************************************** */
154 /*                                                                  */
155 /*                      The Actual Functions                        */
156 /*                                                                  */
157 /* **************************************************************** */
158
159 static void
160 add_unwind_protect_internal (cleanup, arg)
161      Function *cleanup;
162      char *arg;
163 {
164   UNWIND_ELT *elt;
165
166   elt = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT));
167   elt->cleanup = cleanup;
168   elt->arg = arg;
169   elt->next = unwind_protect_list;
170   unwind_protect_list = elt;
171 }
172
173 static void
174 remove_unwind_protect_internal (ignore1, ignore2)
175      char *ignore1, *ignore2;
176 {
177   UNWIND_ELT *elt;
178
179   elt = unwind_protect_list;
180   if (elt)
181     {
182       unwind_protect_list = unwind_protect_list->next;
183       if (elt->cleanup && elt->cleanup == (Function *)restore_variable)
184         discard_saved_var ((SAVED_VAR *)elt->arg);
185       free (elt);
186     }
187 }
188
189 static void
190 run_unwind_protects_internal (ignore1, ignore2)
191      char *ignore1, *ignore2;
192 {
193   UNWIND_ELT *t, *elt = unwind_protect_list;
194
195   while (elt)
196    {
197       /* This function can be run at strange times, like when unwinding
198          the entire world of unwind protects.  Thus, we may come across
199          an element which is simply a label for a catch frame.  Don't call
200          the non-existant function. */
201       if (elt->cleanup)
202         (*(elt->cleanup)) (elt->arg);
203
204       t = elt;
205       elt = elt->next;
206       free (t);
207     }
208   unwind_protect_list = elt;
209 }
210
211 static void
212 clear_unwind_protects_internal (flag, ignore)
213      char *flag, *ignore;
214 {
215   int free_elts = (int)flag;
216   UNWIND_ELT *elt;
217
218   if (free_elts != 0 && unwind_protect_list)
219     {
220       while (unwind_protect_list)
221         remove_unwind_protect_internal ((char *)NULL, (char *)NULL);
222     }
223   unwind_protect_list = (UNWIND_ELT *)NULL;
224 }
225
226 static void
227 unwind_frame_discard_internal (tag, ignore)
228      char *tag, *ignore;
229 {
230   UNWIND_ELT *elt;
231
232   while (elt = unwind_protect_list)
233     {
234       unwind_protect_list = unwind_protect_list->next;
235       if (elt->cleanup == 0 && (STREQ (elt->arg, tag)))
236         {
237           free (elt);
238           break;
239         }
240       else if (elt->cleanup && elt->cleanup == (Function *)restore_variable)
241         {
242           discard_saved_var ((SAVED_VAR *)elt->arg);
243           free (elt);
244         }
245       else
246         free (elt);
247     }
248 }
249
250 static void
251 unwind_frame_run_internal (tag, ignore)
252      char *tag, *ignore;
253 {
254   UNWIND_ELT *elt;
255
256   while (elt = unwind_protect_list)
257     {
258       unwind_protect_list = elt->next;
259
260       /* If tag, then compare. */
261       if (!elt->cleanup)
262         {
263           if (STREQ (elt->arg, tag))
264             {
265               free (elt);
266               break;
267             }
268           free (elt);
269           continue;
270         }
271       else
272         {
273           (*(elt->cleanup)) (elt->arg);
274           free (elt);
275         }
276     }
277 }
278
279 static void
280 discard_saved_var (sv)
281      SAVED_VAR *sv;
282 {
283   if (sv->size != sizeof (int))
284     free (sv->desired_setting);
285   free (sv);
286 }
287
288 /* Restore the value of a variable, based on the contents of SV.  If
289    sv->size is greater than sizeof (int), sv->desired_setting points to
290    a block of memory SIZE bytes long holding the value, rather than the
291    value itself.  This block of memory is copied back into the variable. */
292 static void
293 restore_variable (sv)
294      SAVED_VAR *sv;
295 {
296   if (sv->size != sizeof (int))
297     {
298       FASTCOPY ((char *)sv->desired_setting, (char *)sv->variable, sv->size);
299       free (sv->desired_setting);
300     }
301   else
302     *(sv->variable) = (int)sv->desired_setting;
303
304   free (sv);
305 }
306
307 /* Save the value of a variable so it will be restored when unwind-protects
308    are run.  VAR is a pointer to the variable.  VALUE is the value to be
309    saved.  SIZE is the size in bytes of VALUE.  If SIZE is bigger than what
310    can be saved in an int, memory will be allocated and the value saved
311    into that using bcopy (). */
312 void
313 unwind_protect_var (var, value, size)
314      int *var;
315      char *value;
316      int size;
317 {
318   SAVED_VAR *s = (SAVED_VAR *)xmalloc (sizeof (SAVED_VAR));
319
320   s->variable = var;
321   if (size != sizeof (int))
322     {
323       s->desired_setting = (char *)xmalloc (size);
324       FASTCOPY (value, (char *)s->desired_setting, size);
325     }
326   else
327     s->desired_setting = value;
328   s->size = size;
329   add_unwind_protect ((Function *)restore_variable, (char *)s);
330 }