1 /*
2 * This file is part of DGD, https://github.com/dworkin/dgd
3 * Copyright (C) 1993-2010 Dworkin B.V.
4 * Copyright (C) 2010 DGD Authors (see the commit log for details)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 # define INCLUDE_FILE_IO
21 # include "dgd.h"
22 # include "str.h"
23 # include "array.h"
24 # include "object.h"
25 # include "data.h"
26 # include "interpret.h"
27 # include "comm.h"
28 # include <stdarg.h>
29
30 typedef struct _context_ {
31 jmp_buf env; /* error context */
32 frame *f; /* frame context */
33 unsigned short offset; /* sp offset */
34 bool atomic; /* atomic status */
35 rlinfo *rlim; /* rlimits info */
36 ec_ftn handler; /* error handler */
37 struct _context_ *next; /* next in linked list */
38 } context;
39
40 static context firstcontext; /* bottom context */
41 static context *econtext; /* current error context */
42 static context *atomicec; /* first context beyond atomic */
43 static string *errstr; /* current error string */
44
45 /*
46 * NAME: errcontext->clear()
47 * DESCRIPTION: clear the error context string
48 */
ec_clear()49 void ec_clear()
50 {
51 if (errstr != (string *) NULL) {
52 str_del(errstr);
53 errstr = (string *) NULL;
54 }
55 }
56
57 /*
58 * NAME: errcontext->_push_()
59 * DESCRIPTION: push and return the current errorcontext
60 */
_ec_push_(ec_ftn handler)61 jmp_buf *_ec_push_(ec_ftn handler)
62 {
63 context *e;
64
65 if (econtext == (context *) NULL) {
66 e = &firstcontext;
67 } else {
68 e = ALLOC(context, 1);
69 }
70 e->f = cframe;
71 e->offset = cframe->fp - cframe->sp;
72 e->atomic = cframe->atomic;
73 e->rlim = cframe->rlim;
74
75 e->handler = handler;
76 e->next = econtext;
77 econtext = e;
78 return &e->env;
79 }
80
81 /*
82 * NAME: errcontext->pop()
83 * DESCRIPTION: pop the current errorcontext
84 */
ec_pop()85 void ec_pop()
86 {
87 context *e;
88
89 e = econtext;
90 # ifdef DEBUG
91 if (e == (context *) NULL) {
92 fatal("pop empty error stack");
93 }
94 # endif
95 cframe->atomic = e->atomic;
96 econtext = e->next;
97 if (e != &firstcontext) {
98 FREE(e);
99 } else {
100 ec_clear();
101 }
102 }
103
104 /*
105 * NAME: errorcontext->handler()
106 * DESCRIPTION: dummy handler for previously handled error
107 */
ec_handler(frame * f,Int depth)108 static void ec_handler(frame *f, Int depth)
109 {
110 UNREFERENCED_PARAMETER(f);
111 UNREFERENCED_PARAMETER(depth);
112 }
113
114 /*
115 * NAME: errorstr()
116 * DESCRIPTION: return the current error string
117 */
errorstr()118 string *errorstr()
119 {
120 return errstr;
121 }
122
123 /*
124 * NAME: serror()
125 * DESCRIPTION: cause an error, with a string argument
126 */
serror(string * str)127 void serror(string *str)
128 {
129 jmp_buf env;
130 context *e;
131 int offset;
132 ec_ftn handler;
133
134 if (str != (string *) NULL) {
135 if (errstr != (string *) NULL) {
136 str_del(errstr);
137 }
138 str_ref(errstr = str);
139 # ifdef DEBUG
140 } else if (errstr == (string *) NULL) {
141 fatal("no error string");
142 # endif
143 }
144
145 e = econtext;
146 offset = e->offset;
147 memcpy(&env, &e->env, sizeof(jmp_buf));
148
149 if (atomicec == (context *) NULL || atomicec == e) {
150 do {
151 if (cframe->level != e->f->level) {
152 if (atomicec == (context *) NULL) {
153 i_atomic_error(cframe, e->f->level);
154 if (e != econtext) {
155 atomicec = e;
156 break; /* handle rollback later */
157 }
158 }
159
160 cframe = i_restore(cframe, e->f->level);
161 atomicec = (context *) NULL;
162 }
163
164 if (e->handler != (ec_ftn) NULL) {
165 handler = e->handler;
166 e->handler = (ec_ftn) ec_handler;
167 (*handler)(cframe, e->f->depth);
168 break;
169 }
170 e = e->next;
171 } while (e != (context *) NULL);
172 }
173
174 if (cframe->rlim != econtext->rlim) {
175 i_set_rlimits(cframe, econtext->rlim);
176 }
177 cframe = i_set_sp(cframe, econtext->f->fp - offset);
178 cframe->atomic = econtext->atomic;
179 cframe->rlim = econtext->rlim;
180 ec_pop();
181 longjmp(env, 1);
182 }
183
184 /*
185 * NAME: error()
186 * DESCRIPTION: cause an error
187 */
error(char * format,...)188 void error(char *format, ...)
189 {
190 va_list args;
191 char ebuf[4 * STRINGSZ];
192
193 if (format != (char *) NULL) {
194 va_start(args, format);
195 vsprintf(ebuf, format, args);
196 serror(str_new(ebuf, (long) strlen(ebuf)));
197 } else {
198 serror((string *) NULL);
199 }
200 }
201
202 /*
203 * NAME: fatal()
204 * DESCRIPTION: a fatal error has been encountered; terminate the program and
205 * dump a core if possible
206 */
fatal(char * format,...)207 void fatal(char *format, ...)
208 {
209 static short count;
210 va_list args;
211 char ebuf1[STRINGSZ], ebuf2[STRINGSZ];
212
213 if (count++ == 0) {
214 va_start(args, format);
215 vsprintf(ebuf1, format, args);
216
217 sprintf(ebuf2, "Fatal error: %s\012", ebuf1); /* LF */
218
219 P_message(ebuf2); /* show message */
220 }
221 abort();
222 }
223
224 /*
225 * NAME: message()
226 * DESCRIPTION: issue a message on stderr
227 */
message(char * format,...)228 void message(char *format, ...)
229 {
230 va_list args;
231 char ebuf[4 * STRINGSZ];
232
233 if (format == (char *) NULL) {
234 # ifdef DEBUG
235 if (errstr == (string *) NULL) {
236 fatal("no error string");
237 }
238 # endif
239 if (errstr->len <= sizeof(ebuf) - 2) {
240 sprintf(ebuf, "%s\012", errstr->text);
241 } else {
242 strcpy(ebuf, "[too long error string]\012");
243 }
244 } else {
245 va_start(args, format);
246 vsprintf(ebuf, format, args);
247 }
248 P_message(ebuf); /* show message */
249 }
250