1 /*
2 * scwm_guile.c
3 * Copyright (C) 1999 Steve Tell
4 *
5 * based heavily on callbacks.c from SCWM:
6 * Copyright (C) 1997-1999 Maciej Stachowiak and Greg J. Badros
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this software; see the file COPYING.GPL. If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
21 * Boston, MA 02111-1307 USA
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <sys/time.h>
30 #include <unistd.h>
31 #include <limits.h>
32 #include <assert.h>
33
34 #include <guile/gh.h>
35 #include <libguile.h>
36 #include <libguile/fluids.h>
37
38 #include "scwm_guile.h"
39 #include "guile-compat.h"
40
41 #ifdef USE_DMALLOC
42 #include "dmalloc.h"
43 #endif
44
45 extern char *prog_name;
46
47 XSCM_HOOK(error_hook, "error-hook", 5, (SCM a, SCM b, SCM c, SCM d, SCM e),
48 "Called on all kinds of errors and exceptions."
49 "Whenever an error or other uncaught throw occurs on any callback,"
50 "whether a hook, a mouse binding, a key binding, a menu entry, a file"
51 "being processed, or anything else, error-hook will be invoked. Each"
52 "procedure in the hook will be called with the throw arguments; these"
53 "will generally include information about the nature of the error.");
54
55 struct scwm_body_apply_data {
56 SCM proc;
57 SCM args;
58 };
59
60
61 static SCM
scwm_body_apply(void * body_data)62 scwm_body_apply (void *body_data)
63 {
64 struct scwm_body_apply_data *ad = (struct scwm_body_apply_data *) body_data;
65 return scm_apply(ad->proc, ad->args, SCM_EOL);
66 }
67
68 /* Use scm_internal_cwdr to establish a new dynamic root - this causes
69 all throws to be caught and prevents continuations from exiting the
70 dynamic scope of the callback. This is needed to prevent callbacks
71 from disrupting scwm's flow control, which would likely cause a
72 crash. Use scm_internal_stack_catch to save the stack so we can
73 display a backtrace. scm_internal_stack_cwdr is the combination of
74 both. Note that the current implementation causes three(!) distinct
75 catch-like constructs to be used; this may have negative, perhaps
76 even significantly so, performance implications. */
77
78 struct cwssdr_data
79 {
80 SCM tag;
81 scm_t_catch_body body;
82 void *data;
83 scm_t_catch_handler handler;
84 };
85
86 static SCM
cwssdr_body(void * data)87 cwssdr_body (void *data)
88 {
89 struct cwssdr_data *d = (struct cwssdr_data *) data;
90 return scm_internal_stack_catch (d->tag, d->body, d->data, d->handler,
91 NULL);
92 }
93
94 SCM
scm_internal_stack_cwdr(scm_t_catch_body body,void * body_data,scm_t_catch_handler handler,void * handler_data,SCM_STACKITEM * stack_item)95 scm_internal_stack_cwdr (scm_t_catch_body body,
96 void *body_data,
97 scm_t_catch_handler handler,
98 void *handler_data,
99 SCM_STACKITEM *stack_item)
100 {
101 struct cwssdr_data d;
102 d.tag = SCM_BOOL_T;
103 d.body = body;
104 d.data = body_data;
105 d.handler = handler;
106 return scm_internal_cwdr(cwssdr_body, &d, handler, handler_data,
107 stack_item);
108 }
109
110
111
112 SCM
scwm_safe_apply(SCM proc,SCM args)113 scwm_safe_apply (SCM proc, SCM args)
114 {
115 SCM_STACKITEM stack_item;
116 struct scwm_body_apply_data apply_data;
117
118 apply_data.proc = proc;
119 apply_data.args = args;
120
121 return scm_internal_stack_cwdr(scwm_body_apply, &apply_data,
122 scwm_handle_error, prog_name,
123 &stack_item);
124 }
125
126
127 SCM
scwm_safe_apply_message_only(SCM proc,SCM args)128 scwm_safe_apply_message_only (SCM proc, SCM args)
129 {
130 SCM_STACKITEM stack_item;
131 struct scwm_body_apply_data apply_data;
132
133 apply_data.proc = proc;
134 apply_data.args = args;
135
136 return scm_internal_cwdr(scwm_body_apply, &apply_data,
137 scm_handle_by_message_noexit, prog_name,
138 &stack_item);
139 }
140
141
142 SCM
scwm_safe_call0(SCM thunk)143 scwm_safe_call0 (SCM thunk)
144 {
145 return scwm_safe_apply (thunk, SCM_EOL);
146 }
147
148
149 SCM
scwm_safe_call1(SCM proc,SCM arg)150 scwm_safe_call1 (SCM proc, SCM arg)
151 {
152 /* This means w must cons (albeit only once) on each callback of
153 size one - seems lame. */
154 return scwm_safe_apply (proc, scm_list_1(arg));
155 }
156
157
158 SCM
scwm_safe_call2(SCM proc,SCM arg1,SCM arg2)159 scwm_safe_call2 (SCM proc, SCM arg1, SCM arg2)
160 {
161 /* This means w must cons (albeit only once) on each callback of
162 size two - seems lame. */
163 return scwm_safe_apply (proc, scm_list_2(arg1, arg2));
164 }
165
166 SCM
scwm_safe_call3(SCM proc,SCM arg1,SCM arg2,SCM arg3)167 scwm_safe_call3 (SCM proc, SCM arg1, SCM arg2, SCM arg3)
168 {
169 /* This means w must cons (albeit only once) on each callback of
170 size two - seems lame. */
171 return scwm_safe_apply (proc, scm_list_3(arg1, arg2, arg3));
172 }
173
174 SCM
scwm_safe_call4(SCM proc,SCM arg1,SCM arg2,SCM arg3,SCM arg4)175 scwm_safe_call4 (SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4)
176 {
177 /* This means w must cons (albeit only once) on each callback of
178 size two - seems lame. */
179 return scwm_safe_apply (proc, scm_list_4(arg1, arg2, arg3, arg4));
180 }
181
182 SCM
scwm_safe_call5(SCM proc,SCM arg1,SCM arg2,SCM arg3,SCM arg4,SCM arg5)183 scwm_safe_call5 (SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4, SCM arg5)
184 {
185 /* This means w must cons (albeit only once) on each callback of
186 size two - seems lame. */
187 return scwm_safe_apply (proc, scm_list_5(arg1, arg2, arg3, arg4, arg5));
188 }
189
190 SCM
scwm_safe_call6(SCM proc,SCM arg1,SCM arg2,SCM arg3,SCM arg4,SCM arg5,SCM arg6)191 scwm_safe_call6 (SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4, SCM arg5, SCM arg6)
192 {
193 return scwm_safe_apply (proc, scm_list_n(arg1, arg2, arg3, arg4, arg5, arg6, SCM_UNDEFINED));
194 }
195
196 SCM
scwm_safe_call7(SCM proc,SCM arg1,SCM arg2,SCM arg3,SCM arg4,SCM arg5,SCM arg6,SCM arg7)197 scwm_safe_call7 (SCM proc, SCM arg1, SCM arg2, SCM arg3, SCM arg4, SCM arg5, SCM arg6, SCM arg7)
198 {
199 return scwm_safe_apply (proc, scm_list_n(arg1, arg2, arg3, arg4, arg5, arg6, arg7, SCM_UNDEFINED));
200 }
201
202
203 static SCM run_hook_proc;
204
scwm_run_hook(SCM hook,SCM args)205 SCM scwm_run_hook(SCM hook, SCM args)
206 {
207
208 return scwm_safe_apply(run_hook_proc, scm_cons(hook,args));
209 }
210
scwm_run_hook_message_only(SCM hook,SCM args)211 SCM scwm_run_hook_message_only(SCM hook, SCM args)
212 {
213 return scwm_safe_apply_message_only(run_hook_proc, scm_cons(hook,args));
214 }
215
216
call0_hooks(SCM hook)217 SCM call0_hooks(SCM hook)
218 {
219 return scwm_run_hook(hook,SCM_EOL);
220 }
221
call1_hooks(SCM hook,SCM arg1)222 SCM call1_hooks(SCM hook, SCM arg1)
223 {
224 return scwm_run_hook(hook,scm_list_1(arg1));
225 }
226
call2_hooks(SCM hook,SCM arg1,SCM arg2)227 SCM call2_hooks(SCM hook, SCM arg1, SCM arg2)
228 {
229 return scwm_run_hook(hook,scm_list_2(arg1,arg2));
230 }
231
call3_hooks(SCM hook,SCM arg1,SCM arg2,SCM arg3)232 SCM call3_hooks(SCM hook, SCM arg1, SCM arg2, SCM arg3)
233 {
234 return scwm_run_hook(hook,scm_list_n(arg1,arg2,arg3,SCM_UNDEFINED));
235 }
236
call4_hooks(SCM hook,SCM arg1,SCM arg2,SCM arg3,SCM arg4)237 SCM call4_hooks(SCM hook, SCM arg1, SCM arg2, SCM arg3, SCM arg4)
238 {
239 return scwm_run_hook(hook,scm_list_n(arg1,arg2,arg3,arg4,SCM_UNDEFINED));
240 }
241
call5_hooks(SCM hook,SCM arg1,SCM arg2,SCM arg3,SCM arg4,SCM arg5)242 SCM call5_hooks(SCM hook, SCM arg1, SCM arg2, SCM arg3, SCM arg4, SCM arg5)
243 {
244 return scwm_run_hook(hook,scm_list_n(arg1,arg2,arg3,arg4,arg5,SCM_UNDEFINED));
245 }
246
call6_hooks(SCM hook,SCM arg1,SCM arg2,SCM arg3,SCM arg4,SCM arg5,SCM arg6)247 SCM call6_hooks(SCM hook, SCM arg1, SCM arg2, SCM arg3, SCM arg4, SCM arg5, SCM arg6)
248 {
249 return scwm_run_hook(hook,scm_list_n(arg1,arg2,arg3,arg4,arg5,arg6,SCM_UNDEFINED));
250 }
251
call7_hooks(SCM hook,SCM arg1,SCM arg2,SCM arg3,SCM arg4,SCM arg5,SCM arg6,SCM arg7)252 SCM call7_hooks(SCM hook, SCM arg1, SCM arg2, SCM arg3, SCM arg4, SCM arg5, SCM arg6, SCM arg7)
253 {
254 return scwm_run_hook(hook,scm_list_n(arg1,arg2,arg3,arg4,arg5,arg6,arg7,SCM_UNDEFINED));
255 }
256
257 SCM
scm_empty_hook_p(SCM hook)258 scm_empty_hook_p(SCM hook)
259 {
260 return scm_hook_empty_p(hook);
261 }
262
263
264 /* Slightly tricky - we want to catch errors per expression, but only
265 establish a new dynamic root per load operation, as it's perfectly
266 OK for a file to invoke a continuation created by a different
267 expression in the file as far as scwm is concerned. So we set a
268 dynamic root for the whole load operation, but also catch on each
269 eval. */
270
271 static SCM
scwm_body_eval_x(void * body_data)272 scwm_body_eval_x (void *body_data)
273 {
274 SCM expr = *(SCM *) body_data;
275 return scm_eval_x (expr, scm_current_module() );
276 }
277
278 static SCM
scwm_catching_eval_x(SCM expr)279 scwm_catching_eval_x (SCM expr) {
280 return scm_internal_stack_catch (SCM_BOOL_T, scwm_body_eval_x, &expr,
281 scwm_handle_error, prog_name);
282 }
283
284 static SCM
scwm_catching_load_from_port(SCM port)285 scwm_catching_load_from_port (SCM port)
286 {
287 SCM expr;
288 SCM answer = SCM_UNSPECIFIED;
289
290 while (!SCM_EOF_OBJECT_P(expr = scm_read (port))) {
291 answer = scwm_catching_eval_x (expr);
292 }
293 scm_close_port (port);
294
295 return answer;
296 }
297
298 static SCM
scwm_body_load(void * body_data)299 scwm_body_load (void *body_data)
300 {
301 SCM filename = *(SCM *) body_data;
302 SCM port = scm_open_file (filename, scm_makfrom0str("r"));
303 return scwm_catching_load_from_port (port);
304 }
305
306 static SCM
scwm_body_eval_str(void * body_data)307 scwm_body_eval_str (void *body_data)
308 {
309 char *string = (char *) body_data;
310 SCM port = scm_mkstrport (SCM_MAKINUM (0), scm_makfrom0str(string),
311 SCM_OPN | SCM_RDNG, "scwm_safe_eval_str");
312 return scwm_catching_load_from_port (port);
313 }
314
315
316 SCM
scwm_handle_error(void * ARG_IGNORE (data),SCM tag,SCM throw_args)317 scwm_handle_error (void *ARG_IGNORE(data), SCM tag, SCM throw_args)
318 {
319 #if 0 /* GJB:FIXME:: */
320 SCM port = scm_mkstrport(SCM_INUM0,
321 scm_make_string(SCM_MAKINUM(200), SCM_UNDEFINED),
322 SCM_OPN | SCM_WRTNG,
323 "error-handler");
324 #else
325 SCM port = scm_current_error_port();
326 #endif
327
328 /* GJB:FIXME:MS: is this a guile compatibility test that can be dropped
329 now? */
330 if (scm_ilength (throw_args) >= 3)
331 {
332 SCM fl;
333 fl = SCM_VARIABLE_REF (scm_the_last_stack_fluid_var);
334
335 /* GJB:FIXME:MS: This is a horrible hack,
336 but DEREF_LAST_STACK macro was throwing a wrong type
337 argument at weird times, and I'm trying to avoid
338 a crash when I demo to RMS tomorrow, hence this
339 ugly hack --04/27/99 gjb */
340 if (SCM_NIMP (fl) && SCM_FLUIDP (fl)) {
341 SCM stack = DEREF_LAST_STACK;
342 SCM subr = SCM_CAR (throw_args);
343 SCM message = SCM_CADR (throw_args);
344 SCM args = SCM_CADDR (throw_args);
345
346 scm_newline(port);
347 scm_display_backtrace (stack, port, SCM_UNDEFINED, SCM_UNDEFINED);
348 scm_newline(port);
349 scm_display_error (stack, port, subr, message, args, SCM_EOL);
350 } else {
351 /* scwm_msg(ERR,"scwm_handle_error","scm_the_last_stack_fluid not holding a fluid!"); */
352 }
353 }
354 else
355 {
356 scm_puts ("uncaught throw to ", port);
357 scm_prin1 (tag, port, 0);
358 scm_puts (": ", port);
359 scm_prin1 (throw_args, port, 1);
360 scm_putc ('\n', port);
361 exit (2);
362 }
363 /* GJB:FIXME:MS: can the scheme code display a backtrace without the
364 stack argument? */
365 return scwm_run_hook_message_only(error_hook, scm_cons(tag, throw_args));
366 }
367
368
369 SCM_DEFINE(safe_load, "safe-load", 1, 0, 0,
370 (SCM fname),
371 "Load file FNAME while trapping and displaying errors."
372 "Each individual top-level-expression is evaluated separately and all"
373 "errors are trapped and displayed. You should use this procedure if"
374 "you need to make sure most of a file loads, even if it may contain"
375 "errors.")
376 #define FUNC_NAME s_safe_load
377 {
378 SCM_STACKITEM stack_item;
379 VALIDATE_ARG_STR(1,fname);
380 return scm_internal_cwdr(scwm_body_load, &fname,
381 scm_handle_by_message_noexit, prog_name,
382 &stack_item);
383 }
384 #undef FUNC_NAME
385
scwm_safe_load(char * filename)386 SCM scwm_safe_load (char *filename)
387 {
388 return safe_load(scm_makfrom0str(filename));
389 }
390
scwm_safe_eval_str(char * string)391 SCM scwm_safe_eval_str (char *string)
392 {
393 SCM_STACKITEM stack_item;
394 return scm_internal_cwdr(scwm_body_eval_str, string,
395 scm_handle_by_message_noexit, prog_name,
396 &stack_item);
397 }
398
init_scwm_guile()399 void init_scwm_guile()
400 {
401 run_hook_proc = gh_lookup("run-hook");
402
403 #ifndef SCM_MAGIC_SNARF_INITS
404 #include "scwm_guile.x"
405 #endif
406 }
407
408
409 /* Local Variables: */
410 /* tab-width: 8 */
411 /* c-basic-offset: 2 */
412 /* End: */
413 /* vim:ts=8:sw=2:sta
414 */
415
416