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 "quit.h"
50#include "sig.h"
51
52/* Structure describing a saved variable and the value to restore it to.  */
53typedef struct {
54  char *variable;
55  int size;
56  char desired_setting[1]; /* actual size is `size' */
57} SAVED_VAR;
58
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
77
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 *));
87
88static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;
89
90#define uwpalloc(elt)	(elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT))
91#define uwpfree(elt)	free(elt)
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
164/* Erase the unwind-protect list.  If flags is 1, free the elements. */
165void
166clear_unwind_protect_list (flags)
167     int flags;
168{
169  char *flag;
170
171  if (unwind_protect_list)
172    {
173      flag = flags ? "" : (char *)NULL;
174      without_interrupts
175        (clear_unwind_protects_internal, flag, (char *)NULL);
176    }
177}
178
179int
180have_unwind_protects ()
181{
182  return (unwind_protect_list != 0);
183}
184
185/* **************************************************************** */
186/*								    */
187/*			The Actual Functions		 	    */
188/*								    */
189/* **************************************************************** */
190
191static void
192add_unwind_protect_internal (cleanup, arg)
193     Function *cleanup;
194     char *arg;
195{
196  UNWIND_ELT *elt;
197
198  uwpalloc (elt);
199  elt->head.next = unwind_protect_list;
200  elt->head.cleanup = cleanup;
201  elt->arg.v = arg;
202  unwind_protect_list = elt;
203}
204
205static void
206remove_unwind_protect_internal (ignore1, ignore2)
207     char *ignore1, *ignore2;
208{
209  UNWIND_ELT *elt;
210
211  elt = unwind_protect_list;
212  if (elt)
213    {
214      unwind_protect_list = unwind_protect_list->head.next;
215      uwpfree (elt);
216    }
217}
218
219static void
220run_unwind_protects_internal (ignore1, ignore2)
221     char *ignore1, *ignore2;
222{
223  unwind_frame_run_internal ((char *) NULL, (char *) NULL);
224}
225
226static void
227clear_unwind_protects_internal (flag, ignore)
228     char *flag, *ignore;
229{
230  if (flag)
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;
241{
242  UNWIND_ELT *elt;
243
244  while (elt = unwind_protect_list)
245    {
246      unwind_protect_list = unwind_protect_list->head.next;
247      if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag)))
248	{
249	  uwpfree (elt);
250	  break;
251	}
252      else
253	uwpfree (elt);
254    }
255}
256
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
267static void
268unwind_frame_run_internal (tag, ignore)
269     char *tag, *ignore;
270{
271  UNWIND_ELT *elt;
272
273  while (elt = unwind_protect_list)
274    {
275      unwind_protect_list = elt->head.next;
276
277      /* If tag, then compare. */
278      if (!elt->head.cleanup)
279	{
280	  if (tag && STREQ (elt->arg.v, tag))
281	    {
282	      uwpfree (elt);
283	      break;
284	    }
285	}
286      else
287	{
288	  if (elt->head.cleanup == (Function *) restore_variable)
289	    restore_variable (&elt->sv.v);
290	  else
291	    (*(elt->head.cleanup)) (elt->arg.v);
292	}
293
294      uwpfree (elt);
295    }
296}
297
298static void
299unwind_protect_mem_internal (var, psize)
300     char *var;
301     char *psize;
302{
303  int size, allocated;
304  UNWIND_ELT *elt;
305
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;
315}
316
317/* Save the value of a variable so it will be restored when unwind-protects
318   are run.  VAR is a pointer to the variable.  SIZE is the size in
319   bytes of VAR.  */
320void
321unwind_protect_mem (var, size)
322     char *var;
323     int size;
324{
325  without_interrupts (unwind_protect_mem_internal, var, (char *) &size);
326}
327