111be35a1SLionel Sambuc // Copyright 2012 Google Inc.
211be35a1SLionel Sambuc // All rights reserved.
311be35a1SLionel Sambuc //
411be35a1SLionel Sambuc // Redistribution and use in source and binary forms, with or without
511be35a1SLionel Sambuc // modification, are permitted provided that the following conditions are
611be35a1SLionel Sambuc // met:
711be35a1SLionel Sambuc //
811be35a1SLionel Sambuc // * Redistributions of source code must retain the above copyright
911be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer.
1011be35a1SLionel Sambuc // * Redistributions in binary form must reproduce the above copyright
1111be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer in the
1211be35a1SLionel Sambuc // documentation and/or other materials provided with the distribution.
1311be35a1SLionel Sambuc // * Neither the name of Google Inc. nor the names of its contributors
1411be35a1SLionel Sambuc // may be used to endorse or promote products derived from this software
1511be35a1SLionel Sambuc // without specific prior written permission.
1611be35a1SLionel Sambuc //
1711be35a1SLionel Sambuc // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1811be35a1SLionel Sambuc // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1911be35a1SLionel Sambuc // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2011be35a1SLionel Sambuc // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2111be35a1SLionel Sambuc // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2211be35a1SLionel Sambuc // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2311be35a1SLionel Sambuc // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2411be35a1SLionel Sambuc // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2511be35a1SLionel Sambuc // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2611be35a1SLionel Sambuc // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2711be35a1SLionel Sambuc // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2811be35a1SLionel Sambuc
2911be35a1SLionel Sambuc #include "error.h"
3011be35a1SLionel Sambuc
3111be35a1SLionel Sambuc #include <assert.h>
3211be35a1SLionel Sambuc #include <err.h>
3311be35a1SLionel Sambuc #include <stdarg.h>
3411be35a1SLionel Sambuc #include <stdio.h>
3511be35a1SLionel Sambuc #include <stdlib.h>
3611be35a1SLionel Sambuc #include <string.h>
3711be35a1SLionel Sambuc
3811be35a1SLionel Sambuc
3911be35a1SLionel Sambuc /// Generic hook to format an error that does not have a format callback.
4011be35a1SLionel Sambuc ///
4111be35a1SLionel Sambuc /// \param error Error for which to generate a message.
4211be35a1SLionel Sambuc /// \param output_buffer Buffer to hold the generated message.
4311be35a1SLionel Sambuc /// \param output_size Length of output_buffer.
4411be35a1SLionel Sambuc static int
generic_format_callback(const kyua_error_t error,char * const output_buffer,size_t output_size)4511be35a1SLionel Sambuc generic_format_callback(const kyua_error_t error, char* const output_buffer,
4611be35a1SLionel Sambuc size_t output_size)
4711be35a1SLionel Sambuc {
4811be35a1SLionel Sambuc assert(error != NULL);
4911be35a1SLionel Sambuc return snprintf(output_buffer, output_size, "Error '%s'", error->type_name);
5011be35a1SLionel Sambuc }
5111be35a1SLionel Sambuc
5211be35a1SLionel Sambuc
5311be35a1SLionel Sambuc /// Initializes an error object.
5411be35a1SLionel Sambuc ///
5511be35a1SLionel Sambuc /// \param error Error for which to generate a message.
5611be35a1SLionel Sambuc /// \param type_name Name of the error type.
5711be35a1SLionel Sambuc /// \param data Opaque data that belongs to the error, for usage by
5811be35a1SLionel Sambuc /// error-specific methods like format_callback.
5911be35a1SLionel Sambuc /// \param data_size Size of the opaque data object.
6011be35a1SLionel Sambuc /// \param format_callback Type-specific method to generate a user
6111be35a1SLionel Sambuc /// representation of the error.
6211be35a1SLionel Sambuc ///
6311be35a1SLionel Sambuc /// \return True if the initialization succeeds; false otherwise. If
6411be35a1SLionel Sambuc /// false, the error object passed in has not been modified.
6511be35a1SLionel Sambuc static bool
error_init(kyua_error_t const error,const char * const type_name,void * const data,const size_t data_size,const kyua_error_format_callback format_callback)6611be35a1SLionel Sambuc error_init(kyua_error_t const error, const char* const type_name,
6711be35a1SLionel Sambuc void* const data, const size_t data_size,
6811be35a1SLionel Sambuc const kyua_error_format_callback format_callback)
6911be35a1SLionel Sambuc {
7011be35a1SLionel Sambuc assert(data != NULL || data_size == 0);
7111be35a1SLionel Sambuc assert(data_size != 0 || data == NULL);
7211be35a1SLionel Sambuc
7311be35a1SLionel Sambuc bool ok;
7411be35a1SLionel Sambuc
7511be35a1SLionel Sambuc if (data == NULL) {
7611be35a1SLionel Sambuc error->data = NULL;
7711be35a1SLionel Sambuc error->needs_free = false;
7811be35a1SLionel Sambuc ok = true;
7911be35a1SLionel Sambuc } else {
8011be35a1SLionel Sambuc void* new_data = malloc(data_size);
8111be35a1SLionel Sambuc if (new_data == NULL) {
8211be35a1SLionel Sambuc ok = false;
8311be35a1SLionel Sambuc } else {
8411be35a1SLionel Sambuc memcpy(new_data, data, data_size);
8511be35a1SLionel Sambuc error->data = new_data;
8611be35a1SLionel Sambuc ok = true;
8711be35a1SLionel Sambuc }
8811be35a1SLionel Sambuc }
8911be35a1SLionel Sambuc
9011be35a1SLionel Sambuc if (ok) {
9111be35a1SLionel Sambuc error->type_name = type_name;
9211be35a1SLionel Sambuc error->format_callback = (format_callback == NULL) ?
9311be35a1SLionel Sambuc generic_format_callback : format_callback;
9411be35a1SLionel Sambuc }
9511be35a1SLionel Sambuc
9611be35a1SLionel Sambuc return ok;
9711be35a1SLionel Sambuc }
9811be35a1SLionel Sambuc
9911be35a1SLionel Sambuc
10011be35a1SLionel Sambuc /// Allocates and initializes a new error.
10111be35a1SLionel Sambuc ///
10211be35a1SLionel Sambuc /// \param type_name Name of the error type.
10311be35a1SLionel Sambuc /// \param data Opaque data that belongs to the error, for usage by
10411be35a1SLionel Sambuc /// error-specific methods like format_callback.
10511be35a1SLionel Sambuc /// \param data_size Size of the opaque data object.
10611be35a1SLionel Sambuc /// \param format_callback Type-specific method to generate a user
10711be35a1SLionel Sambuc /// representation of the error.
10811be35a1SLionel Sambuc ///
10911be35a1SLionel Sambuc /// \return The newly initialized error, or an out of memory error.
11011be35a1SLionel Sambuc kyua_error_t
kyua_error_new(const char * const type_name,void * const data,const size_t data_size,const kyua_error_format_callback format_callback)11111be35a1SLionel Sambuc kyua_error_new(const char* const type_name, void* const data,
11211be35a1SLionel Sambuc const size_t data_size,
11311be35a1SLionel Sambuc const kyua_error_format_callback format_callback)
11411be35a1SLionel Sambuc {
11511be35a1SLionel Sambuc assert(data != NULL || data_size == 0);
11611be35a1SLionel Sambuc assert(data_size != 0 || data == NULL);
11711be35a1SLionel Sambuc
11811be35a1SLionel Sambuc kyua_error_t error = malloc(sizeof(struct kyua_error));
11911be35a1SLionel Sambuc if (error == NULL)
12011be35a1SLionel Sambuc error = kyua_oom_error_new();
12111be35a1SLionel Sambuc else {
12211be35a1SLionel Sambuc if (!error_init(error, type_name, data, data_size, format_callback)) {
12311be35a1SLionel Sambuc free(error);
12411be35a1SLionel Sambuc error = kyua_oom_error_new();
12511be35a1SLionel Sambuc } else {
12611be35a1SLionel Sambuc error->needs_free = true;
12711be35a1SLionel Sambuc }
12811be35a1SLionel Sambuc }
12911be35a1SLionel Sambuc
13011be35a1SLionel Sambuc assert(error != NULL);
13111be35a1SLionel Sambuc return error;
13211be35a1SLionel Sambuc }
13311be35a1SLionel Sambuc
13411be35a1SLionel Sambuc
13511be35a1SLionel Sambuc /// Releases an error.
13611be35a1SLionel Sambuc ///
13711be35a1SLionel Sambuc /// \param error The error object to release.
13811be35a1SLionel Sambuc void
kyua_error_free(kyua_error_t error)13911be35a1SLionel Sambuc kyua_error_free(kyua_error_t error)
14011be35a1SLionel Sambuc {
14111be35a1SLionel Sambuc assert(error != NULL);
14211be35a1SLionel Sambuc
14311be35a1SLionel Sambuc const bool needs_free = error->needs_free;
14411be35a1SLionel Sambuc
14511be35a1SLionel Sambuc if (error->data != NULL)
14611be35a1SLionel Sambuc free(error->data);
14711be35a1SLionel Sambuc if (needs_free)
14811be35a1SLionel Sambuc free(error);
14911be35a1SLionel Sambuc }
15011be35a1SLionel Sambuc
15111be35a1SLionel Sambuc
15211be35a1SLionel Sambuc /// Returns the "most important" of two errors.
15311be35a1SLionel Sambuc ///
15411be35a1SLionel Sambuc /// "Most important" is defined as: the primary error is returned if set,
15511be35a1SLionel Sambuc /// otherwise the secondary error is returned.
15611be35a1SLionel Sambuc ///
15711be35a1SLionel Sambuc /// It is the responsibility of the caller to free the *resulting* error of this
15811be35a1SLionel Sambuc /// call. The original errors passed in should not be consulted any longer,
15911be35a1SLionel Sambuc /// because it is impossible to know which one was chosen.
16011be35a1SLionel Sambuc ///
16111be35a1SLionel Sambuc /// \param primary The primary error to compare.
16211be35a1SLionel Sambuc /// \param [in,out] secondary The secondary error to compare. This is freed if
16311be35a1SLionel Sambuc /// the primary error is set.
16411be35a1SLionel Sambuc ///
16511be35a1SLionel Sambuc /// \return Either primary or secondary.
16611be35a1SLionel Sambuc kyua_error_t
kyua_error_subsume(kyua_error_t primary,kyua_error_t secondary)16711be35a1SLionel Sambuc kyua_error_subsume(kyua_error_t primary, kyua_error_t secondary)
16811be35a1SLionel Sambuc {
16911be35a1SLionel Sambuc if (kyua_error_is_set(primary)) {
17011be35a1SLionel Sambuc if (kyua_error_is_set(secondary))
17111be35a1SLionel Sambuc kyua_error_free(secondary);
17211be35a1SLionel Sambuc return primary;
17311be35a1SLionel Sambuc } else {
17411be35a1SLionel Sambuc return secondary;
17511be35a1SLionel Sambuc }
17611be35a1SLionel Sambuc }
17711be35a1SLionel Sambuc
17811be35a1SLionel Sambuc
17911be35a1SLionel Sambuc /// Constructor for a no-error condition.
18011be35a1SLionel Sambuc ///
18111be35a1SLionel Sambuc /// \return Opaque representation of a no-error condition.
18211be35a1SLionel Sambuc kyua_error_t
kyua_error_ok(void)18311be35a1SLionel Sambuc kyua_error_ok(void)
18411be35a1SLionel Sambuc {
18511be35a1SLionel Sambuc return NULL;
18611be35a1SLionel Sambuc }
18711be35a1SLionel Sambuc
18811be35a1SLionel Sambuc
18911be35a1SLionel Sambuc /// Checks if the given error object represents an error or not.
19011be35a1SLionel Sambuc ///
19111be35a1SLionel Sambuc /// \param error The error to check.
19211be35a1SLionel Sambuc ///
19311be35a1SLionel Sambuc /// \return True if the error is set.
19411be35a1SLionel Sambuc bool
kyua_error_is_set(const kyua_error_t error)19511be35a1SLionel Sambuc kyua_error_is_set(const kyua_error_t error)
19611be35a1SLionel Sambuc {
19711be35a1SLionel Sambuc return error != NULL;
19811be35a1SLionel Sambuc }
19911be35a1SLionel Sambuc
20011be35a1SLionel Sambuc
20111be35a1SLionel Sambuc /// Checks if the given error object is of a specific type.
20211be35a1SLionel Sambuc ///
20311be35a1SLionel Sambuc /// \pre The error must be set.
20411be35a1SLionel Sambuc ///
20511be35a1SLionel Sambuc /// \param error The error to check.
20611be35a1SLionel Sambuc /// \param type_name The type of the expected error.
20711be35a1SLionel Sambuc ///
20811be35a1SLionel Sambuc /// \return True if the error is of type type_name.
20911be35a1SLionel Sambuc bool
kyua_error_is_type(const kyua_error_t error,const char * type_name)21011be35a1SLionel Sambuc kyua_error_is_type(const kyua_error_t error, const char* type_name)
21111be35a1SLionel Sambuc {
21211be35a1SLionel Sambuc assert(error != NULL);
21311be35a1SLionel Sambuc
21411be35a1SLionel Sambuc return strcmp(error->type_name, type_name) == 0;
21511be35a1SLionel Sambuc }
21611be35a1SLionel Sambuc
21711be35a1SLionel Sambuc
21811be35a1SLionel Sambuc /// Returns a pointer to the error-specific data.
21911be35a1SLionel Sambuc ///
22011be35a1SLionel Sambuc /// \pre The error must be set.
22111be35a1SLionel Sambuc ///
22211be35a1SLionel Sambuc /// \param error The error to query.
22311be35a1SLionel Sambuc ///
22411be35a1SLionel Sambuc /// \return An opaque pointer to the error data. This should only be
22511be35a1SLionel Sambuc /// dereferenced by the methods of the error class that created it.
22611be35a1SLionel Sambuc const void*
kyua_error_data(const kyua_error_t error)22711be35a1SLionel Sambuc kyua_error_data(const kyua_error_t error)
22811be35a1SLionel Sambuc {
22911be35a1SLionel Sambuc assert(error != NULL);
23011be35a1SLionel Sambuc
23111be35a1SLionel Sambuc return error->data;
23211be35a1SLionel Sambuc }
23311be35a1SLionel Sambuc
23411be35a1SLionel Sambuc
23511be35a1SLionel Sambuc /// Generates a user-friendly representation of the error.
23611be35a1SLionel Sambuc ///
23711be35a1SLionel Sambuc /// This cannot fail, but it is possible that the generated error does not
23811be35a1SLionel Sambuc /// fit in the provided buffer.
23911be35a1SLionel Sambuc ///
24011be35a1SLionel Sambuc /// \pre The error must be set.
24111be35a1SLionel Sambuc ///
24211be35a1SLionel Sambuc /// \param error Error for which to generate a message.
24311be35a1SLionel Sambuc /// \param output_buffer Buffer to hold the generated message.
24411be35a1SLionel Sambuc /// \param output_size Length of output_buffer.
24511be35a1SLionel Sambuc ///
24611be35a1SLionel Sambuc /// \return The number of bytes written to output_buffer, or a negative value if
24711be35a1SLionel Sambuc /// there was an error.
24811be35a1SLionel Sambuc int
kyua_error_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)24911be35a1SLionel Sambuc kyua_error_format(const kyua_error_t error, char* const output_buffer,
25011be35a1SLionel Sambuc const size_t output_size)
25111be35a1SLionel Sambuc {
25211be35a1SLionel Sambuc assert(kyua_error_is_set(error));
25311be35a1SLionel Sambuc return error->format_callback(error, output_buffer, output_size);
25411be35a1SLionel Sambuc }
25511be35a1SLionel Sambuc
25611be35a1SLionel Sambuc
25711be35a1SLionel Sambuc /// Formats a string and appends an error code to it.
25811be35a1SLionel Sambuc ///
25911be35a1SLionel Sambuc /// \param error Error to append to the formatted message.
26011be35a1SLionel Sambuc /// \param format User-specified message, as a formatting string.
26111be35a1SLionel Sambuc /// \param ap List of arguments to the format string.
26211be35a1SLionel Sambuc /// \param [out] output_buffer Buffer into which to write the message.
26311be35a1SLionel Sambuc /// \param output_size Length of the output_buffer.
26411be35a1SLionel Sambuc ///
26511be35a1SLionel Sambuc /// \return The number of bytes written to output_buffer, or a negative value if
26611be35a1SLionel Sambuc /// there was an error.
26711be35a1SLionel Sambuc static int
format_user_message(const kyua_error_t error,const char * format,va_list ap,char * const output_buffer,const size_t output_size)26811be35a1SLionel Sambuc format_user_message(const kyua_error_t error, const char* format, va_list ap,
26911be35a1SLionel Sambuc char* const output_buffer, const size_t output_size)
27011be35a1SLionel Sambuc {
27111be35a1SLionel Sambuc assert(kyua_error_is_set(error));
27211be35a1SLionel Sambuc
27311be35a1SLionel Sambuc va_list ap2;
27411be35a1SLionel Sambuc va_copy(ap2, ap);
27511be35a1SLionel Sambuc size_t written = vsnprintf(output_buffer, output_size, format, ap2);
27611be35a1SLionel Sambuc va_end(ap2);
27711be35a1SLionel Sambuc if (written >= output_size)
27811be35a1SLionel Sambuc return -1;
27911be35a1SLionel Sambuc
28011be35a1SLionel Sambuc written += snprintf(output_buffer + written, output_size - written, ": ");
28111be35a1SLionel Sambuc if (written >= output_size)
28211be35a1SLionel Sambuc return -1;
28311be35a1SLionel Sambuc
28411be35a1SLionel Sambuc return kyua_error_format(error, output_buffer + written,
28511be35a1SLionel Sambuc output_size - written);
28611be35a1SLionel Sambuc }
28711be35a1SLionel Sambuc
28811be35a1SLionel Sambuc
28911be35a1SLionel Sambuc /// Version of err(3) that works with kyua_error_t objects.
29011be35a1SLionel Sambuc ///
29111be35a1SLionel Sambuc /// \param exit_code Error code with which to terminate the execution.
29211be35a1SLionel Sambuc /// \param error Error to append to the output.
29311be35a1SLionel Sambuc /// \param format User-specified message, as a formatting string.
29411be35a1SLionel Sambuc /// \param ... Positional arguments to the format string.
29511be35a1SLionel Sambuc ///
29611be35a1SLionel Sambuc /// \post Execution terminates with exit_code.
29711be35a1SLionel Sambuc void
kyua_error_err(const int exit_code,const kyua_error_t error,const char * format,...)29811be35a1SLionel Sambuc kyua_error_err(const int exit_code, const kyua_error_t error,
29911be35a1SLionel Sambuc const char* format, ...)
30011be35a1SLionel Sambuc {
30111be35a1SLionel Sambuc char buffer[2048];
30211be35a1SLionel Sambuc
30311be35a1SLionel Sambuc va_list ap;
30411be35a1SLionel Sambuc va_start(ap, format);
30511be35a1SLionel Sambuc (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
30611be35a1SLionel Sambuc va_end(ap);
30711be35a1SLionel Sambuc kyua_error_free(error);
30811be35a1SLionel Sambuc
30911be35a1SLionel Sambuc errx(exit_code, "%s", buffer);
31011be35a1SLionel Sambuc }
31111be35a1SLionel Sambuc
31211be35a1SLionel Sambuc
31311be35a1SLionel Sambuc /// Writes an error to a file stream.
31411be35a1SLionel Sambuc ///
31511be35a1SLionel Sambuc /// \param stream Stream to which to write the message.
31611be35a1SLionel Sambuc /// \param error Error to append to the output. This is not released.
31711be35a1SLionel Sambuc /// \param format User-specified message, as a formatting string.
31811be35a1SLionel Sambuc /// \param ... Positional arguments to the format string.
31911be35a1SLionel Sambuc void
kyua_error_fprintf(FILE * stream,const kyua_error_t error,const char * format,...)32011be35a1SLionel Sambuc kyua_error_fprintf(FILE* stream, const kyua_error_t error,
32111be35a1SLionel Sambuc const char* format, ...)
32211be35a1SLionel Sambuc {
32311be35a1SLionel Sambuc char buffer[2048];
32411be35a1SLionel Sambuc
32511be35a1SLionel Sambuc va_list ap;
32611be35a1SLionel Sambuc va_start(ap, format);
32711be35a1SLionel Sambuc (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
32811be35a1SLionel Sambuc va_end(ap);
32911be35a1SLionel Sambuc
33011be35a1SLionel Sambuc fprintf(stream, "%s", buffer);
33111be35a1SLionel Sambuc }
33211be35a1SLionel Sambuc
33311be35a1SLionel Sambuc
33411be35a1SLionel Sambuc /// Version of warn(3) that works with kyua_error_t objects.
33511be35a1SLionel Sambuc ///
33611be35a1SLionel Sambuc /// \param error Error to append to the output. This is not released.
33711be35a1SLionel Sambuc /// \param format User-specified message, as a formatting string.
33811be35a1SLionel Sambuc /// \param ... Positional arguments to the format string.
33911be35a1SLionel Sambuc void
kyua_error_warn(const kyua_error_t error,const char * format,...)34011be35a1SLionel Sambuc kyua_error_warn(const kyua_error_t error, const char* format, ...)
34111be35a1SLionel Sambuc {
34211be35a1SLionel Sambuc char buffer[2048];
34311be35a1SLionel Sambuc
34411be35a1SLionel Sambuc va_list ap;
34511be35a1SLionel Sambuc va_start(ap, format);
34611be35a1SLionel Sambuc (void)format_user_message(error, format, ap, buffer, sizeof(buffer));
34711be35a1SLionel Sambuc va_end(ap);
34811be35a1SLionel Sambuc
34911be35a1SLionel Sambuc warnx("%s", buffer);
35011be35a1SLionel Sambuc }
35111be35a1SLionel Sambuc
35211be35a1SLionel Sambuc
35311be35a1SLionel Sambuc /// Name of an generic error type.
35411be35a1SLionel Sambuc const char* const kyua_generic_error_type = "generic";
35511be35a1SLionel Sambuc
35611be35a1SLionel Sambuc
35711be35a1SLionel Sambuc /// Generates a user-friendly representation of the error.
35811be35a1SLionel Sambuc ///
35911be35a1SLionel Sambuc /// \pre The error must be set.
36011be35a1SLionel Sambuc ///
36111be35a1SLionel Sambuc /// \param error Error for which to generate a message.
36211be35a1SLionel Sambuc /// \param output_buffer Buffer to hold the generated message.
36311be35a1SLionel Sambuc /// \param output_size Length of output_buffer.
36411be35a1SLionel Sambuc ///
36511be35a1SLionel Sambuc /// \return The number of bytes written to output_buffer, or a negative value if
36611be35a1SLionel Sambuc /// there was an error.
36711be35a1SLionel Sambuc static int
generic_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)36811be35a1SLionel Sambuc generic_format(const kyua_error_t error, char* const output_buffer,
36911be35a1SLionel Sambuc const size_t output_size)
37011be35a1SLionel Sambuc {
37111be35a1SLionel Sambuc assert(kyua_error_is_type(error, kyua_generic_error_type));
37211be35a1SLionel Sambuc
37311be35a1SLionel Sambuc const char* message = kyua_error_data(error);
37411be35a1SLionel Sambuc return snprintf(output_buffer, output_size, "%s", message);
37511be35a1SLionel Sambuc }
37611be35a1SLionel Sambuc
37711be35a1SLionel Sambuc
37811be35a1SLionel Sambuc /// Constructs a new generic error.
37911be35a1SLionel Sambuc ///
38011be35a1SLionel Sambuc /// \param message Textual description of the problem.
38111be35a1SLionel Sambuc /// \param ... Positional arguments for the description.
38211be35a1SLionel Sambuc ///
38311be35a1SLionel Sambuc /// \return The generated error.
38411be35a1SLionel Sambuc kyua_error_t
kyua_generic_error_new(const char * message,...)38511be35a1SLionel Sambuc kyua_generic_error_new(const char* message, ...)
38611be35a1SLionel Sambuc {
38711be35a1SLionel Sambuc char formatted[1024];
38811be35a1SLionel Sambuc va_list ap;
38911be35a1SLionel Sambuc
39011be35a1SLionel Sambuc va_start(ap, message);
39111be35a1SLionel Sambuc (void)vsnprintf(formatted, sizeof(formatted), message, ap);
39211be35a1SLionel Sambuc va_end(ap);
39311be35a1SLionel Sambuc
39411be35a1SLionel Sambuc return kyua_error_new(kyua_generic_error_type, formatted, sizeof(formatted),
39511be35a1SLionel Sambuc generic_format);
39611be35a1SLionel Sambuc }
39711be35a1SLionel Sambuc
39811be35a1SLionel Sambuc
39911be35a1SLionel Sambuc /// Name of a libc type.
40011be35a1SLionel Sambuc const char* const kyua_libc_error_type = "libc";
40111be35a1SLionel Sambuc
40211be35a1SLionel Sambuc
40311be35a1SLionel Sambuc /// Representation of a libc error.
40411be35a1SLionel Sambuc struct libc_error_data {
40511be35a1SLionel Sambuc /// Value of the errno captured during the error creation.
40611be35a1SLionel Sambuc int original_errno;
40711be35a1SLionel Sambuc
40811be35a1SLionel Sambuc /// Explanation of the problem that lead to the error.
40911be35a1SLionel Sambuc char description[4096];
41011be35a1SLionel Sambuc };
41111be35a1SLionel Sambuc /// Shorthand for a libc_error_data structure.
41211be35a1SLionel Sambuc typedef struct libc_error_data libc_error_data_t;
41311be35a1SLionel Sambuc
41411be35a1SLionel Sambuc
41511be35a1SLionel Sambuc /// Generates a user-friendly representation of the error.
41611be35a1SLionel Sambuc ///
41711be35a1SLionel Sambuc /// \pre The error must be set.
41811be35a1SLionel Sambuc ///
41911be35a1SLionel Sambuc /// \param error Error for which to generate a message.
42011be35a1SLionel Sambuc /// \param output_buffer Buffer to hold the generated message.
42111be35a1SLionel Sambuc /// \param output_size Length of output_buffer.
42211be35a1SLionel Sambuc ///
42311be35a1SLionel Sambuc /// \return The number of bytes written to output_buffer, or a negative value if
42411be35a1SLionel Sambuc /// there was an error.
42511be35a1SLionel Sambuc static int
libc_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)42611be35a1SLionel Sambuc libc_format(const kyua_error_t error, char* const output_buffer,
42711be35a1SLionel Sambuc const size_t output_size)
42811be35a1SLionel Sambuc {
42911be35a1SLionel Sambuc assert(kyua_error_is_type(error, kyua_libc_error_type));
43011be35a1SLionel Sambuc
43111be35a1SLionel Sambuc const libc_error_data_t* data = kyua_error_data(error);
43211be35a1SLionel Sambuc return snprintf(output_buffer, output_size, "%s: %s", data->description,
43311be35a1SLionel Sambuc strerror(data->original_errno));
43411be35a1SLionel Sambuc }
43511be35a1SLionel Sambuc
43611be35a1SLionel Sambuc
43711be35a1SLionel Sambuc /// Constructs a new libc error.
43811be35a1SLionel Sambuc ///
43911be35a1SLionel Sambuc /// \param original_errno libc error code for this error.
44011be35a1SLionel Sambuc /// \param description Textual description of the problem.
44111be35a1SLionel Sambuc /// \param ... Positional arguments for the description.
44211be35a1SLionel Sambuc ///
44311be35a1SLionel Sambuc /// \return The generated error.
44411be35a1SLionel Sambuc kyua_error_t
kyua_libc_error_new(const int original_errno,const char * description,...)44511be35a1SLionel Sambuc kyua_libc_error_new(const int original_errno, const char* description, ...)
44611be35a1SLionel Sambuc {
44711be35a1SLionel Sambuc va_list ap;
44811be35a1SLionel Sambuc
44911be35a1SLionel Sambuc const size_t data_size = sizeof(libc_error_data_t);
45011be35a1SLionel Sambuc libc_error_data_t* data = (libc_error_data_t*)malloc(data_size);
45111be35a1SLionel Sambuc if (data == NULL)
45211be35a1SLionel Sambuc return kyua_oom_error_new();
45311be35a1SLionel Sambuc
45411be35a1SLionel Sambuc data->original_errno = original_errno;
45511be35a1SLionel Sambuc va_start(ap, description);
45611be35a1SLionel Sambuc (void)vsnprintf(data->description, sizeof(data->description),
45711be35a1SLionel Sambuc description, ap);
45811be35a1SLionel Sambuc va_end(ap);
45911be35a1SLionel Sambuc
46011be35a1SLionel Sambuc return kyua_error_new(kyua_libc_error_type, data, data_size, libc_format);
46111be35a1SLionel Sambuc }
46211be35a1SLionel Sambuc
46311be35a1SLionel Sambuc
46411be35a1SLionel Sambuc /// Extracts the original errno of a libc error.
46511be35a1SLionel Sambuc ///
46611be35a1SLionel Sambuc /// \pre error must have been constructed by kyua_libc_error_new.
46711be35a1SLionel Sambuc ///
46811be35a1SLionel Sambuc /// \param error The error object to access.
46911be35a1SLionel Sambuc ///
47011be35a1SLionel Sambuc /// \return The libc error code.
47111be35a1SLionel Sambuc int
kyua_libc_error_errno(const kyua_error_t error)47211be35a1SLionel Sambuc kyua_libc_error_errno(const kyua_error_t error)
47311be35a1SLionel Sambuc {
47411be35a1SLionel Sambuc assert(kyua_error_is_type(error, kyua_libc_error_type));
47511be35a1SLionel Sambuc
47611be35a1SLionel Sambuc const struct libc_error_data* data = kyua_error_data(error);
47711be35a1SLionel Sambuc return data->original_errno;
47811be35a1SLionel Sambuc }
47911be35a1SLionel Sambuc
48011be35a1SLionel Sambuc
48111be35a1SLionel Sambuc /// Name of an OOM type.
48211be35a1SLionel Sambuc const char* const kyua_oom_error_type = "oom";
48311be35a1SLionel Sambuc
48411be35a1SLionel Sambuc
48511be35a1SLionel Sambuc /// Data of an out of memory error.
48611be35a1SLionel Sambuc ///
48711be35a1SLionel Sambuc /// All error types are allocated in dynamic memory. However, doing so for
48811be35a1SLionel Sambuc /// an out of memory error is not possible because, when we are out of
48911be35a1SLionel Sambuc /// memory, we probably cannot allocate more memory to generate an error.
49011be35a1SLionel Sambuc /// Therefore, we just keep a single static instance of the out of memory
49111be35a1SLionel Sambuc /// error around all the time.
49211be35a1SLionel Sambuc static struct kyua_error oom_error;
49311be35a1SLionel Sambuc
49411be35a1SLionel Sambuc
49511be35a1SLionel Sambuc /// Generates a user-friendly representation of the error.
49611be35a1SLionel Sambuc ///
49711be35a1SLionel Sambuc /// \pre The error must be set.
49811be35a1SLionel Sambuc ///
49911be35a1SLionel Sambuc /// \param error Error for which to generate a message.
50011be35a1SLionel Sambuc /// \param output_buffer Buffer to hold the generated message.
50111be35a1SLionel Sambuc /// \param output_size Length of output_buffer.
50211be35a1SLionel Sambuc ///
50311be35a1SLionel Sambuc /// \return The number of bytes written to output_buffer, or a negative value if
50411be35a1SLionel Sambuc /// there was an error.
50511be35a1SLionel Sambuc static int
oom_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)50611be35a1SLionel Sambuc oom_format(const kyua_error_t error, char* const output_buffer,
50711be35a1SLionel Sambuc const size_t output_size)
50811be35a1SLionel Sambuc {
50911be35a1SLionel Sambuc assert(kyua_error_is_type(error, kyua_oom_error_type));
51011be35a1SLionel Sambuc
51111be35a1SLionel Sambuc return snprintf(output_buffer, output_size, "Not enough memory");
51211be35a1SLionel Sambuc }
51311be35a1SLionel Sambuc
51411be35a1SLionel Sambuc
51511be35a1SLionel Sambuc /// Constructs a new out-of-memory error.
51611be35a1SLionel Sambuc ///
51711be35a1SLionel Sambuc /// This will always succeed because we just return a reference to the
51811be35a1SLionel Sambuc /// statically-allocated oom_error.
51911be35a1SLionel Sambuc ///
52011be35a1SLionel Sambuc /// \return An error representing an out of memory condition.
52111be35a1SLionel Sambuc kyua_error_t
kyua_oom_error_new(void)52211be35a1SLionel Sambuc kyua_oom_error_new(void)
52311be35a1SLionel Sambuc {
52411be35a1SLionel Sambuc // This is idempotent; no need to ensure that we call it only once.
525*3260d16fSLionel Sambuc #if defined(__minix) && !defined(NDEBUG)
526*3260d16fSLionel Sambuc const bool ok =
527*3260d16fSLionel Sambuc #endif /* defined(__minix) && !defined(NDEBUG) */
528*3260d16fSLionel Sambuc error_init(&oom_error, kyua_oom_error_type, NULL, 0, oom_format);
52911be35a1SLionel Sambuc assert(ok);
53011be35a1SLionel Sambuc
53111be35a1SLionel Sambuc return &oom_error;
53211be35a1SLionel Sambuc }
53311be35a1SLionel Sambuc
53411be35a1SLionel Sambuc
53511be35a1SLionel Sambuc /// Name of an usage error type.
53611be35a1SLionel Sambuc const char* const kyua_usage_error_type = "usage";
53711be35a1SLionel Sambuc
53811be35a1SLionel Sambuc
53911be35a1SLionel Sambuc /// Generates a user-friendly representation of the error.
54011be35a1SLionel Sambuc ///
54111be35a1SLionel Sambuc /// \pre The error must be set.
54211be35a1SLionel Sambuc ///
54311be35a1SLionel Sambuc /// \param error Error for which to generate a message.
54411be35a1SLionel Sambuc /// \param output_buffer Buffer to hold the generated message.
54511be35a1SLionel Sambuc /// \param output_size Length of output_buffer.
54611be35a1SLionel Sambuc ///
54711be35a1SLionel Sambuc /// \return The number of bytes written to output_buffer, or a negative value if
54811be35a1SLionel Sambuc /// there was an error.
54911be35a1SLionel Sambuc static int
usage_format(const kyua_error_t error,char * const output_buffer,const size_t output_size)55011be35a1SLionel Sambuc usage_format(const kyua_error_t error, char* const output_buffer,
55111be35a1SLionel Sambuc const size_t output_size)
55211be35a1SLionel Sambuc {
55311be35a1SLionel Sambuc assert(kyua_error_is_type(error, kyua_usage_error_type));
55411be35a1SLionel Sambuc
55511be35a1SLionel Sambuc const char* message = kyua_error_data(error);
55611be35a1SLionel Sambuc return snprintf(output_buffer, output_size, "%s", message);
55711be35a1SLionel Sambuc }
55811be35a1SLionel Sambuc
55911be35a1SLionel Sambuc
56011be35a1SLionel Sambuc /// Constructs a new usage error.
56111be35a1SLionel Sambuc ///
56211be35a1SLionel Sambuc /// \param message Textual description of the problem.
56311be35a1SLionel Sambuc /// \param ... Positional arguments for the description.
56411be35a1SLionel Sambuc ///
56511be35a1SLionel Sambuc /// \return The generated error.
56611be35a1SLionel Sambuc kyua_error_t
kyua_usage_error_new(const char * message,...)56711be35a1SLionel Sambuc kyua_usage_error_new(const char* message, ...)
56811be35a1SLionel Sambuc {
56911be35a1SLionel Sambuc char formatted[1024];
57011be35a1SLionel Sambuc va_list ap;
57111be35a1SLionel Sambuc
57211be35a1SLionel Sambuc va_start(ap, message);
57311be35a1SLionel Sambuc (void)vsnprintf(formatted, sizeof(formatted), message, ap);
57411be35a1SLionel Sambuc va_end(ap);
57511be35a1SLionel Sambuc
57611be35a1SLionel Sambuc return kyua_error_new(kyua_usage_error_type, formatted, sizeof(formatted),
57711be35a1SLionel Sambuc usage_format);
57811be35a1SLionel Sambuc }
579