1 /* gEDA - GPL Electronic Design Automation
2 * libgeda - gEDA's library
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
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 General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
36 #include "libgeda_priv.h"
37 #include "libgedaguile.h"
39 static void process_error_stack (SCM s_stack
, SCM s_key
, SCM s_args
, GError
**err
);
41 /* Pre-unwind handler called in the context in which the exception was
43 static SCM
protected_pre_unwind_handler (void *data
, SCM key
, SCM args
)
45 /* Capture the stack trace */
46 *((SCM
*) data
) = scm_make_stack (SCM_BOOL_T
, SCM_EOL
);
51 /* Post-unwind handler called in the context of the catch expression.
52 * This actually does the work of parsing the stack and generating log
54 static SCM
protected_post_unwind_handler (void *data
, SCM key
, SCM args
)
56 /* The stack was captured pre-unwind */
57 SCM s_stack
= *(SCM
*) data
;
59 process_error_stack (s_stack
, key
, args
, NULL
);
64 /* Actually carries out evaluation for protected eval */
65 static SCM
protected_body_eval (void *data
)
67 SCM args
= *((SCM
*)data
);
68 return scm_eval (scm_car (args
), scm_cadr (args
));
71 /*! \brief Evaluate a Scheme expression safely.
72 * \par Function Description
74 * Often a libgeda program (or libgeda itself) will need to call out
75 * to Scheme code, for example to load a Scheme initialisation (RC) file.
76 * If an error or exception caused by such code goes uncaught, it
77 * locks up the Scheme interpreter, stopping any further Scheme code
78 * from being run until the program is restarted.
80 * This function is equivalent to scm_eval (), with the important
81 * difference that any errors or exceptions caused by the evaluated
82 * expression \a exp are caught and reported via the libgeda logging
83 * mechanism. If an error occurs during evaluation, this function
84 * returns SCM_BOOL_F. If \a module_or_state is undefined, uses the
85 * current interaction environment.
87 * \param exp Expression to evaluate
88 * \param module_or_state Environment in which to evaluate \a exp
90 * \returns Evaluation results or SCM_BOOL_F if exception caught.
92 SCM
g_scm_eval_protected (SCM exp
, SCM module_or_state
)
94 SCM stack
= SCM_BOOL_T
;
98 if (SCM_UNBNDP (module_or_state
)) {
99 body_data
= scm_list_2 (exp
, scm_interaction_environment ());
101 body_data
= scm_list_2 (exp
, module_or_state
);
104 result
= scm_c_catch (SCM_BOOL_T
,
105 protected_body_eval
, /* catch body */
106 &body_data
, /* body data */
107 protected_post_unwind_handler
, /* post handler */
108 &stack
, /* post data */
109 protected_pre_unwind_handler
, /* pre handler */
110 &stack
/* pre data */
113 scm_remember_upto_here_2 (body_data
, stack
);
118 /*! \brief Evaluate a C string as a Scheme expression safely
119 * \par Function Description
121 * Evaluates a C string like scm_c_eval_string(). Simple wrapper for
122 * g_scm_eval_string_protected().
124 * \param str String to evaluate.
126 * \returns Evaluation results or SCM_BOOL_F if exception caught.
128 SCM
g_scm_c_eval_string_protected (const gchar
*str
) {
130 g_return_val_if_fail ((str
!= NULL
), SCM_BOOL_F
);
131 s_str
= scm_from_utf8_string (str
);
132 return g_scm_eval_string_protected (s_str
);
135 /*! \brief Evaluate a string as a Scheme expression safely
136 * \par Function Description
138 * Evaluates a string similarly to scm_eval_string(), but catching
139 * any errors or exceptions and reporting them via the libgeda
142 * See also g_scm_eval_protected() and g_scm_c_eval_string_protected().
144 * \param str String to evaluate.
146 * \returns Evaluation results or SCM_BOOL_F if exception caught.
148 SCM
g_scm_eval_string_protected (SCM str
)
150 SCM expr
= scm_list_2 (scm_from_utf8_symbol ("eval-string"),
153 return g_scm_eval_protected (expr
, SCM_UNDEFINED
);
156 /* Data to be passed to g_read_file()'s worker functions. */
157 struct g_read_file_data_t
164 /* Body function for g_read_file(). Simply loads the specified
167 g_read_file__body (struct g_read_file_data_t
*data
)
169 return scm_primitive_load (data
->filename
);
172 /* Post-unwind handler for g_read_file(). Processes the stack captured
173 * in the pre-unwind handler. */
175 g_read_file__post_handler (struct g_read_file_data_t
*data
, SCM key
, SCM args
)
177 process_error_stack (data
->stack
, key
, args
, &data
->err
);
181 /* Pre-unwind handler for g_read_file(). Captures the Guile stack for
182 * processing in the post-unwind handler. */
184 g_read_file__pre_handler (struct g_read_file_data_t
*data
, SCM key
, SCM args
)
186 data
->stack
= scm_make_stack (SCM_BOOL_T
, SCM_EOL
);
190 /*! \brief Load a Scheme file, catching and logging errors.
191 * \par Function Description
192 * Loads \a filename, catching any uncaught errors and logging them.
194 * \bug Most other functions in the libgeda API return TRUE on success
195 * and FALSE on failure. g_read_file() shouldn't be an exception.
197 * \param toplevel The TOPLEVEL structure.
198 * \param filename The file name of the Scheme file to load.
199 * \param err Return location for errors, or NULL.
200 * \return TRUE on success, FALSE on failure.
203 g_read_file(TOPLEVEL
*toplevel
, const gchar
*filename
, GError
**err
)
205 struct g_read_file_data_t data
;
207 g_return_val_if_fail ((filename
!= NULL
), FALSE
);
209 data
.stack
= SCM_BOOL_F
;
210 data
.filename
= scm_from_utf8_string (filename
);
213 scm_dynwind_begin (SCM_F_DYNWIND_REWINDABLE
);
214 edascm_dynwind_toplevel (toplevel
);
216 scm_c_catch (SCM_BOOL_T
,
217 (scm_t_catch_body
) g_read_file__body
, &data
,
218 (scm_t_catch_handler
) g_read_file__post_handler
, &data
,
219 (scm_t_catch_handler
) g_read_file__pre_handler
, &data
);
223 /* If no error occurred, indicate success. */
224 if (data
.err
== NULL
) return TRUE
;
226 g_propagate_error (err
, data
.err
);
231 /*! \brief Process a Scheme error into the log and/or a GError
232 * \par Function Description
233 * Process a captured Guile exception with the given \a s_key and \a
234 * s_args, and optionally the stack trace \a s_stack. The stack trace
235 * and source location are logged, and if a GError return location \a
236 * err is provided, it is populated with an informative error message.
239 process_error_stack (SCM s_stack
, SCM s_key
, SCM s_args
, GError
**err
) {
242 SCM s_port
, s_subr
, s_message
, s_message_args
, s_rest
, s_location
;
244 /* Split s_args up */
246 s_subr
= scm_car (s_rest
); s_rest
= scm_cdr (s_rest
);
247 s_message
= scm_car (s_rest
); s_rest
= scm_cdr (s_rest
);
248 s_message_args
= scm_car (s_rest
); s_rest
= scm_cdr (s_rest
);
250 /* Capture short error message */
251 s_port
= scm_open_output_string ();
252 scm_display_error_message (s_message
, s_message_args
, s_port
);
253 short_message
= scm_to_utf8_string (scm_get_output_string (s_port
));
254 scm_close_output_port (s_port
);
256 /* Capture long error message (including possible backtrace) */
257 s_port
= scm_open_output_string ();
258 if (scm_is_true (scm_stack_p (s_stack
))) {
259 scm_puts (_("\nBacktrace:\n"), s_port
);
260 scm_display_backtrace (s_stack
, s_port
, SCM_BOOL_F
, SCM_BOOL_F
);
261 scm_puts ("\n", s_port
);
264 s_location
= SCM_BOOL_F
;
265 #ifdef HAVE_SCM_DISPLAY_ERROR_STACK
266 s_location
= s_stack
;
267 #endif /* HAVE_SCM_DISPLAY_ERROR_STACK */
268 #ifdef HAVE_SCM_DISPLAY_ERROR_FRAME
270 scm_is_true (s_stack
) ? scm_stack_ref (s_stack
, SCM_INUM0
) : SCM_BOOL_F
;
271 #endif /* HAVE_SCM_DISPLAY_ERROR_FRAME */
273 scm_display_error (s_location
, s_port
, s_subr
,
274 s_message
, s_message_args
, s_rest
);
276 long_message
= scm_to_utf8_string (scm_get_output_string (s_port
));
277 scm_close_output_port (s_port
);
279 /* Send long message to log */
280 s_log_message ("%s", long_message
);
282 /* Populate any GError */
283 g_set_error (err
, EDA_ERROR
, EDA_ERROR_SCHEME
, "%s", short_message
);