GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gfec.c
1 /* Authors: Eric M. Ludlam <[email protected]>
2  * Russ McManus <[email protected]>
3  * Dave Peticolas <[email protected]>
4  *
5  * gfec stands for 'guile fancy error catching'.
6  * This code is in the public domain.
7  */
8 
9 #include <assert.h>
10 #include <string.h>
11 
12 #include "config.h"
13 #include "gfec.h"
14 #include "gnc-guile-utils.h"
15 #include "platform.h"
16 #include <glib.h>
17 #if COMPILER(MSVC)
18 # define strdup _strdup
19 #endif
20 
21 typedef struct {
22  char **msg;
23  SCM *scm_string;
25 
26 static SCM helper_scm_to_string(void *ptr_void)
27 {
28  helper_data_t* ptr = ptr_void;
29  g_assert(ptr);
30  *(ptr->msg) = gnc_scm_to_utf8_string(*ptr->scm_string);
31  return SCM_UNDEFINED;
32 }
33 
34 
35 static int gfec_catcher_recursion_level = 0;
36 
37 /* We assume that data is actually a char**. The way we return results
38  * from this function is to malloc a fresh string, and store it in
39  * this pointer. It is the caller's responsibility to do something
40  * smart with this freshly allocated storage. the caller can determine
41  * whether there was an error by initializing the char* passed in to
42  * NULL. If there is an error, the char string will not be NULL on
43  * return.
44  *
45  * This function might call itself recursively: The conversion of the error
46  * object to a string might itself throw an exception, hence the scm_to_string
47  * function must be wrapped into a stack_catch block as well. To avoid infinite
48  * recursion, we check the recursion level by gfec_catcher_recursion_level.
49  */
50 static SCM
51 gfec_catcher(void *data, SCM tag, SCM throw_args)
52 {
53  SCM func;
54  SCM result;
55  char *msg = NULL;
56 
57  // To much recursion? Better jump out of here quickly.
58  if (gfec_catcher_recursion_level > 2)
59  {
60  *(char**)data = strdup("Guile error: Too many recursions in error catch handler.");
61  return SCM_UNDEFINED;
62  }
63 
64  gfec_catcher_recursion_level++;
65 
66  func = scm_c_eval_string("gnc:error->string");
67  if (scm_is_procedure(func))
68  {
69  result = scm_call_2(func, tag, throw_args);
70  if (scm_is_string(result))
71  {
72  char *internal_err_msg = NULL;
73  helper_data_t helper_data;
74 
75  helper_data.msg = &msg;
76  helper_data.scm_string = &result;
77 
78  // The conversion to string can itself throw as well
79  scm_internal_stack_catch(SCM_BOOL_T,
80  helper_scm_to_string,
81  (void *) &helper_data,
82  gfec_catcher,
83  &internal_err_msg);
84  // Previously: msg = gnc_scm_to_utf8_string (result);
85 
86  // Did we run into an exception? Then the output argument msg is
87  // not set (due to the exception), but err_msg is set and contains
88  // that error message. We thus pass the err_msg instead of msg to
89  // our caller.
90  if (internal_err_msg)
91  {
92  msg = internal_err_msg;
93  }
94  }
95  }
96 
97  if (msg == NULL)
98  {
99  *(char**)data = strdup("Error running guile function.");
100  }
101  else
102  {
103  *(char**)data = strdup(msg);
104  g_free(msg);
105  }
106 
107  --gfec_catcher_recursion_level;
108  return SCM_UNDEFINED;
109 }
110 
111 
112 /* The arguments to scm_internal_stack_catch:
113  ------------------------------------------
114  SCM tag : this should be SCM_BOOL_T to catch all errors.
115  scm_catch_body_t body : the function to run.
116  void *body_data : a pointer to pass to body
117  scm_catch_handler_t handler : the hander function
118  void *handler_data : a pointer to pass to the handler
119 */
120 
121 static SCM
122 gfec_string_helper(void *data)
123 {
124  char *string = data;
125 
126  return scm_c_eval_string(string);
127 }
128 
129 SCM
130 gfec_eval_string(const char *str, gfec_error_handler error_handler)
131 {
132  char *err_msg = NULL;
133  SCM result;
134 
135  result = scm_internal_stack_catch(SCM_BOOL_T,
136  gfec_string_helper,
137  (void *) str,
138  gfec_catcher,
139  &err_msg);
140 
141  if (err_msg != NULL)
142  {
143  if (error_handler)
144  error_handler(err_msg);
145 
146  free(err_msg);
147 
148  return SCM_UNDEFINED;
149  }
150 
151  return result;
152 }
153 
154 SCM
155 gfec_eval_file(const char *file, gfec_error_handler error_handler)
156 {
157  char *err_msg = NULL;
158  gchar *contents = NULL;
159  GError *save_error = NULL;
160  SCM result;
161 
162  if (!g_file_get_contents (file, &contents, NULL, &save_error))
163  {
164  gchar *full_msg = g_strdup_printf ("Couldn't read contents of %s.\nReason: %s", file, save_error->message);
165  error_handler(full_msg);
166 
167  g_error_free (save_error);
168  g_free(full_msg);
169 
170  return SCM_UNDEFINED;
171  }
172 
173  result = gfec_eval_string (contents, error_handler);
174  g_free (contents);
175 
176  return result;
177 }
178 
180 {
181  SCM proc;
182  SCM arglist;
183 };
184 
185 static SCM
186 gfec_apply_helper(void *data)
187 {
188  struct gfec_apply_rec *apply_rec = (struct gfec_apply_rec *)data;
189 
190  return scm_apply(apply_rec->proc, apply_rec->arglist, SCM_EOL);
191 }
192 
193 SCM
194 gfec_apply(SCM proc, SCM arglist, gfec_error_handler error_handler)
195 {
196  char *err_msg = NULL;
197  struct gfec_apply_rec apply_rec;
198  SCM result;
199 
200  apply_rec.proc = proc;
201  apply_rec.arglist = arglist;
202 
203  result = scm_internal_stack_catch(SCM_BOOL_T,
204  gfec_apply_helper,
205  &apply_rec,
206  gfec_catcher,
207  &err_msg);
208 
209  if (err_msg != NULL)
210  {
211  if (error_handler)
212  error_handler(err_msg);
213 
214  free(err_msg);
215 
216  return SCM_UNDEFINED;
217  }
218 
219  return result;
220 }
221 
222 static int error_in_scm_eval = FALSE;
223 
224 static void
225 error_handler(const char *msg)
226 {
227  g_warning("%s", msg);
228  error_in_scm_eval = TRUE;
229 }
230 
231 gboolean
232 gfec_try_load(gchar *fn)
233 {
234  g_debug("looking for %s", fn);
235  if (g_file_test(fn, G_FILE_TEST_EXISTS))
236  {
237  g_debug("trying to load %s", fn);
238  error_in_scm_eval = FALSE;
239  gfec_eval_file(fn, error_handler);
240  return !error_in_scm_eval;
241  }
242  return FALSE;
243 }
244