This repository has been archived by the owner on Oct 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 123
/
elektra_error.c
368 lines (319 loc) · 11.4 KB
/
elektra_error.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/**
* @file
*
* @brief The error module of the High level API.
* Can be used to create errors from scratch or from errors that were attached to keys using src/libs/elektra/errors.
*
* @copyright BSD License (see doc/LICENSE.md or http://www.libelektra.org)
*/
#include "elektra/conversion.h"
#include "elektra/error.h"
#include "kdberrors.h"
#include "kdbhelper.h"
#include "kdbprivate.h"
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
// kdbprivate.h
/**
* Creates a new ElektraError using the provided values.
* The returned value will be allocated with elektraCalloc().
*
* @param code The error code of the error. Will be copied and stored in the struct.
* @param description The description of the error. Will be copied and stored in the struct.
* @param module The module that raised the error. Will be copied and stored in the struct.
* @param file The file that raised the error. Will be copied and stored in the struct.
* @param line The line in which the error was raised.
*
* @return A newly allocated ElektraError (free with elektraErrorReset()).
*/
ElektraError * elektraErrorCreate (const char * code, const char * description, const char * module, const char * file, kdb_long_t line)
{
ElektraError * const error = elektraCalloc (sizeof (struct _ElektraError));
error->code = code == NULL ? NULL : elektraStrDup (code);
error->codeFromKey = NULL;
error->description = description == NULL ? NULL : elektraStrDup (description);
error->module = module == NULL ? NULL : elektraStrDup (module);
error->file = file == NULL ? NULL : elektraStrDup (file);
error->line = line;
error->warningCount = 0;
error->warningAlloc = 0;
error->warnings = NULL;
error->errorKey = NULL;
return error;
}
/**
* Adds a warning to an existing ElektraError struct.
* If you want to report a warning without an error, create a dummy error with
* elektraErrorPureWarning() and then add a warning to it.
*
* @param error The error to which @p warning shall be added.
* @param warning The warning to add. Once added it is owned by @p error.
* DO NOT call elektraErrorReset() on it afterwards.
*/
void elektraErrorAddWarning (ElektraError * error, ElektraError * warning)
{
if (error->warningAlloc == 0)
{
error->warningCount = 1;
error->warningAlloc = 4;
error->warnings = elektraCalloc (error->warningAlloc * sizeof (ElektraError *));
}
else
{
++error->warningCount;
if (error->warningCount > error->warningAlloc)
{
error->warningAlloc *= 2;
elektraRealloc ((void **) &error->warnings, error->warningAlloc * sizeof (ElektraError *));
}
}
error->warnings[error->warningCount - 1] = warning;
}
/**
* Extracts the error and all warnings from the given key.
* If no error exists, a pure warning error will be used.
* @see elektraErrorPureWarning
*
* @note Use the functions in src/libs/elektra/errors.c to add errors to a key.
*
* @param key The to extract error and warnings from.
*
* @return A newly allocated ElektraError (free with elektraErrorReset()).
*/
ElektraError * elektraErrorFromKey (Key * key)
{
if (key == NULL)
{
return NULL;
}
ElektraError * error;
if (keyGetMeta (key, "error") == NULL)
{
error = elektraErrorPureWarning ();
}
else
{
const Key * reasonMeta = keyGetMeta (key, "error/reason");
const char * codeFromKey = elektraStrDup (keyString (keyGetMeta (key, "error/number")));
const char * description = elektraStrDup (keyString (keyGetMeta (key, "error/description")));
const char * module = elektraStrDup (keyString (keyGetMeta (key, "error/module")));
const char * file = elektraStrDup (keyString (keyGetMeta (key, "error/file")));
char * fullDescription =
reasonMeta != NULL ? elektraFormat ("%s: %s", description, keyString (reasonMeta)) : elektraStrDup (description);
kdb_long_t line = 0;
elektraKeyToLong (key, &line);
error = elektraErrorCreate (NULL, fullDescription, module, file, line);
error->codeFromKey = elektraStrDup (codeFromKey);
error->errorKey = key;
elektraFree (fullDescription);
}
// Code for extracting warnings was adapted from src/tools/kdb/coloredkdbio.h:printWarnings()
KeySet * metaKeys = keyMeta (key);
Key * warningsParent = keyNew ("meta:/warnings", KEY_END);
KeySet * warningKeys = ksCut (metaKeys, warningsParent);
if (ksGetSize (warningKeys) > 0)
{
for (elektraCursor it = 0; it < ksGetSize (warningKeys); it++)
{
if (!keyIsDirectlyBelow (warningsParent, ksAtCursor (warningKeys, it)))
{
// Warning details are sub-keys of the warning key. (e.g. .../warnings/#0/line, .../warnings/#0/reason, ...)
// For the extraction to work, we have to ignore the warningsParent itself and only look at keys directly
// below the warningParent (i.e. .../warnings/#0, .../warnings/#1, ...)
continue;
}
// Extract warning details set by errors.c
Key * warningKey = ksAtCursor (warningKeys, it);
const char * warningKeyName = keyName (warningKey);
char * lookupName = elektraFormat ("%s/number", warningKeyName);
// "number" and "code" are used interchangeably for the error code. "line" is the line number.
const char * code = keyString (ksLookupByName (warningKeys, lookupName, 0));
elektraFree (lookupName);
lookupName = elektraFormat ("%s/reason", warningKeyName);
const Key * reasonKey = ksLookupByName (warningKeys, lookupName, 0);
elektraFree (lookupName);
lookupName = elektraFormat ("%s/description", warningKeyName);
const char * description = keyString (ksLookupByName (warningKeys, lookupName, 0));
elektraFree (lookupName);
lookupName = elektraFormat ("%s/module", warningKeyName);
const char * module = keyString (ksLookupByName (warningKeys, lookupName, 0));
elektraFree (lookupName);
lookupName = elektraFormat ("%s/file", warningKeyName);
const char * file = keyString (ksLookupByName (warningKeys, lookupName, 0));
elektraFree (lookupName);
lookupName = elektraFormat ("%s/line", warningKeyName);
Key * lineKey = ksLookupByName (warningKeys, lookupName, 0);
elektraFree (lookupName);
kdb_long_t lineNumber = -1;
elektraKeyToLong (lineKey, &lineNumber);
// Generate fullDescription out of reason and description. Reason might be null.
char * fullDescription = reasonKey != NULL ? elektraFormat ("%s: %s", description, keyString (reasonKey)) :
elektraStrDup (description);
// Code, module, file and lineNumber are compile-time constants, no need to strDup().
ElektraError * warning = elektraErrorCreate (code, fullDescription, module, file, lineNumber);
elektraFree (fullDescription);
warning->codeFromKey = elektraStrDup (code);
warning->errorKey = key;
elektraErrorAddWarning (error, warning);
}
}
keyDel (warningsParent);
ksDel (warningKeys);
return error;
}
/**
* Creates a "Key not found" error
*
* @param keyname The name of the key that wasn't found.
*
* @return A newly allocated ElektraError (free with elektraErrorReset()).
*/
ElektraError * elektraErrorKeyNotFound (const char * keyname)
{
char * description = elektraFormat ("The key '%s' could not be found.", keyname);
ElektraError * error = elektraErrorCreate (ELEKTRA_ERROR_INTERNAL, description, "highlevel", "unknown", 0);
elektraFree (description);
return error;
}
/**
* Creates a "Wrong type" error
*
* @param keyname The name of the key that had the wrong type.
* @param expectedType The type that was expected.
* @param actualType The type that was actually found.
*
* @return A newly allocated ElektraError (free with elektraErrorReset()).
*/
ElektraError * elektraErrorWrongType (const char * keyname, KDBType expectedType, KDBType actualType)
{
char * description =
elektraFormat ("The key '%s' has the wrong type (expected '%s' but got '%s').", keyname, expectedType, actualType);
ElektraError * error = elektraErrorCreate (ELEKTRA_ERROR_VALIDATION_SEMANTIC, description, "highlevel", "unknown", 0);
elektraFree (description);
return error;
}
/**
* Creates a "Null error argument" error
*
* @param function The name of the function that was called with a null pointer error argument.
*
* @return A newly allocated ElektraError (free with elektraErrorReset()).
*/
ElektraError * elektraErrorNullError (const char * function)
{
char * description = elektraFormat ("The value passed to the ElektraError ** argument of %s was NULL.", function);
ElektraError * error = elektraErrorCreate (ELEKTRA_ERROR_INTERNAL, description, "highlevel", "unknown", 0);
elektraFree (description);
return error;
}
/**
* Creates a "Conversion to string failed" error
*
* @param sourceType The type which failed to be converted to string.
* @param keyname The name of the key that couldn't be converted.
*
* @return A newly allocated ElektraError (free with elektraErrorReset()).
*/
ElektraError * elektraErrorConversionToString (KDBType sourceType, const char * keyname)
{
char * description = elektraFormat ("The value of key '%s' with type '%s' could not be converted to string.", keyname, sourceType);
ElektraError * error = elektraErrorCreate (ELEKTRA_ERROR_VALIDATION_SEMANTIC, description, "highlevel", "unknown", 0);
elektraFree (description);
return error;
}
/**
* Creates a "Conversion from string failed" error
*
* @param targetType The type into which @p sourceValue couldn't be converted.
* @param keyname The name of the key that couldn't be converted.
* @param sourceValue The value that couldn't be converted.
*
* @return A newly allocated ElektraError (free with elektraErrorReset()).
*/
ElektraError * elektraErrorConversionFromString (KDBType targetType, const char * keyname, const char * sourceValue)
{
char * description =
elektraFormat ("The value '%s' of key '%s' could not be converted to type '%s'.", sourceValue, keyname, targetType);
ElektraError * error = elektraErrorCreate (ELEKTRA_ERROR_VALIDATION_SEMANTIC, description, "highlevel", "unknown", 0);
elektraFree (description);
return error;
}
// elektra/error.h
/**
* \addtogroup highlevel High-level API
* @{
*/
/**
* Creates a dummy ElektraError struct to store warnings in.
* If elektraErrorCode() is called on the resulting struct, it will return NULL.
*
* @return A newly allocated ElektraError (free with elektraFree()).
*/
ElektraError * elektraErrorPureWarning (void)
{
return elektraErrorCreate (NULL, "", NULL, NULL, -1);
}
/**
* @return the error code of the given error
*/
const char * elektraErrorCode (const ElektraError * error)
{
return error->errorKey == NULL ? error->code : error->codeFromKey;
}
/**
* @return the description for the given error
*/
const char * elektraErrorDescription (const ElektraError * error)
{
return error->description;
}
/**
* Frees the memory used by the error and sets
* the referenced error variable to NULL.
*/
void elektraErrorReset (ElektraError ** error)
{
if (*error == NULL)
{
return;
}
ElektraError * actualError = *error;
if (actualError->description != NULL)
{
elektraFree (actualError->description);
}
if (actualError->codeFromKey != NULL)
{
elektraFree (actualError->codeFromKey);
}
if (actualError->code != NULL)
{
elektraFree (actualError->code);
}
if (actualError->module != NULL)
{
elektraFree (actualError->module);
}
if (actualError->file != NULL)
{
elektraFree (actualError->file);
}
if (actualError->warnings != NULL)
{
for (int i = 0; i < actualError->warningCount; ++i)
{
elektraErrorReset (&actualError->warnings[i]);
}
elektraFree (actualError->warnings);
}
elektraFree (actualError);
*error = NULL;
}
/**
* @}
*/
#ifdef __cplusplus
};
#endif