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