Files
gnucash/libgnucash/app-utils/gfec.c
T

243 lines
6.1 KiB
C
Raw Normal View History

/* Authors: Eric M. Ludlam <zappo@ultranet.com>
* Russ McManus <russell.mcmanus@gs.com>
* Dave Peticolas <dave@krondo.com>
*
* gfec stands for 'guile fancy error catching'.
* This code is in the public domain.
*/
#include <assert.h>
#include <string.h>
#include <config.h>
#include "gfec.h"
#include "gnc-guile-utils.h"
#include "platform.h"
#include <glib.h>
#if COMPILER(MSVC)
# define strdup _strdup
#endif
typedef struct {
char **msg;
SCM *scm_string;
} helper_data_t;
static SCM helper_scm_to_string(void *ptr_void)
{
helper_data_t* ptr = ptr_void;
g_assert(ptr);
*(ptr->msg) = gnc_scm_to_utf8_string(*ptr->scm_string);
return SCM_UNDEFINED;
}
static int gfec_catcher_recursion_level = 0;
/* We assume that data is actually a char**. The way we return results
* from this function is to malloc a fresh string, and store it in
* this pointer. It is the caller's responsibility to do something
* smart with this freshly allocated storage. the caller can determine
* whether there was an error by initializing the char* passed in to
* NULL. If there is an error, the char string will not be NULL on
* return.
*
* This function might call itself recursively: The conversion of the error
* object to a string might itself throw an exception, hence the scm_to_string
* function must be wrapped into a stack_catch block as well. To avoid infinite
* recursion, we check the recursion level by gfec_catcher_recursion_level.
*/
static SCM
gfec_catcher(void *data, SCM tag, SCM throw_args)
{
2009-12-29 20:12:48 +00:00
SCM func;
SCM result;
2011-08-21 19:42:00 +00:00
char *msg = NULL;
2009-12-29 20:12:48 +00:00
// To much recursion? Better jump out of here quickly.
if (gfec_catcher_recursion_level > 2)
{
*(char**)data = strdup("Guile error: Too many recursions in error catch handler.");
return SCM_UNDEFINED;
}
gfec_catcher_recursion_level++;
2009-12-29 20:12:48 +00:00
func = scm_c_eval_string("gnc:error->string");
if (scm_is_procedure(func))
2009-12-29 20:12:48 +00:00
{
result = scm_call_2(func, tag, throw_args);
if (scm_is_string(result))
{
char *internal_err_msg = NULL;
helper_data_t helper_data;
helper_data.msg = &msg;
helper_data.scm_string = &result;
// The conversion to string can itself throw as well
scm_internal_stack_catch(SCM_BOOL_T,
helper_scm_to_string,
(void *) &helper_data,
gfec_catcher,
&internal_err_msg);
// Previously: msg = gnc_scm_to_utf8_string (result);
// Did we run into an exception? Then the output argument msg is
// not set (due to the exception), but err_msg is set and contains
// that error message. We thus pass the err_msg instead of msg to
// our caller.
if (internal_err_msg)
{
msg = internal_err_msg;
}
}
2009-12-29 20:12:48 +00:00
}
if (msg == NULL)
{
2011-08-21 19:42:00 +00:00
*(char**)data = strdup("Error running guile function.");
}
else
{
*(char**)data = strdup(msg);
g_free(msg);
2009-12-29 20:12:48 +00:00
}
--gfec_catcher_recursion_level;
2009-12-29 20:12:48 +00:00
return SCM_UNDEFINED;
}
/* The arguments to scm_internal_stack_catch:
------------------------------------------
SCM tag : this should be SCM_BOOL_T to catch all errors.
scm_catch_body_t body : the function to run.
void *body_data : a pointer to pass to body
scm_catch_handler_t handler : the hander function
void *handler_data : a pointer to pass to the handler
*/
static SCM
gfec_string_helper(void *data)
{
char *string = data;
return scm_c_eval_string(string);
}
SCM
gfec_eval_string(const char *str, gfec_error_handler error_handler)
{
2009-12-29 20:12:48 +00:00
char *err_msg = NULL;
SCM result;
2009-12-29 20:12:48 +00:00
result = scm_internal_stack_catch(SCM_BOOL_T,
gfec_string_helper,
(void *) str,
2009-12-29 20:12:48 +00:00
gfec_catcher,
&err_msg);
2009-12-29 20:12:48 +00:00
if (err_msg != NULL)
{
if (error_handler)
error_handler(err_msg);
2009-12-29 20:12:48 +00:00
free(err_msg);
2009-12-29 20:12:48 +00:00
return SCM_UNDEFINED;
}
2009-12-29 20:12:48 +00:00
return result;
}
SCM
gfec_eval_file(const char *file, gfec_error_handler error_handler)
{
gchar *contents = NULL;
GError *save_error = NULL;
2009-12-29 20:12:48 +00:00
SCM result;
if (!g_file_get_contents (file, &contents, NULL, &save_error))
2009-12-29 20:12:48 +00:00
{
gchar *full_msg = g_strdup_printf ("Couldn't read contents of %s.\nReason: %s", file, save_error->message);
error_handler(full_msg);
g_error_free (save_error);
g_free(full_msg);
2009-12-29 20:12:48 +00:00
return SCM_UNDEFINED;
}
result = gfec_eval_string (contents, error_handler);
g_free (contents);
2009-12-29 20:12:48 +00:00
return result;
}
struct gfec_apply_rec
{
2009-12-29 20:12:48 +00:00
SCM proc;
SCM arglist;
};
static SCM
gfec_apply_helper(void *data)
{
2009-12-29 20:12:48 +00:00
struct gfec_apply_rec *apply_rec = (struct gfec_apply_rec *)data;
2009-12-29 20:12:48 +00:00
return scm_apply(apply_rec->proc, apply_rec->arglist, SCM_EOL);
}
SCM
gfec_apply(SCM proc, SCM arglist, gfec_error_handler error_handler)
{
2009-12-29 20:12:48 +00:00
char *err_msg = NULL;
struct gfec_apply_rec apply_rec;
SCM result;
2009-12-29 20:12:48 +00:00
apply_rec.proc = proc;
apply_rec.arglist = arglist;
2009-12-29 20:12:48 +00:00
result = scm_internal_stack_catch(SCM_BOOL_T,
gfec_apply_helper,
&apply_rec,
gfec_catcher,
&err_msg);
2009-12-29 20:12:48 +00:00
if (err_msg != NULL)
{
if (error_handler)
error_handler(err_msg);
2009-12-29 20:12:48 +00:00
free(err_msg);
2009-12-29 20:12:48 +00:00
return SCM_UNDEFINED;
}
2009-12-29 20:12:48 +00:00
return result;
}
2006-10-15 19:02:05 +00:00
static int error_in_scm_eval = FALSE;
static void
error_handler(const char *msg)
{
2008-07-04 00:42:00 +00:00
g_warning("%s", msg);
2006-10-15 19:02:05 +00:00
error_in_scm_eval = TRUE;
}
gboolean
gfec_try_load(const gchar *fn)
2006-10-15 19:02:05 +00:00
{
2007-02-10 02:31:35 +00:00
g_debug("looking for %s", fn);
2009-12-29 20:12:48 +00:00
if (g_file_test(fn, G_FILE_TEST_EXISTS))
{
2007-02-10 02:31:35 +00:00
g_debug("trying to load %s", fn);
2006-10-15 19:02:05 +00:00
error_in_scm_eval = FALSE;
gfec_eval_file(fn, error_handler);
return !error_in_scm_eval;
}
return FALSE;
}