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