OSDN Git Service

TortoiseMerge Basic Support Git patch created by format patch
[tortoisegit/TortoiseGitJp.git] / src / TortoiseMerge / libsvn_diff / error.c
1 /* error.c:  common exception handling for Subversion\r
2  *\r
3  * ====================================================================\r
4  * Copyright (c) 2000-2007 CollabNet.  All rights reserved.\r
5  *\r
6  * This software is licensed as described in the file COPYING, which\r
7  * you should have received as part of this distribution.  The terms\r
8  * are also available at http://subversion.tigris.org/license-1.html.\r
9  * If newer versions of this license are posted there, you may use a\r
10  * newer version instead, at your option.\r
11  *\r
12  * This software consists of voluntary contributions made by many\r
13  * individuals.  For exact contribution history, see the revision\r
14  * history and logs, available at http://subversion.tigris.org/.\r
15  * ====================================================================\r
16  */\r
17 \r
18 \r
19 \f\r
20 #include <stdarg.h>\r
21 \r
22 #include <apr_general.h>\r
23 #include <apr_pools.h>\r
24 #include <apr_strings.h>\r
25 \r
26 //#include "svn_cmdline.h"\r
27 #include "svn_error.h"\r
28 #include "svn_pools.h"\r
29 #include "svn_utf.h"\r
30 \r
31 #ifdef SVN_DEBUG\r
32 /* file_line for the non-debug case. */\r
33 static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";\r
34 #endif /* SVN_DEBUG */\r
35 \r
36 //#include "svn_private_config.h"\r
37 \r
38 \f\r
39 /*** Helpers for creating errors ***/\r
40 #undef svn_error_create\r
41 #undef svn_error_createf\r
42 #undef svn_error_quick_wrap\r
43 #undef svn_error_wrap_apr\r
44 \r
45 \r
46 /* XXX FIXME: These should be protected by a thread mutex.\r
47    svn_error__locate and make_error_internal should cooperate\r
48    in locking and unlocking it. */\r
49 \r
50 /* XXX TODO: Define mutex here #if APR_HAS_THREADS */\r
51 static const char *error_file = NULL;\r
52 static long error_line = -1;\r
53 \r
54 void\r
55 svn_error__locate(const char *file, long line)\r
56 {\r
57   /* XXX TODO: Lock mutex here */\r
58   error_file = file;\r
59   error_line = line;\r
60 }\r
61 \r
62 \r
63 /* Cleanup function for errors.  svn_error_clear () removes this so\r
64    errors that are properly handled *don't* hit this code. */\r
65 #if defined(SVN_DEBUG)\r
66 static apr_status_t err_abort(void *data)\r
67 {\r
68   svn_error_t *err = data;  /* For easy viewing in a debugger */\r
69   err = err; /* Fake a use for the variable to avoid compiler warnings */\r
70   abort();\r
71 }\r
72 #endif\r
73 \r
74 \r
75 static svn_error_t *\r
76 make_error_internal(apr_status_t apr_err,\r
77                     svn_error_t *child)\r
78 {\r
79   apr_pool_t *pool;\r
80   svn_error_t *new_error;\r
81 \r
82   /* Reuse the child's pool, or create our own. */\r
83   if (child)\r
84     pool = child->pool;\r
85   else\r
86     {\r
87       if (apr_pool_create(&pool, NULL))\r
88         abort();\r
89     }\r
90 \r
91   /* Create the new error structure */\r
92   new_error = apr_pcalloc(pool, sizeof(*new_error));\r
93 \r
94   /* Fill 'er up. */\r
95   new_error->apr_err = apr_err;\r
96   new_error->child   = child;\r
97   new_error->pool    = pool;\r
98   new_error->file    = error_file;\r
99   new_error->line    = error_line;\r
100   /* XXX TODO: Unlock mutex here */\r
101 \r
102 #if defined(SVN_DEBUG)\r
103   if (! child)\r
104       apr_pool_cleanup_register(pool, new_error,\r
105                                 err_abort,\r
106                                 apr_pool_cleanup_null);\r
107 #endif\r
108 \r
109   return new_error;\r
110 }\r
111 \r
112 \r
113 \f\r
114 /*** Creating and destroying errors. ***/\r
115 \r
116 svn_error_t *\r
117 svn_error_create(apr_status_t apr_err,\r
118                  svn_error_t *child,\r
119                  const char *message)\r
120 {\r
121   svn_error_t *err;\r
122 \r
123   err = make_error_internal(apr_err, child);\r
124 \r
125   if (message)\r
126     err->message = apr_pstrdup(err->pool, message);\r
127 \r
128   return err;\r
129 }\r
130 \r
131 \r
132 svn_error_t *\r
133 svn_error_createf(apr_status_t apr_err,\r
134                   svn_error_t *child,\r
135                   const char *fmt,\r
136                   ...)\r
137 {\r
138   svn_error_t *err;\r
139   va_list ap;\r
140 \r
141   err = make_error_internal(apr_err, child);\r
142 \r
143   va_start(ap, fmt);\r
144   err->message = apr_pvsprintf(err->pool, fmt, ap);\r
145   va_end(ap);\r
146 \r
147   return err;\r
148 }\r
149 \r
150 \r
151 svn_error_t *\r
152 svn_error_wrap_apr(apr_status_t status,\r
153                    const char *fmt,\r
154                    ...)\r
155 {\r
156   svn_error_t *err, *utf8_err;\r
157   va_list ap;\r
158   char errbuf[255];\r
159   const char *msg_apr, *msg;\r
160 \r
161   err = make_error_internal(status, NULL);\r
162 \r
163   if (fmt)\r
164     {\r
165       /* Grab the APR error message. */\r
166       apr_strerror(status, errbuf, sizeof(errbuf));\r
167       utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);\r
168       if (utf8_err)\r
169         msg_apr = NULL;\r
170       svn_error_clear(utf8_err);\r
171 \r
172       /* Append it to the formatted message. */\r
173       va_start(ap, fmt);\r
174       msg = apr_pvsprintf(err->pool, fmt, ap);\r
175       va_end(ap);\r
176       err->message = apr_psprintf(err->pool, "%s%s%s", msg,\r
177                                   (msg_apr) ? ": " : "",\r
178                                   (msg_apr) ? msg_apr : "");\r
179     }\r
180 \r
181   return err;\r
182 }\r
183 \r
184 \r
185 svn_error_t *\r
186 svn_error_quick_wrap(svn_error_t *child, const char *new_msg)\r
187 {\r
188   return svn_error_create(child->apr_err,\r
189                           child,\r
190                           new_msg);\r
191 }\r
192 \r
193 \r
194 svn_error_t *\r
195 svn_error_compose_create(svn_error_t *err1,\r
196                          svn_error_t *err2)\r
197 {\r
198   if (err1 && err2)\r
199     {\r
200       svn_error_compose(err1, err2);\r
201       return err1;\r
202     }\r
203   return err1 ? err1 : err2;\r
204 }\r
205 \r
206 \r
207 void\r
208 svn_error_compose(svn_error_t *chain, svn_error_t *new_err)\r
209 {\r
210   apr_pool_t *pool = chain->pool;\r
211   apr_pool_t *oldpool = new_err->pool;\r
212 \r
213   while (chain->child)\r
214     chain = chain->child;\r
215 \r
216 #if defined(SVN_DEBUG)\r
217   /* Kill existing handler since the end of the chain is going to change */\r
218   apr_pool_cleanup_kill(pool, chain, err_abort);\r
219 #endif\r
220 \r
221   /* Copy the new error chain into the old chain's pool. */\r
222   while (new_err)\r
223     {\r
224       chain->child = apr_palloc(pool, sizeof(*chain->child));\r
225       chain = chain->child;\r
226       *chain = *new_err;\r
227       if (chain->message)\r
228         chain->message = apr_pstrdup(pool, new_err->message);\r
229       chain->pool = pool;\r
230 #if defined(SVN_DEBUG)\r
231       if (! new_err->child)\r
232         apr_pool_cleanup_kill(oldpool, new_err, err_abort);\r
233 #endif\r
234       new_err = new_err->child;\r
235     }\r
236 \r
237 #if defined(SVN_DEBUG)\r
238   apr_pool_cleanup_register(pool, chain,\r
239                             err_abort,\r
240                             apr_pool_cleanup_null);\r
241 #endif\r
242 \r
243   /* Destroy the new error chain. */\r
244   svn_pool_destroy(oldpool);\r
245 }\r
246 \r
247 svn_error_t *\r
248 svn_error_root_cause(svn_error_t *err)\r
249 {\r
250   while (err)\r
251     {\r
252       if (err->child)\r
253         err = err->child;\r
254       else\r
255         break;\r
256     }\r
257 \r
258   return err;\r
259 }\r
260 \r
261 svn_error_t *\r
262 svn_error_dup(svn_error_t *err)\r
263 {\r
264   apr_pool_t *pool;\r
265   svn_error_t *new_err = NULL, *tmp_err = NULL;\r
266 \r
267   if (apr_pool_create(&pool, NULL))\r
268     abort();\r
269 \r
270   for (; err; err = err->child)\r
271     {\r
272       if (! new_err)\r
273         {\r
274           new_err = apr_palloc(pool, sizeof(*new_err));\r
275           tmp_err = new_err;\r
276         }\r
277       else\r
278         {\r
279           tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));\r
280           tmp_err = tmp_err->child;\r
281         }\r
282       *tmp_err = *err;\r
283       tmp_err->pool = pool;\r
284       if (tmp_err->message)\r
285         tmp_err->message = apr_pstrdup(pool, tmp_err->message);\r
286     }\r
287 \r
288 #if defined(SVN_DEBUG)\r
289   apr_pool_cleanup_register(pool, tmp_err,\r
290                             err_abort,\r
291                             apr_pool_cleanup_null);\r
292 #endif\r
293 \r
294   return new_err;\r
295 }\r
296 \r
297 void\r
298 svn_error_clear(svn_error_t *err)\r
299 {\r
300   if (err)\r
301     {\r
302 #if defined(SVN_DEBUG)\r
303       while (err->child)\r
304         err = err->child;\r
305       apr_pool_cleanup_kill(err->pool, err, err_abort);\r
306 #endif\r
307       svn_pool_destroy(err->pool);\r
308     }\r
309 }\r
310 \r
311 static void\r
312 print_error(svn_error_t *err, FILE *stream, const char *prefix)\r
313 {\r
314   char errbuf[256];\r
315   const char *err_string;\r
316   svn_error_t *temp_err = NULL;  /* ensure initialized even if\r
317                                     err->file == NULL */\r
318   /* Pretty-print the error */\r
319   /* Note: we can also log errors here someday. */\r
320 \r
321 #ifdef SVN_DEBUG\r
322   /* Note: err->file is _not_ in UTF-8, because it's expanded from\r
323            the __FILE__ preprocessor macro. */\r
324   const char *file_utf8;\r
325 \r
326   if (err->file\r
327       && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,\r
328                                               err->pool)))\r
329     svn_error_clear(svn_cmdline_fprintf(stream, err->pool,\r
330                                         "%s:%ld", err->file, err->line));\r
331   else\r
332     {\r
333       svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,\r
334                                         stream, err->pool));\r
335       svn_error_clear(temp_err);\r
336     }\r
337 \r
338   svn_error_clear(svn_cmdline_fprintf(stream, err->pool,\r
339                                       ": (apr_err=%d)\n", err->apr_err));\r
340 #endif /* SVN_DEBUG */\r
341 \r
342   /* Only print the same APR error string once. */\r
343   if (err->message)\r
344     {\r
345       svn_error_clear(svn_cmdline_fprintf(stream, err->pool, "%s%s\n",\r
346                                           prefix, err->message));\r
347     }\r
348   else\r
349     {\r
350       /* Is this a Subversion-specific error code? */\r
351       if ((err->apr_err > APR_OS_START_USEERR)\r
352           && (err->apr_err <= APR_OS_START_CANONERR))\r
353         err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));\r
354       /* Otherwise, this must be an APR error code. */\r
355       else if ((temp_err = svn_utf_cstring_to_utf8\r
356                 (&err_string, apr_strerror(err->apr_err, errbuf,\r
357                                            sizeof(errbuf)), err->pool)))\r
358         {\r
359           svn_error_clear(temp_err);\r
360           err_string = _("Can't recode error string from APR");\r
361         }\r
362 \r
363       svn_error_clear(svn_cmdline_fprintf(stream, err->pool,\r
364                                           "%s%s\n", prefix, err_string));\r
365     }\r
366 }\r
367 \r
368 void\r
369 svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)\r
370 {\r
371   svn_handle_error2(err, stream, fatal, "svn: ");\r
372 }\r
373 \r
374 void\r
375 svn_handle_error2(svn_error_t *err,\r
376                   FILE *stream,\r
377                   svn_boolean_t fatal,\r
378                   const char *prefix)\r
379 {\r
380   /* In a long error chain, there may be multiple errors with the same\r
381      error code and no custom message.  We only want to print the\r
382      default message for that code once; printing it multiple times\r
383      would add no useful information.  The 'empties' array below\r
384      remembers the codes of empty errors already seen in the chain.\r
385 \r
386      We could allocate it in err->pool, but there's no telling how\r
387      long err will live or how many times it will get handled.  So we\r
388      use a subpool. */\r
389   apr_pool_t *subpool;\r
390   apr_array_header_t *empties;\r
391   svn_error_t *tmp_err;\r
392 \r
393   /* ### The rest of this file carefully avoids using svn_pool_*(),\r
394      preferring apr_pool_*() instead.  I can't remember why -- it may\r
395      be an artifact of r3719, or it may be for some deeper reason --\r
396      but I'm playing it safe and using apr_pool_*() here too. */\r
397   apr_pool_create(&subpool, err->pool);\r
398   empties = apr_array_make(subpool, 0, sizeof(apr_status_t));\r
399 \r
400   tmp_err = err;\r
401   while (tmp_err)\r
402     {\r
403       int i;\r
404       svn_boolean_t printed_already = FALSE;\r
405 \r
406       if (! tmp_err->message)\r
407         {\r
408           for (i = 0; i < empties->nelts; i++)\r
409             {\r
410               if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )\r
411                 {\r
412                   printed_already = TRUE;\r
413                   break;\r
414                 }\r
415             }\r
416         }\r
417 \r
418       if (! printed_already)\r
419         {\r
420           print_error(tmp_err, stream, prefix);\r
421           if (! tmp_err->message)\r
422             {\r
423               APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;\r
424             }\r
425         }\r
426 \r
427       tmp_err = tmp_err->child;\r
428     }\r
429 \r
430   svn_pool_destroy(subpool);\r
431 \r
432   fflush(stream);\r
433   if (fatal)\r
434     {\r
435       /* Avoid abort()s in maintainer mode. */\r
436       svn_error_clear(err);\r
437 \r
438       /* We exit(1) here instead of abort()ing so that atexit handlers\r
439          get called. */\r
440       exit(EXIT_FAILURE);\r
441     }\r
442 }\r
443 \r
444 \r
445 void\r
446 svn_handle_warning(FILE *stream, svn_error_t *err)\r
447 {\r
448   svn_handle_warning2(stream, err, "svn: ");\r
449 }\r
450 \r
451 void\r
452 svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)\r
453 {\r
454   char buf[256];\r
455 \r
456   svn_error_clear(svn_cmdline_fprintf\r
457                   (stream, err->pool,\r
458                    _("%swarning: %s\n"),\r
459                    prefix, svn_err_best_message(err, buf, sizeof(buf))));\r
460   fflush(stream);\r
461 }\r
462 \r
463 const char *\r
464 svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)\r
465 {\r
466   if (err->message)\r
467     return err->message;\r
468   else\r
469     return svn_strerror(err->apr_err, buf, bufsize);\r
470 }\r
471 \r
472 \f\r
473 /* svn_strerror() and helpers */\r
474 \r
475 typedef struct {\r
476   svn_errno_t errcode;\r
477   const char *errdesc;\r
478 } err_defn;\r
479 \r
480 /* To understand what is going on here, read svn_error_codes.h. */\r
481 #define SVN_ERROR_BUILD_ARRAY\r
482 #include "svn_error_codes.h"\r
483 \r
484 char *\r
485 svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)\r
486 {\r
487   const err_defn *defn;\r
488 \r
489   for (defn = error_table; defn->errdesc != NULL; ++defn)\r
490     if (defn->errcode == (svn_errno_t)statcode)\r
491       {\r
492         apr_cpystrn(buf, _(defn->errdesc), bufsize);\r
493         return buf;\r
494       }\r
495 \r
496   return apr_strerror(statcode, buf, bufsize);\r
497 }\r
498 \r
499 svn_error_t *\r
500 svn_error_raise_on_malfunction(svn_boolean_t can_return,\r
501                                const char *file, int line,\r
502                                const char *expr)\r
503 {\r
504   if (!can_return)\r
505     abort(); /* Nothing else we can do as a library */\r
506 \r
507   if (expr)\r
508     return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,\r
509                              _("In file '%s' line %d: assertion failed (%s)"),\r
510                              file, line, expr);\r
511   else\r
512     return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,\r
513                              _("In file '%s' line %d: internal malfunction"),\r
514                              file, line);\r
515 }\r
516 \r
517 svn_error_t *\r
518 svn_error_abort_on_malfunction(svn_boolean_t can_return,\r
519                                const char *file, int line,\r
520                                const char *expr)\r
521 {\r
522   svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);\r
523 \r
524   svn_handle_error2(err, stderr, FALSE, "svn: ");\r
525   abort();\r
526   return err;  /* Not reached. */\r
527 }\r
528 \r
529 /* The current handler for reporting malfunctions, and its default setting. */\r
530 static svn_error_malfunction_handler_t malfunction_handler\r
531   = svn_error_abort_on_malfunction;\r
532 \r
533 svn_error_malfunction_handler_t\r
534 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)\r
535 {\r
536   svn_error_malfunction_handler_t old_malfunction_handler\r
537     = malfunction_handler;\r
538 \r
539   malfunction_handler = func;\r
540   return old_malfunction_handler;\r
541 }\r
542 \r
543 svn_error_t *\r
544 svn_error__malfunction(svn_boolean_t can_return,\r
545                        const char *file, int line,\r
546                        const char *expr)\r
547 {\r
548   return malfunction_handler(can_return, file, line, expr);\r
549 }\r