]> git.ipfire.org Git - thirdparty/bash.git/blame - unwind_prot.c
bash-5.1 distribution sources and documentation
[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
8868edaf 6/* Copyright (C) 1987-2020 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"
ccc6cda3 49#include "sig.h"
ac50fbac 50#include "quit.h"
495aee44 51#include "error.h" /* for internal_warning */
a0c0a00f 52#include "ocache.h"
726f6388 53
f73dda09 54/* Structure describing a saved variable and the value to restore it to. */
d166f048 55typedef struct {
f73dda09 56 char *variable;
d166f048 57 int size;
f73dda09 58 char desired_setting[1]; /* actual size is `size' */
d166f048
JA
59} SAVED_VAR;
60
f73dda09
JA
61/* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to.
62 If HEAD.CLEANUP is restore_variable, then SV.V contains the saved
63 variable. Otherwise, call HEAD.CLEANUP (ARG.V) to clean up. */
64typedef union uwp {
65 struct uwp_head {
66 union uwp *next;
67 Function *cleanup;
68 } head;
69 struct {
70 struct uwp_head uwp_head;
71 char *v;
72 } arg;
73 struct {
74 struct uwp_head uwp_head;
75 SAVED_VAR v;
76 } sv;
77} UNWIND_ELT;
78
8868edaf
CR
79static void without_interrupts PARAMS((VFunction *, char *, char *));
80static void unwind_frame_discard_internal PARAMS((char *, char *));
81static void unwind_frame_run_internal PARAMS((char *, char *));
82static void add_unwind_protect_internal PARAMS((Function *, char *));
83static void remove_unwind_protect_internal PARAMS((char *, char *));
84static void run_unwind_protects_internal PARAMS((char *, char *));
85static void clear_unwind_protects_internal PARAMS((char *, char *));
86static inline void restore_variable PARAMS((SAVED_VAR *));
87static void unwind_protect_mem_internal PARAMS((char *, char *));
726f6388
JA
88
89static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;
90
a0c0a00f
CR
91/* Allocating from a cache of unwind-protect elements */
92#define UWCACHESIZE 128
93
94sh_obj_cache_t uwcache = {0, 0, 0};
95
96#if 0
7117c2d2
JA
97#define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT))
98#define uwpfree(elt) free(elt)
a0c0a00f
CR
99#else
100#define uwpalloc(elt) ocache_alloc (uwcache, UNWIND_ELT, elt)
101#define uwpfree(elt) ocache_free (uwcache, UNWIND_ELT, elt)
102#endif
103
104void
105uwp_init ()
106{
107 ocache_create (uwcache, UNWIND_ELT, UWCACHESIZE);
108}
726f6388
JA
109
110/* Run a function without interrupts. This relies on the fact that the
8868edaf 111 FUNCTION cannot call QUIT (). */
726f6388
JA
112static void
113without_interrupts (function, arg1, arg2)
114 VFunction *function;
115 char *arg1, *arg2;
116{
726f6388 117 (*function)(arg1, arg2);
726f6388
JA
118}
119
120/* Start the beginning of a region. */
121void
122begin_unwind_frame (tag)
123 char *tag;
124{
125 add_unwind_protect ((Function *)NULL, tag);
126}
127
128/* Discard the unwind protects back to TAG. */
129void
130discard_unwind_frame (tag)
131 char *tag;
132{
133 if (unwind_protect_list)
134 without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL);
135}
136
137/* Run the unwind protects back to TAG. */
138void
139run_unwind_frame (tag)
140 char *tag;
141{
142 if (unwind_protect_list)
143 without_interrupts (unwind_frame_run_internal, tag, (char *)NULL);
144}
145
146/* Add the function CLEANUP with ARG to the list of unwindable things. */
147void
148add_unwind_protect (cleanup, arg)
149 Function *cleanup;
150 char *arg;
151{
152 without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg);
153}
154
155/* Remove the top unwind protect from the list. */
156void
157remove_unwind_protect ()
158{
159 if (unwind_protect_list)
160 without_interrupts
161 (remove_unwind_protect_internal, (char *)NULL, (char *)NULL);
162}
163
164/* Run the list of cleanup functions in unwind_protect_list. */
165void
166run_unwind_protects ()
167{
168 if (unwind_protect_list)
169 without_interrupts
170 (run_unwind_protects_internal, (char *)NULL, (char *)NULL);
171}
172
28ef6c31
JA
173/* Erase the unwind-protect list. If flags is 1, free the elements. */
174void
175clear_unwind_protect_list (flags)
176 int flags;
177{
f73dda09
JA
178 char *flag;
179
28ef6c31 180 if (unwind_protect_list)
f73dda09
JA
181 {
182 flag = flags ? "" : (char *)NULL;
183 without_interrupts
184 (clear_unwind_protects_internal, flag, (char *)NULL);
185 }
28ef6c31
JA
186}
187
3185942a
JA
188int
189have_unwind_protects ()
190{
191 return (unwind_protect_list != 0);
192}
193
a0c0a00f
CR
194int
195unwind_protect_tag_on_stack (tag)
196 const char *tag;
197{
198 UNWIND_ELT *elt;
199
200 elt = unwind_protect_list;
201 while (elt)
202 {
203 if (elt->head.cleanup == 0 && STREQ (elt->arg.v, tag))
204 return 1;
205 elt = elt->head.next;
206 }
207 return 0;
208}
209
726f6388
JA
210/* **************************************************************** */
211/* */
28ef6c31 212/* The Actual Functions */
726f6388
JA
213/* */
214/* **************************************************************** */
215
216static void
217add_unwind_protect_internal (cleanup, arg)
218 Function *cleanup;
219 char *arg;
220{
221 UNWIND_ELT *elt;
222
7117c2d2 223 uwpalloc (elt);
f73dda09
JA
224 elt->head.next = unwind_protect_list;
225 elt->head.cleanup = cleanup;
226 elt->arg.v = arg;
726f6388
JA
227 unwind_protect_list = elt;
228}
229
230static void
28ef6c31
JA
231remove_unwind_protect_internal (ignore1, ignore2)
232 char *ignore1, *ignore2;
726f6388 233{
d166f048 234 UNWIND_ELT *elt;
726f6388 235
d166f048 236 elt = unwind_protect_list;
726f6388
JA
237 if (elt)
238 {
f73dda09 239 unwind_protect_list = unwind_protect_list->head.next;
7117c2d2 240 uwpfree (elt);
726f6388
JA
241 }
242}
243
244static void
28ef6c31
JA
245run_unwind_protects_internal (ignore1, ignore2)
246 char *ignore1, *ignore2;
726f6388 247{
f73dda09 248 unwind_frame_run_internal ((char *) NULL, (char *) NULL);
726f6388
JA
249}
250
251static void
28ef6c31
JA
252clear_unwind_protects_internal (flag, ignore)
253 char *flag, *ignore;
254{
f73dda09 255 if (flag)
28ef6c31
JA
256 {
257 while (unwind_protect_list)
258 remove_unwind_protect_internal ((char *)NULL, (char *)NULL);
259 }
260 unwind_protect_list = (UNWIND_ELT *)NULL;
261}
262
263static void
264unwind_frame_discard_internal (tag, ignore)
265 char *tag, *ignore;
726f6388
JA
266{
267 UNWIND_ELT *elt;
495aee44 268 int found;
726f6388 269
495aee44 270 found = 0;
726f6388
JA
271 while (elt = unwind_protect_list)
272 {
f73dda09
JA
273 unwind_protect_list = unwind_protect_list->head.next;
274 if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag)))
726f6388 275 {
7117c2d2 276 uwpfree (elt);
495aee44 277 found = 1;
726f6388
JA
278 break;
279 }
280 else
7117c2d2 281 uwpfree (elt);
726f6388 282 }
495aee44
CR
283
284 if (found == 0)
285 internal_warning ("unwind_frame_discard: %s: frame not found", tag);
726f6388
JA
286}
287
f73dda09
JA
288/* Restore the value of a variable, based on the contents of SV.
289 sv->desired_setting is a block of memory SIZE bytes long holding the
290 value itself. This block of memory is copied back into the variable. */
291static inline void
292restore_variable (sv)
293 SAVED_VAR *sv;
294{
295 FASTCOPY (sv->desired_setting, sv->variable, sv->size);
296}
297
726f6388 298static void
28ef6c31
JA
299unwind_frame_run_internal (tag, ignore)
300 char *tag, *ignore;
726f6388
JA
301{
302 UNWIND_ELT *elt;
495aee44 303 int found;
726f6388 304
495aee44 305 found = 0;
726f6388
JA
306 while (elt = unwind_protect_list)
307 {
f73dda09 308 unwind_protect_list = elt->head.next;
726f6388
JA
309
310 /* If tag, then compare. */
495aee44 311 if (elt->head.cleanup == 0)
726f6388 312 {
f73dda09 313 if (tag && STREQ (elt->arg.v, tag))
726f6388 314 {
7117c2d2 315 uwpfree (elt);
495aee44 316 found = 1;
726f6388
JA
317 break;
318 }
726f6388
JA
319 }
320 else
321 {
f73dda09
JA
322 if (elt->head.cleanup == (Function *) restore_variable)
323 restore_variable (&elt->sv.v);
324 else
325 (*(elt->head.cleanup)) (elt->arg.v);
726f6388 326 }
726f6388 327
7117c2d2 328 uwpfree (elt);
f73dda09 329 }
495aee44
CR
330 if (tag && found == 0)
331 internal_warning ("unwind_frame_run: %s: frame not found", tag);
d166f048 332}
726f6388 333
726f6388 334static void
f73dda09
JA
335unwind_protect_mem_internal (var, psize)
336 char *var;
337 char *psize;
726f6388 338{
f73dda09
JA
339 int size, allocated;
340 UNWIND_ELT *elt;
726f6388 341
f73dda09
JA
342 size = *(int *) psize;
343 allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]);
8868edaf
CR
344 if (allocated < sizeof (UNWIND_ELT))
345 allocated = sizeof (UNWIND_ELT);
f73dda09
JA
346 elt = (UNWIND_ELT *)xmalloc (allocated);
347 elt->head.next = unwind_protect_list;
348 elt->head.cleanup = (Function *) restore_variable;
349 elt->sv.v.variable = var;
350 elt->sv.v.size = size;
351 FASTCOPY (var, elt->sv.v.desired_setting, size);
352 unwind_protect_list = elt;
726f6388
JA
353}
354
355/* Save the value of a variable so it will be restored when unwind-protects
f73dda09
JA
356 are run. VAR is a pointer to the variable. SIZE is the size in
357 bytes of VAR. */
726f6388 358void
f73dda09
JA
359unwind_protect_mem (var, size)
360 char *var;
726f6388
JA
361 int size;
362{
f73dda09 363 without_interrupts (unwind_protect_mem_internal, var, (char *) &size);
726f6388 364}
495aee44
CR
365
366#if defined (DEBUG)
367#include <stdio.h>
368
369void
370print_unwind_protect_tags ()
371{
372 UNWIND_ELT *elt;
373
374 elt = unwind_protect_list;
375 while (elt)
376 {
495aee44
CR
377 if (elt->head.cleanup == 0)
378 fprintf(stderr, "tag: %s\n", elt->arg.v);
a0c0a00f 379 elt = elt->head.next;
495aee44
CR
380 }
381}
382#endif