]> git.draconx.ca Git - cdecl99.git/blob - src/error.c
8d26d5b7c604db7b4ecde35ffd2985a51f327fb6
[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  = NULL,
25         },
26 };
27
28 const char *cdecl__strerror(unsigned code)
29 {
30 #       include "errtab.h"
31
32         assert(code < sizeof offtab / sizeof offtab[0]);
33         assert(offtab[code] != 0);
34
35         return gettext((char *)&strtab + offtab[code]);
36 }
37
38 static void free_err(void *err)
39 {
40         if (err == &err_no_mem)
41                 return;
42
43         free(err);
44 }
45
46 static void set_error(const struct cdecl_error *err)
47 {
48         struct err_state *state;
49         size_t need_len = 0;
50
51         if (err->str) {
52                 need_len = strlen(err->str) + 1;
53         }
54
55         state = gl_tls_get(tls_key);
56         if (state == &err_no_mem)
57                 state = NULL;
58         if (!state || state->nstr < need_len) {
59                 struct err_state *tmp;
60                 
61                 tmp = realloc(state, sizeof *state + need_len);
62                 if (!tmp) {
63                         /* Re-use the existing state buffer, if any. */
64                         if (state)
65                                 state->err = err_no_mem.err;
66                         else
67                                 state = &err_no_mem;
68                         
69                         gl_tls_set(tls_key, state);
70                         return;
71                 }
72
73                 state = tmp;
74                 state->nstr = need_len;
75                 gl_tls_set(tls_key, state);
76         }
77
78         state->err = *err;
79         if (err->str) {
80                 memcpy(state->str, err->str, need_len);
81                 state->err.str = state->str;
82         } else {
83                 state->err.str = cdecl__strerror(state->err.code);
84         }
85 }
86
87 static void initialize(void)
88 {
89         cdecl__init_i18n();
90         err_no_mem.err.str = cdecl__strerror(CDECL_ENOMEM);
91
92         gl_tls_key_init(tls_key, free_err);
93
94         /*
95          * This default error message is a stop-gap measure until all library
96          * error conditions use the new interface.
97          */
98         set_error(&(const struct cdecl_error){ .code = CDECL_ENOPARSE });
99 }
100
101 const struct cdecl_error *cdecl_get_error(void)
102 {
103         struct err_state *state;
104
105         gl_once(tls_initialized, initialize);
106
107         state = gl_tls_get(tls_key);
108         assert(state);
109
110         return &state->err;
111 }
112
113 void cdecl__set_error(const struct cdecl_error *err)
114 {
115         gl_once(tls_initialized, initialize);
116
117         set_error(err);
118 }