1// Copyright 2012 Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9//   notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright
11//   notice, this list of conditions and the following disclaimer in the
12//   documentation and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors
14//   may be used to endorse or promote products derived from this software
15//   without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include "error.h"
30
31#include <assert.h>
32#include <err.h>
33#include <stdarg.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38
39/// Generic hook to format an error that does not have a format callback.
40///
41/// \param error Error for which to generate a message.
42/// \param output_buffer Buffer to hold the generated message.
43/// \param output_size Length of output_buffer.
44static int
45generic_format_callback(const kyua_error_t error, char* const output_buffer,
46                        size_t output_size)
47{
48    assert(error != NULL);
49    return snprintf(output_buffer, output_size, "Error '%s'", error->type_name);
50}
51
52
53/// Initializes an error object.
54///
55/// \param error Error for which to generate a message.
56/// \param type_name Name of the error type.
57/// \param data Opaque data that belongs to the error, for usage by
58///     error-specific methods like format_callback.
59/// \param data_size Size of the opaque data object.
60/// \param format_callback Type-specific method to generate a user
61///     representation of the error.
62///
63/// \return True if the initialization succeeds; false otherwise.  If
64/// false, the error object passed in has not been modified.
65static bool
66error_init(kyua_error_t const error, const char* const type_name,
67           void* const data, const size_t data_size,
68           const kyua_error_format_callback format_callback)
69{
70    assert(data != NULL || data_size == 0);
71    assert(data_size != 0 || data == NULL);
72
73    bool ok;
74
75    if (data == NULL) {
76        error->data = NULL;
77        error->needs_free = false;
78        ok = true;
79    } else {
80        void* new_data = malloc(data_size);
81        if (new_data == NULL) {
82            ok = false;
83        } else {
84            memcpy(new_data, data, data_size);
85            error->data = new_data;
86            ok = true;
87        }
88    }
89
90    if (ok) {
91        error->type_name = type_name;
92        error->format_callback = (format_callback == NULL) ?
93            generic_format_callback : format_callback;
94    }
95
96    return ok;
97}
98
99
100/// Allocates and initializes a new error.
101///
102/// \param type_name Name of the error type.
103/// \param data Opaque data that belongs to the error, for usage by
104///     error-specific methods like format_callback.
105/// \param data_size Size of the opaque data object.
106/// \param format_callback Type-specific method to generate a user
107///     representation of the error.
108///
109/// \return The newly initialized error, or an out of memory error.
110kyua_error_t
111kyua_error_new(const char* const type_name, void* const data,
112               const size_t data_size,
113               const kyua_error_format_callback format_callback)
114{
115    assert(data != NULL || data_size == 0);
116    assert(data_size != 0 || data == NULL);
117
118    kyua_error_t error = malloc(sizeof(struct kyua_error));
119    if (error == NULL)
120        error = kyua_oom_error_new();
121    else {
122        if (!error_init(error, type_name, data, data_size, format_callback)) {
123            free(error);
124            error = kyua_oom_error_new();
125        } else {
126            error->needs_free = true;
127        }
128    }
129
130    assert(error != NULL);
131    return error;
132}
133
134
135/// Releases an error.
136///
137/// \param error The error object to release.
138void
139kyua_error_free(kyua_error_t error)
140{
141    assert(error != NULL);
142
143    const bool needs_free = error->needs_free;
144
145    if (error->data != NULL)
146        free(error->data);
147    if (needs_free)
148        free(error);
149}
150
151
152/// Returns the "most important" of two errors.
153///
154/// "Most important" is defined as: the primary error is returned if set,
155/// otherwise the secondary error is returned.
156///
157/// It is the responsibility of the caller to free the *resulting* error of this
158/// call.  The original errors passed in should not be consulted any longer,
159/// because it is impossible to know which one was chosen.
160///
161/// \param primary The primary error to compare.
162/// \param [in,out] secondary The secondary error to compare.  This is freed if
163///     the primary error is set.
164///
165/// \return Either primary or secondary.
166kyua_error_t
167kyua_error_subsume(kyua_error_t primary, kyua_error_t secondary)
168{
169    if (kyua_error_is_set(primary)) {
170        if (kyua_error_is_set(secondary))
171            kyua_error_free(secondary);
172        return primary;
173    } else {
174        return secondary;
175    }
176}
177
178
179/// Constructor for a no-error condition.
180///
181/// \return Opaque representation of a no-error condition.
182kyua_error_t
183kyua_error_ok(void)
184{
185    return NULL;
186}
187
188
189/// Checks if the given error object represents an error or not.
190///
191/// \param error The error to check.
192///
193/// \return True if the error is set.
194bool
195kyua_error_is_set(const kyua_error_t error)
196{
197    return error != NULL;
198}
199
200
201/// Checks if the given error object is of a specific type.
202///
203/// \pre The error must be set.
204///
205/// \param error The error to check.
206/// \param type_name The type of the expected error.
207///
208/// \return True if the error is of type type_name.
209bool
210kyua_error_is_type(const kyua_error_t error, const char* type_name)
211{
212    assert(error != NULL);
213
214    return strcmp(error->type_name, type_name) == 0;
215}
216
217
218/// Returns a pointer to the error-specific data.
219///
220/// \pre The error must be set.
221///
222/// \param error The error to query.
223///
224/// \return An opaque pointer to the error data.  This should only be
225/// dereferenced by the methods of the error class that created it.
226const void*
227kyua_error_data(const kyua_error_t error)
228{
229    assert(error != NULL);
230
231    return error->data;
232}
233
234
235/// Generates a user-friendly representation of the error.
236///
237/// This cannot fail, but it is possible that the generated error does not
238/// fit in the provided buffer.
239///
240/// \pre The error must be set.
241///
242/// \param error Error for which to generate a message.
243/// \param output_buffer Buffer to hold the generated message.
244/// \param output_size Length of output_buffer.
245///
246/// \return The number of bytes written to output_buffer, or a negative value if
247/// there was an error.
248int
249kyua_error_format(const kyua_error_t error, char* const output_buffer,
250                  const size_t output_size)
251{
252    assert(kyua_error_is_set(error));
253    return error->format_callback(error, output_buffer, output_size);
254}
255
256
257/// Formats a string and appends an error code to it.
258///
259/// \param error Error to append to the formatted message.
260/// \param format User-specified message, as a formatting string.
261/// \param ap List of arguments to the format string.
262/// \param [out] output_buffer Buffer into which to write the message.
263/// \param output_size Length of the output_buffer.
264///
265/// \return The number of bytes written to output_buffer, or a negative value if
266/// there was an error.
267static int
268format_user_message(const kyua_error_t error, const char* format, va_list ap,
269                    char* const output_buffer, const size_t output_size)
270{
271    assert(kyua_error_is_set(error));
272
273    va_list ap2;
274    va_copy(ap2, ap);
275    size_t written = vsnprintf(output_buffer, output_size, format, ap2);
276    va_end(ap2);
277    if (written >= output_size)
278        return -1;
279
280    written += snprintf(output_buffer + written, output_size - written, ": ");
281    if (written >= output_size)
282        return -1;
283
284    return kyua_error_format(error, output_buffer + written,
285                             output_size - written);
286}
287
288
289/// Version of err(3) that works with kyua_error_t objects.
290///
291/// \param exit_code Error code with which to terminate the execution.
292/// \param error Error to append to the output.
293/// \param format User-specified message, as a formatting string.
294/// \param ... Positional arguments to the format string.
295///
296/// \post Execution terminates with exit_code.
297void
298kyua_error_err(const int exit_code, const kyua_error_t error,
299               const char* format, ...)
300{
301    char buffer[2048];
302
303    va_list ap;
304    va_start(ap, format);
305    (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
306    va_end(ap);
307    kyua_error_free(error);
308
309    errx(exit_code, "%s", buffer);
310}
311
312
313/// Writes an error to a file stream.
314///
315/// \param stream Stream to which to write the message.
316/// \param error Error to append to the output.  This is not released.
317/// \param format User-specified message, as a formatting string.
318/// \param ... Positional arguments to the format string.
319void
320kyua_error_fprintf(FILE* stream, const kyua_error_t error,
321                   const char* format, ...)
322{
323    char buffer[2048];
324
325    va_list ap;
326    va_start(ap, format);
327    (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
328    va_end(ap);
329
330    fprintf(stream, "%s", buffer);
331}
332
333
334/// Version of warn(3) that works with kyua_error_t objects.
335///
336/// \param error Error to append to the output.  This is not released.
337/// \param format User-specified message, as a formatting string.
338/// \param ... Positional arguments to the format string.
339void
340kyua_error_warn(const kyua_error_t error, const char* format, ...)
341{
342    char buffer[2048];
343
344    va_list ap;
345    va_start(ap, format);
346    (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
347    va_end(ap);
348
349    warnx("%s", buffer);
350}
351
352
353/// Name of an generic error type.
354const char* const kyua_generic_error_type = "generic";
355
356
357/// Generates a user-friendly representation of the error.
358///
359/// \pre The error must be set.
360///
361/// \param error Error for which to generate a message.
362/// \param output_buffer Buffer to hold the generated message.
363/// \param output_size Length of output_buffer.
364///
365/// \return The number of bytes written to output_buffer, or a negative value if
366/// there was an error.
367static int
368generic_format(const kyua_error_t error, char* const output_buffer,
369             const size_t output_size)
370{
371    assert(kyua_error_is_type(error, kyua_generic_error_type));
372
373    const char* message = kyua_error_data(error);
374    return snprintf(output_buffer, output_size, "%s", message);
375}
376
377
378/// Constructs a new generic error.
379///
380/// \param message Textual description of the problem.
381/// \param ... Positional arguments for the description.
382///
383/// \return The generated error.
384kyua_error_t
385kyua_generic_error_new(const char* message, ...)
386{
387    char formatted[1024];
388    va_list ap;
389
390    va_start(ap, message);
391    (void)vsnprintf(formatted, sizeof(formatted), message, ap);
392    va_end(ap);
393
394    return kyua_error_new(kyua_generic_error_type, formatted, sizeof(formatted),
395                          generic_format);
396}
397
398
399/// Name of a libc type.
400const char* const kyua_libc_error_type = "libc";
401
402
403/// Representation of a libc error.
404struct libc_error_data {
405    /// Value of the errno captured during the error creation.
406    int original_errno;
407
408    /// Explanation of the problem that lead to the error.
409    char description[4096];
410};
411/// Shorthand for a libc_error_data structure.
412typedef struct libc_error_data libc_error_data_t;
413
414
415/// Generates a user-friendly representation of the error.
416///
417/// \pre The error must be set.
418///
419/// \param error Error for which to generate a message.
420/// \param output_buffer Buffer to hold the generated message.
421/// \param output_size Length of output_buffer.
422///
423/// \return The number of bytes written to output_buffer, or a negative value if
424/// there was an error.
425static int
426libc_format(const kyua_error_t error, char* const output_buffer,
427            const size_t output_size)
428{
429    assert(kyua_error_is_type(error, kyua_libc_error_type));
430
431    const libc_error_data_t* data = kyua_error_data(error);
432    return snprintf(output_buffer, output_size, "%s: %s", data->description,
433                    strerror(data->original_errno));
434}
435
436
437/// Constructs a new libc error.
438///
439/// \param original_errno libc error code for this error.
440/// \param description Textual description of the problem.
441/// \param ... Positional arguments for the description.
442///
443/// \return The generated error.
444kyua_error_t
445kyua_libc_error_new(const int original_errno, const char* description, ...)
446{
447    va_list ap;
448
449    const size_t data_size = sizeof(libc_error_data_t);
450    libc_error_data_t* data = (libc_error_data_t*)malloc(data_size);
451    if (data == NULL)
452        return kyua_oom_error_new();
453
454    data->original_errno = original_errno;
455    va_start(ap, description);
456    (void)vsnprintf(data->description, sizeof(data->description),
457                    description, ap);
458    va_end(ap);
459
460    return kyua_error_new(kyua_libc_error_type, data, data_size, libc_format);
461}
462
463
464/// Extracts the original errno of a libc error.
465///
466/// \pre error must have been constructed by kyua_libc_error_new.
467///
468/// \param error The error object to access.
469///
470/// \return The libc error code.
471int
472kyua_libc_error_errno(const kyua_error_t error)
473{
474    assert(kyua_error_is_type(error, kyua_libc_error_type));
475
476    const struct libc_error_data* data = kyua_error_data(error);
477    return data->original_errno;
478}
479
480
481/// Name of an OOM type.
482const char* const kyua_oom_error_type = "oom";
483
484
485/// Data of an out of memory error.
486///
487/// All error types are allocated in dynamic memory.  However, doing so for
488/// an out of memory error is not possible because, when we are out of
489/// memory, we probably cannot allocate more memory to generate an error.
490/// Therefore, we just keep a single static instance of the out of memory
491/// error around all the time.
492static struct kyua_error oom_error;
493
494
495/// Generates a user-friendly representation of the error.
496///
497/// \pre The error must be set.
498///
499/// \param error Error for which to generate a message.
500/// \param output_buffer Buffer to hold the generated message.
501/// \param output_size Length of output_buffer.
502///
503/// \return The number of bytes written to output_buffer, or a negative value if
504/// there was an error.
505static int
506oom_format(const kyua_error_t error, char* const output_buffer,
507           const size_t output_size)
508{
509    assert(kyua_error_is_type(error, kyua_oom_error_type));
510
511    return snprintf(output_buffer, output_size, "Not enough memory");
512}
513
514
515/// Constructs a new out-of-memory error.
516///
517/// This will always succeed because we just return a reference to the
518/// statically-allocated oom_error.
519///
520/// \return An error representing an out of memory condition.
521kyua_error_t
522kyua_oom_error_new(void)
523{
524    // This is idempotent; no need to ensure that we call it only once.
525    const bool ok = error_init(&oom_error, kyua_oom_error_type, NULL, 0,
526                               oom_format);
527    assert(ok);
528
529    return &oom_error;
530}
531
532
533/// Name of an usage error type.
534const char* const kyua_usage_error_type = "usage";
535
536
537/// Generates a user-friendly representation of the error.
538///
539/// \pre The error must be set.
540///
541/// \param error Error for which to generate a message.
542/// \param output_buffer Buffer to hold the generated message.
543/// \param output_size Length of output_buffer.
544///
545/// \return The number of bytes written to output_buffer, or a negative value if
546/// there was an error.
547static int
548usage_format(const kyua_error_t error, char* const output_buffer,
549             const size_t output_size)
550{
551    assert(kyua_error_is_type(error, kyua_usage_error_type));
552
553    const char* message = kyua_error_data(error);
554    return snprintf(output_buffer, output_size, "%s", message);
555}
556
557
558/// Constructs a new usage error.
559///
560/// \param message Textual description of the problem.
561/// \param ... Positional arguments for the description.
562///
563/// \return The generated error.
564kyua_error_t
565kyua_usage_error_new(const char* message, ...)
566{
567    char formatted[1024];
568    va_list ap;
569
570    va_start(ap, message);
571    (void)vsnprintf(formatted, sizeof(formatted), message, ap);
572    va_end(ap);
573
574    return kyua_error_new(kyua_usage_error_type, formatted, sizeof(formatted),
575                          usage_format);
576}
577