r23059 - gnucash/trunk/src/app-utils - Bug #691587: Catch scheme exceptions when converting error messages to a C string.
Christian Stimming
cstim at code.gnucash.org
Wed Jun 19 11:48:14 EDT 2013
Author: cstim
Date: 2013-06-19 11:48:13 -0400 (Wed, 19 Jun 2013)
New Revision: 23059
Trac: http://svn.gnucash.org/trac/changeset/23059
Modified:
gnucash/trunk/src/app-utils/gfec.c
Log:
Bug #691587: Catch scheme exceptions when converting error messages to a C string.
When loading a scheme data file such as ~/.gnucash/stylesheets that contains
invalid characters (e.g., all-zero bytes), the loading throws a scheme
exception and we are redirected into the error catch handler. When trying
to convert the erroneous scheme expression into a string for the
message output, scheme runs into an exception right again. Hence, the
conversion to string must be guarded by a stack_catch as well.
Modified: gnucash/trunk/src/app-utils/gfec.c
===================================================================
--- gnucash/trunk/src/app-utils/gfec.c 2013-06-19 15:48:02 UTC (rev 23058)
+++ gnucash/trunk/src/app-utils/gfec.c 2013-06-19 15:48:13 UTC (rev 23059)
@@ -13,18 +13,40 @@
#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_locale_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. */
+ * 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)
{
@@ -32,12 +54,44 @@
SCM result;
char *msg = NULL;
+ // 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++;
+
func = scm_c_eval_string("gnc:error->string");
if (scm_is_procedure(func))
{
result = scm_call_2(func, tag, throw_args);
if (scm_is_string(result))
- msg = gnc_scm_to_locale_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_locale_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;
+ }
+ }
}
if (msg == NULL)
@@ -50,6 +104,7 @@
g_free(msg);
}
+ --gfec_catcher_recursion_level;
return SCM_UNDEFINED;
}
@@ -86,7 +141,12 @@
if (err_msg != NULL)
{
if (error_handler)
- error_handler(err_msg);
+ {
+ gchar *full_msg = g_strdup_printf("Could not load file %s: %s",
+ file, err_msg);
+ error_handler(full_msg);
+ g_free(full_msg);
+ }
free(err_msg);
More information about the gnucash-changes
mailing list