216
|
1 |
#ifndef LIBQMSK_ERROR_H
|
|
2 |
#define LIBQMSK_ERROR_H
|
|
3 |
|
|
4 |
/**
|
|
5 |
* @file
|
|
6 |
*
|
|
7 |
* Support for error handling, using explicit function return codes and error info contexts.
|
|
8 |
*
|
|
9 |
* This is designed to support multiple "namespaces" of errors, which are entirely independent of eachother. These
|
|
10 |
* namespaces can then reference one another, to build a 'tree' of useable error codes. Hence, each 'level' of this
|
|
11 |
* tree has it's own set of error codes, and then error info contexts contain a stack of these error codes, which can
|
|
12 |
* then be used to trace the error down.
|
|
13 |
*/
|
217
|
14 |
#include <stdbool.h>
|
216
|
15 |
|
|
16 |
/**
|
|
17 |
* The type used to represent a scalar error code, there are only unique per level.
|
|
18 |
*
|
|
19 |
* Note that this type is signed to avoid nastyness with negating error codes, but all valid ERR_* codes are *always*
|
|
20 |
* greater than zero in value.
|
|
21 |
*/
|
|
22 |
typedef signed short err_t;
|
|
23 |
|
|
24 |
/**
|
|
25 |
* Standardized err_t code to mean no error, evaluates as logical false.
|
|
26 |
*/
|
|
27 |
static const err_t SUCCESS = 0;
|
|
28 |
|
|
29 |
/**
|
|
30 |
* Extra information about an error, used to reference error codes in external libraries etc.
|
|
31 |
*/
|
|
32 |
union error_extra {
|
|
33 |
/** (signed) integer code */
|
|
34 |
int int_;
|
|
35 |
|
|
36 |
/** Constant string */
|
|
37 |
const char *str;
|
|
38 |
};
|
|
39 |
|
|
40 |
/**
|
|
41 |
* Errors can contain 'extra' information to reference things like error codes from external libraries; when these are
|
|
42 |
* used, a 'type' must be specified to handle these.
|
|
43 |
*/
|
|
44 |
struct error_extra_type {
|
|
45 |
/** Short name */
|
|
46 |
const char *name;
|
|
47 |
|
|
48 |
/** Conversion func, must return a static pointer */
|
|
49 |
const char * (*msg_func) (const struct error_extra_type *type, const union error_extra *extra);
|
|
50 |
};
|
|
51 |
|
|
52 |
/**
|
|
53 |
* error_extra_type for libc errno values
|
|
54 |
*/
|
|
55 |
const struct error_extra_type error_extra_errno;
|
|
56 |
|
|
57 |
/**
|
|
58 |
* error_extra_type for string error values
|
|
59 |
*/
|
|
60 |
const struct error_extra_type error_extra_string;
|
|
61 |
|
|
62 |
/**
|
|
63 |
* Description of an error code
|
|
64 |
*/
|
|
65 |
struct error_type {
|
|
66 |
/** The actual code */
|
|
67 |
err_t code;
|
|
68 |
|
|
69 |
/** The short name of the error */
|
|
70 |
const char *name;
|
|
71 |
|
|
72 |
/** The type of any contained extra info */
|
|
73 |
const struct error_extra_type *extra_type;
|
|
74 |
|
|
75 |
/** Any linked error_code table for looking up sub-errors */
|
|
76 |
const struct error_list *sublist;
|
|
77 |
};
|
|
78 |
|
|
79 |
/**
|
|
80 |
* Helper macros to define error_type's
|
|
81 |
*/
|
|
82 |
#define ERROR_TYPE(code, name) \
|
|
83 |
{ (code), (name), NULL, NULL }
|
|
84 |
|
|
85 |
#define ERROR_TYPE_ERRNO(code, name) \
|
|
86 |
{ (code), (name), &error_extra_errno, NULL }
|
|
87 |
|
|
88 |
#define ERROR_TYPE_STRING(code, name) \
|
|
89 |
{ (code), (name), &error_extra_string, NULL }
|
|
90 |
|
|
91 |
#define ERROR_TYPE_CUSTOM(code, name, type) \
|
|
92 |
{ (code), (name), (type), NULL }
|
|
93 |
|
|
94 |
#define ERROR_TYPE_SUB(code, name, sub) \
|
|
95 |
{ (code), (name), NULL, (sub) }
|
|
96 |
|
|
97 |
#define ERROR_TYPE_END \
|
|
98 |
{ 0, NULL, NULL, NULL }
|
|
99 |
|
|
100 |
/**
|
|
101 |
* List of error types
|
|
102 |
*/
|
|
103 |
struct error_list {
|
|
104 |
/** Name of sublib */
|
|
105 |
const char *name;
|
|
106 |
|
|
107 |
/** The list */
|
|
108 |
struct error_type list[];
|
|
109 |
};
|
|
110 |
|
|
111 |
/**
|
|
112 |
* Helper macro to define an error_list
|
|
113 |
*/
|
|
114 |
#define ERROR_LIST(name, ...) \
|
|
115 |
{ (name), __VA_ARGS__, ERROR_TYPE_END }
|
|
116 |
|
|
117 |
/**
|
|
118 |
* Maximum number of nesting levels supported for errors
|
|
119 |
*/
|
|
120 |
#define ERROR_DEPTH_MAX 8
|
|
121 |
|
|
122 |
/**
|
|
123 |
* Information about an actual error
|
|
124 |
*/
|
|
125 |
typedef struct error_state {
|
|
126 |
/** The stack of error codes */
|
|
127 |
struct error_item {
|
|
128 |
/** This level's error code */
|
|
129 |
err_t code;
|
|
130 |
|
|
131 |
/** Optional table for interpreting error_code, can be used instead of error_code::next */
|
|
132 |
const struct error_list *list;
|
|
133 |
|
|
134 |
} stack[ERROR_DEPTH_MAX];
|
|
135 |
|
|
136 |
/** Current top of stack for accumulating errors, NULL means empty stack */
|
217
|
137 |
struct error_item *cur;
|
216
|
138 |
|
|
139 |
/** Type info for external error info */
|
|
140 |
const struct error_extra_type *extra_type;
|
|
141 |
|
|
142 |
/** Value of external error info */
|
|
143 |
union error_extra extra_value;
|
|
144 |
|
|
145 |
} error_t;
|
|
146 |
|
|
147 |
/**
|
|
148 |
* Look up the error_type for the given table and scalar code.
|
|
149 |
*/
|
|
150 |
const struct error_type* error_lookup_code (const struct error_list *list, err_t code);
|
|
151 |
|
|
152 |
/**
|
|
153 |
* Look up the error_type for for the given root table and error_info.
|
|
154 |
*/
|
|
155 |
const struct error_type* error_lookup (const error_t *err);
|
|
156 |
|
|
157 |
/**
|
217
|
158 |
* Return the error name for the given code.
|
216
|
159 |
*/
|
217
|
160 |
const char* error_name (const struct error_list *list, err_t code);
|
216
|
161 |
|
|
162 |
/**
|
|
163 |
* Maximum length of messages returned by error_msg
|
|
164 |
*/
|
|
165 |
#define ERROR_MSG_MAX 1024
|
|
166 |
|
|
167 |
/**
|
|
168 |
* Return a pointer to a statically allocated string describing the given full error
|
|
169 |
*/
|
|
170 |
const char* error_msg (const error_t *err);
|
|
171 |
|
|
172 |
/**
|
|
173 |
* Compare the given errors for equivalency
|
|
174 |
*/
|
|
175 |
bool error_cmp_eq (const error_t *a, const error_t *b);
|
|
176 |
|
|
177 |
|
|
178 |
/**
|
217
|
179 |
* Reset the given error state to an empty state.
|
|
180 |
*
|
|
181 |
* This is just the same as memset() with zero.
|
216
|
182 |
*/
|
217
|
183 |
void error_reset (error_t *err);
|
216
|
184 |
|
|
185 |
/**
|
|
186 |
* Evaluates to the current top of the error stack,
|
|
187 |
*/
|
217
|
188 |
static struct error_item* error_top (error_t *err)
|
216
|
189 |
{
|
|
190 |
return err->cur ? err->cur : err->stack;
|
|
191 |
}
|
|
192 |
|
|
193 |
/**
|
|
194 |
* Evaluate to the most recent error code pushed, or SUCCESS if none
|
|
195 |
*/
|
|
196 |
static inline err_t error_code (error_t *err)
|
|
197 |
{
|
|
198 |
return err->cur ? err->cur->code : SUCCESS;
|
|
199 |
}
|
|
200 |
|
|
201 |
/**
|
|
202 |
* Reset the error state, push the given initial err.
|
|
203 |
*/
|
|
204 |
void error_set (error_t *err, const struct error_list *list, err_t code);
|
|
205 |
|
|
206 |
/**
|
|
207 |
* Reset, push err, and set extra.
|
|
208 |
*/
|
|
209 |
void error_set_extra (error_t *err, const struct error_list *list, err_t code, const struct error_extra_type *type, union error_extra extra);
|
|
210 |
|
|
211 |
/**
|
|
212 |
* Push an additional level of error info onto the error state's stack. If it doesn't fit, XXX: it is ignored.
|
|
213 |
*/
|
|
214 |
void error_push (error_t *err, const struct error_list *list, err_t code);
|
|
215 |
|
|
216 |
/**
|
|
217 |
* Copy value of error
|
|
218 |
*/
|
|
219 |
void error_copy (error_t *dst, const error_t *src);
|
|
220 |
|
|
221 |
/** Evalutes to the lvalue representing the error code at the top of the stack */
|
|
222 |
#define ERROR_CODE(err_state) \
|
|
223 |
(error_top(err_state)->code)
|
|
224 |
|
|
225 |
/** Push a new error code onto the given state */
|
|
226 |
#define PUSH_ERROR(err_state, err_list, err_code) ({ \
|
|
227 |
error_push(err_state, err_list, err_code); \
|
|
228 |
err_code; })
|
|
229 |
|
|
230 |
/** Set the error to the given value */
|
|
231 |
#define SET_ERROR(err_state, err_list, err_code) ({ \
|
|
232 |
error_set(err_state, err_list, err_code); \
|
|
233 |
err_code; })
|
|
234 |
|
|
235 |
/* Helper macro to use error_set_extra */
|
|
236 |
#define _ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, _err_extra_field_, err_extra_value) \
|
|
237 |
error_set_extra(err_state, err_list, err_code, err_extra_type, ((union error_extra) { ._err_extra_field_ = (err_extra_value)}))
|
|
238 |
|
|
239 |
/** Set the error with extra info as integer */
|
|
240 |
#define SET_ERROR_EXTRA(err_state, err_list, err_code, err_extra_type, err_extra_int) ({ \
|
|
241 |
_ERROR_SET_EXTRA(err_state, err_list, err_code, err_extra_type, int_, err_extra); \
|
|
242 |
err_code; })
|
|
243 |
|
|
244 |
/** Set the error with extra info as the libc errno */
|
|
245 |
#define SET_ERROR_ERRNO(err_state, err_list, err_code) ({ \
|
|
246 |
_ERROR_SET_EXTRA(err_state, err_list, err_code, &error_extra_errno, int_, errno); \
|
|
247 |
err_code; })
|
|
248 |
|
|
249 |
/** Set the error with extra info as the given static string */
|
|
250 |
#define SET_ERROR_STR(err_state, err_list, err_code, err_str) ({ \
|
|
251 |
_ERROR_SET_EXTRA(err_state, err_list, err_code, &error_extra_string, str, err_str); \
|
|
252 |
err_code; })
|
|
253 |
|
|
254 |
/** Set the error with the contents of the given error */
|
|
255 |
#define SET_ERROR_COPY(err_state_dst, err_state_src) ({ \
|
|
256 |
error_code(err_state_dst); })
|
|
257 |
|
|
258 |
/** Same as above, but also do a 'goto error' jump */
|
|
259 |
#define JUMP_PUSH_ERROR(...) do { PUSH_ERROR(__VA_ARGS__); goto error; } while (0)
|
|
260 |
#define JUMP_SET_ERROR(...) do { SET_ERROR(__VA_ARGS__); goto error; } while (0)
|
|
261 |
#define JUMP_SET_ERROR_EXTRA(...) do { SET_ERROR_EXTRA(__VA_ARGS__); goto error; } while (0)
|
|
262 |
#define JUMP_SET_ERROR_ERRNO(...) do { SET_ERROR_ERRNO(__VA_ARGS__); goto error; } while (0)
|
|
263 |
#define JUMP_SET_ERROR_STR(...) do { SET_ERROR_STR(__VA_ARGS__); goto error; } while (0)
|
|
264 |
#define JUMP_SET_ERROR_INFO(...) do { SET_ERROR_INFO(__VA_ARGS__); goto error; } while (0)
|
|
265 |
|
|
266 |
|
|
267 |
/**
|
|
268 |
* Abort execution of process with error message
|
|
269 |
*/
|
|
270 |
void _error_abort (const char *file, const char *line, const char *func, const char *fmt, ...);
|
|
271 |
#define error_abort(...) _error_abort(__FILE__, __LINE__, __func__, __VA_ARGS__);
|
|
272 |
|
|
273 |
/**
|
|
274 |
* Used to mark unexpcted conditions for switch-default. The given val must be an integer, as passed to switch
|
|
275 |
*/
|
|
276 |
#define NOT_REACHED(val) error_abort("%s = %#x", #val, (int) (val));
|
|
277 |
|
217
|
278 |
/**
|
|
279 |
* General-purpose errors that may be useful and don't belong in any more specific namespace.
|
|
280 |
*/
|
|
281 |
enum general_error_code {
|
|
282 |
ERR_GENERAL_NONE,
|
|
283 |
ERR_MEM, ///< memory allocation error
|
|
284 |
ERR_NOT_IMPLEMENTED, ///< function not implmented: <func>
|
|
285 |
ERR_MISC, ///< miscellaneous error: <error>
|
|
286 |
ERR_CMD_OPT, ///< invalid command line option: <error> - XXX: replace with something getopt
|
|
287 |
ERR_UNKNOWN, ///< unknown error
|
|
288 |
};
|
|
289 |
|
|
290 |
const struct error_list general_errors;
|
|
291 |
|
216
|
292 |
#endif /* LIBQMSK_ERROR_H */
|