Imported from ../bash-2.01.tar.gz.
[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 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 }