]> git.draconx.ca Git - cdecl99.git/blob - src/error.c
Start implementing proper error handling.
[cdecl99.git] / src / error.c
1 #include <config.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <glthread/lock.h>
6 #include <glthread/tls.h>
7 #include "cdecl.h"
8 #include "error.h"
9 #include "i18n.h"
10
11 gl_once_define(static, tls_initialized);
12 static gl_tls_key_t tls_key;
13
14 struct err_state {
15         struct cdecl_error err;
16         size_t nstr;
17         char str[];
18 };
19
20 /* This error is reserved for extremely dire out-of-memory conditions. */
21 static struct err_state err_no_mem = {
22         .err = {
23                 .code = CDECL_ENOMEM,
24                 .str  = "failed to allocate memory",
25         },
26 };
27
28 static void free_err(void *err)
29 {
30         if (err == &err_no_mem)
31                 return;
32
33         free(err);
34 }
35
36 static void initialize(void)
37 {
38         cdecl__init_i18n();
39         err_no_mem.err.str = gettext(err_no_mem.err.str);
40
41         gl_tls_key_init(tls_key, free_err);
42
43         /*
44          * This default error message is a stop-gap measure until all library
45          * error conditions use the new interface.
46          */
47         cdecl__set_error(&(const struct cdecl_error){ .code = CDECL_ENOPARSE });
48 }
49
50 const struct cdecl_error *cdecl_get_error(void)
51 {
52         struct err_state *state;
53
54         gl_once(tls_initialized, initialize);
55
56         state = gl_tls_get(tls_key);
57         assert(state);
58
59         return &state->err;
60 }
61
62 void cdecl__set_error(const struct cdecl_error *err)
63 {
64         struct err_state *state;
65         size_t need_len = 0;
66
67         gl_once(tls_initialized, initialize);
68
69         if (err->str) {
70                 need_len = strlen(err->str) + 1;
71         }
72
73         state = gl_tls_get(tls_key);
74         if (state == &err_no_mem)
75                 state = NULL;
76         if (!state || state->nstr < need_len) {
77                 struct err_state *tmp;
78                 
79                 tmp = realloc(state, sizeof *state + need_len);
80                 if (!tmp) {
81                         /* Re-use the existing state buffer, if any. */
82                         if (state)
83                                 state->err = err_no_mem.err;
84                         else
85                                 state = &err_no_mem;
86                         
87                         gl_tls_set(tls_key, state);
88                         return;
89                 }
90
91                 state = tmp;
92                 state->nstr = need_len;
93                 gl_tls_set(tls_key, state);
94         }
95
96         state->err = *err;
97         if (err->str) {
98                 memcpy(state->str, err->str, need_len);
99                 state->err.str = state->str;
100         } else {
101                 state->err.str = "unknown error";
102         }
103 }