aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore10
-rw-r--r--LICENSE15
-rw-r--r--Makefile75
-rw-r--r--config.mk6
-rw-r--r--copy_error.c31
-rw-r--r--free_error.c15
-rw-r--r--get_error.c11
-rw-r--r--internal.c6
-rw-r--r--internal.h34
-rw-r--r--liberror.h179
-rw-r--r--linux.mk5
-rw-r--r--macos.mk5
-rw-r--r--print_backtrace.c11
-rw-r--r--print_error.c68
-rw-r--r--reset_error.c10
-rw-r--r--save_backtrace.c10
-rw-r--r--set_error.c33
-rw-r--r--set_error_errno.c9
18 files changed, 533 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..983233b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*\#*
+*~
+*.o
+*.a
+*.lo
+*.so
+*.so.*
+*.su
+*.dll
+*.dylib
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c437151
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2019 Mattias Andrée <maandree@kth.se>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f2157ad
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,75 @@
+.POSIX:
+
+CONFIGFILE = config.mk
+include $(CONFIGFILE)
+
+OS = linux
+# linux = Linux
+# macos = Mac OS
+include $(OS).mk
+
+LIB_MAJOR = 1
+LIB_MINOR = 0
+LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR)
+
+
+HDR =\
+ liberror.h\
+ internal.h
+
+OBJ =\
+ copy_error.o\
+ free_error.o\
+ get_error.o\
+ internal.o\
+ print_backtrace.o\
+ print_error.o\
+ reset_error.o\
+ save_backtrace.o\
+ set_error.o\
+ set_error_errno.o
+
+LOBJ = $(OBJ:.o=.lo)
+
+
+all: liberror.a liberror.$(LIBEXT)
+$(OBJ): $(@:.o=.c) $(HDR)
+$(LOBJ): $(@:.lo=.c) $(HDR)
+
+liberror.a: $(OBJ)
+ -rm -f -- $@
+ $(AR) rc $@ $(OBJ)
+ $(AR) s $@
+
+liberror.$(LIBEXT): $(LOBJ)
+ $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS)
+
+.c.o:
+ $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+
+.c.lo:
+ $(CC) -c -o $@ $< -fPIC $(CFLAGS) $(CPPFLAGS)
+
+install: liberror.a liberror.$(LIBEXT)
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/lib"
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/include"
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/licenses/liberror"
+ cp -- liberror.a "$(DESTDIR)$(PREFIX)/lib"
+ cp -- liberror.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/liberror.$(LIBMINOREXT)"
+ ln -sf -- liberror.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/liberror.$(LIBMAJOREXT)"
+ ln -sf -- liberror.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/liberror.$(LIBEXT)"
+ cp -- liberror.h "$(DESTDIR)$(PREFIX)/include"
+ cp -- LICENSE "$(DESTDIR)$(PREFIX)/licenses/liberror"
+
+uninstall:
+ -rm -f -- "$(DESTDIR)$(PREFIX)/lib/liberror."*
+ -rm -f -- "$(DESTDIR)$(PREFIX)/include/liberror.h"
+ -rm -rf -- "$(DESTDIR)$(PREFIX)/licenses/liberror"
+
+clean:
+ -rm -f -- *.o *.lo *.a *.so *.so.* *.su *.test
+
+.SUFFIXES:
+.SUFFIXES: .c .o .lo .a
+
+.PHONY: all install uninstall clean
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..fdfd458
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,6 @@
+PREFIX = /usr
+MANPREFIX = $(PREFIX)/share/man
+
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700
+CFLAGS = -std=c11 -Wall -pedantic
+LDFLAGS = -s
diff --git a/copy_error.c b/copy_error.c
new file mode 100644
index 0000000..8d93131
--- /dev/null
+++ b/copy_error.c
@@ -0,0 +1,31 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+struct liberror_error *
+liberror_copy_error(struct liberror_error *src)
+{
+ struct liberror_error *dest;
+ int saved_errno;
+ if (!src)
+ return NULL;
+ saved_errno = errno;
+ dest = malloc(sizeof(*dest));
+ if (dest) {
+ memcpy(dest, src, sizeof(*src));
+ if (dest->backtrace)
+ dest->backtrace->refcount += 1;
+ dest->dynamically_allocated = 1;
+ if (src->cause) {
+ dest->cause = liberror_copy_error(src->cause);
+ if (!dest->cause) {
+ if (dest->backtrace)
+ dest->backtrace->refcount -= 1;
+ free(dest);
+ dest = NULL;
+ }
+ }
+ }
+ errno = saved_errno;
+ return dest;
+}
diff --git a/free_error.c b/free_error.c
new file mode 100644
index 0000000..a15d36c
--- /dev/null
+++ b/free_error.c
@@ -0,0 +1,15 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+void
+liberror_free_error(struct liberror_error *error)
+{
+ if (!error)
+ return;
+ if (error->backtrace && !--error->backtrace->refcount)
+ free(error->backtrace);
+ liberror_free_error(error->cause);
+ if (error->dynamically_allocated)
+ free(error);
+}
diff --git a/get_error.c b/get_error.c
new file mode 100644
index 0000000..4ee3dbc
--- /dev/null
+++ b/get_error.c
@@ -0,0 +1,11 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+struct liberror_error *
+liberror_get_error(void)
+{
+ if (liberror_have_error_)
+ return &liberror_error_;
+ return NULL;
+}
diff --git a/internal.c b/internal.c
new file mode 100644
index 0000000..034a2db
--- /dev/null
+++ b/internal.c
@@ -0,0 +1,6 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+_Thread_local struct liberror_error liberror_error_;
+_Thread_local int liberror_have_error_;
diff --git a/internal.h b/internal.h
new file mode 100644
index 0000000..a77aa43
--- /dev/null
+++ b/internal.h
@@ -0,0 +1,34 @@
+/* See LICENSE file for copyright and license details. */
+#include "liberror.h"
+
+#include <alloca.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#if defined(__GNUC__)
+# define GCC_ATTRIBUTES(...) __attribute__((__VA_ARGS__))
+#else
+# define GCC_ATTRIBUTES(...)
+#endif
+
+#define HIDDEN GCC_ATTRIBUTES(__visibility__("hidden"))
+#define WEAK GCC_ATTRIBUTES(__weak__)
+
+
+struct liberror_backtrace {
+ size_t refcount;
+ size_t n;
+ uintptr_t rips[];
+};
+
+
+HIDDEN extern _Thread_local struct liberror_error liberror_error_;
+HIDDEN extern _Thread_local int liberror_have_error_;
+
+
+int liberror_save_backtrace_(struct liberror_error *);
diff --git a/liberror.h b/liberror.h
new file mode 100644
index 0000000..27c700d
--- /dev/null
+++ b/liberror.h
@@ -0,0 +1,179 @@
+/* See LICENSE file for copyright and license details. */
+#ifndef LIBERROR_H
+#define LIBERROR_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+
+/**
+ * Opaque backtrace structure
+ */
+struct liberror_backtrace;
+
+/**
+ * Error structure
+ */
+struct liberror_error {
+ /**
+ * Backtrace for the error, `NULL` if the it could
+ * not be allocated or if the program is not linked
+ * with `-lerror-backtrace`
+ */
+ struct liberror_backtrace *backtrace;
+
+ /**
+ * Description of the error
+ */
+ char description[256];
+
+ /**
+ * The function that failed
+ */
+ char source[64];
+
+ /**
+ * Name of error code group, for normal `errno`
+ * errors this is "error", for getaddrinfo(3) errors
+ * this is "addrinfo", for custom errors it is the
+ * name of the library or application
+ */
+ char code_group[64];
+
+ /**
+ * The error code
+ */
+ long long int code;
+
+ /**
+ * The error that caused this error, `NULL` if
+ * none or it could not be allocated (if and only
+ * if so, `.failed_to_allocate_cause` will be set
+ * to a non-zero value, specifically 1)
+ */
+ struct liberror_error *cause;
+
+ /**
+ * Whether allocation of `.cause` failed
+ */
+ int failed_to_allocate_cause;
+
+ /**
+ * Whether the error is physically allocated
+ */
+ int dynamically_allocated;
+};
+
+
+/**
+ * Get the current error for the thread
+ *
+ * @return The current error, `NULL` if none
+ */
+struct liberror_error *liberror_get_error(void);
+
+/**
+ * Create a copy of an error
+ *
+ * This function will only fail of enough memory
+ * cannot be allocated, however `errno` will not
+ * be changed
+ *
+ * @param error The error to copy
+ * @return Copy of the error, `NULL` on failure
+ */
+struct liberror_error *liberror_copy_error(struct liberror_error *);
+
+/**
+ * Deallocate and error and the error stored as
+ * its cause (recursively)
+ *
+ * @param error The error to deallocate
+ */
+void liberror_free_error(struct liberror_error *);
+
+/**
+ * Deallocate the current error for the thread
+ * and reset the error for the thread
+ *
+ * This function shall be called after handling
+ * the error
+ */
+void liberror_reset_error(void);
+
+/**
+ * Print the backtrace of an error
+ *
+ * If the backtrace could not be allocated,
+ * nothing will be printed
+ *
+ * This function will never change `errno`
+ *
+ * Note: this library does not actually save
+ * a backtrace, `-lerror-backtrace` is needed
+ * for that functionallity (it will replace
+ * some things in this library, so no other
+ * action is required)
+ *
+ * @param error The error
+ * @param fp The file to print the backtrace to
+ * @param indent Text to print at the beginning of each line
+ */
+void liberror_print_backtrace(struct liberror_error *, FILE *, const char *);
+
+/**
+ * Set the current error for the thread
+ *
+ * If the thread already has an error saved,
+ * it will be stored as the cause of the new
+ * error
+ *
+ * @param description Description of the error, empty for default description
+ * @param source The function that failed
+ * @param code_group Name of error code group, for normal `errno` errors
+ * this shall be "error", for getaddrinfo(3) errors
+ * this shall be "addrinfo", for custom errors it shall
+ * be the name of the library or application
+ * @param code The error code
+ */
+void liberror_set_error(const char[256], const char[64], const char[64], long long int);
+
+/**
+ * Set the current error for the thread
+ *
+ * This function can be used as an alternative
+ * to `liberror_set_error` for `errno` errors
+ *
+ * If the thread already has an error saved,
+ * it will be stored as the cause of the new
+ * error
+ *
+ * @param description Description of the error, empty for default description
+ * @param source The function that failed
+ * @param code The `errno` value
+ */
+void liberror_set_error_errno(const char[256], const char[64], int);
+
+/**
+ * The an error, its backtrace, and its
+ * cause (recursively)
+ *
+ * If `error` is `NULL` and the thread does
+ * not have any error set, this function
+ * will not do anything
+ *
+ * @param error The error, the thread's current error if `NULL`
+ * @param fp Output stream, standard error if `NULL`
+ * @param reset Whether `error` shall be deallocated, `error`
+ * is null and `reset` is non-zero, the thread's
+ * error will be reset
+ * @param prefix Prefix for each printed line, ": " will be
+ * appended to this prefix; if `NULL` or empty,
+ * no prefix is used; this should normally be
+ * `argv[0]` from the main() function
+ */
+void liberror_print_error(struct liberror_error *, FILE *, int, const char *);
+
+
+#endif
diff --git a/linux.mk b/linux.mk
new file mode 100644
index 0000000..c9f74a0
--- /dev/null
+++ b/linux.mk
@@ -0,0 +1,5 @@
+LIBEXT = so
+LIBFLAGS = -shared -Wl,-soname,libkeccak.$(LIBEXT).$(LIB_MAJOR)
+
+LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR)
+LIBMINOREXT = $(LIBEXT).$(LIB_VERSION)
diff --git a/macos.mk b/macos.mk
new file mode 100644
index 0000000..b475197
--- /dev/null
+++ b/macos.mk
@@ -0,0 +1,5 @@
+LIBEXT = dylib
+LIBFLAGS = -dynamiclib
+
+LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT)
+LIBMINOREXT = $(LIB_VERSION).$(LIBEXT)
diff --git a/print_backtrace.c b/print_backtrace.c
new file mode 100644
index 0000000..d69dafd
--- /dev/null
+++ b/print_backtrace.c
@@ -0,0 +1,11 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+WEAK void
+liberror_print_backtrace(struct liberror_error *error, FILE *fp, const char *indent)
+{
+ (void) error;
+ (void) fp;
+ (void) indent;
+}
diff --git a/print_error.c b/print_error.c
new file mode 100644
index 0000000..4675bab
--- /dev/null
+++ b/print_error.c
@@ -0,0 +1,68 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+static void
+print_error_description(struct liberror_error *error, FILE *fp, const char *prefix)
+{
+ if (*error->description) {
+ if (*error->source)
+ fprintf(fp, "%sError in function %s: %s\n", prefix, error->source, error->description);
+ else
+ fprintf(fp, "%sError: %s\n", prefix, error->description);
+ } else if (*error->source) {
+ fprintf(fp, "%sError in function %s: %s error %lli\n", prefix, error->source, error->code_group, error->code);
+ } else {
+ fprintf(fp, "%sError: %s error %lli\n", prefix, error->code_group, error->code);
+ }
+}
+
+
+void
+liberror_print_error(struct liberror_error *error, FILE *fp, int reset, const char *prefix_)
+{
+ struct liberror_error *err = error;
+ char *prefix = (char []){" "};
+ char *p, *q;
+
+ if (!err) {
+ err = liberror_get_error();
+ if (!err)
+ return;
+ }
+
+ if (prefix_ && *prefix_) {
+ prefix = alloca(strlen(prefix) + sizeof(": "));
+ stpcpy(q = stpcpy(p = stpcpy(stpcpy(prefix, prefix_), ": "), " "), " ");
+ } else {
+ p = &prefix[0];
+ q = &prefix[2];
+ }
+
+ if (!fp)
+ fp = stderr;
+
+ *p = *q = '\0';
+ print_error_description(err, fp, prefix);
+
+ *p = ' ';
+ liberror_print_backtrace(err, fp, prefix);
+
+ while ((err = err->cause)) {
+ *p = *q = '\0';
+ fprintf(fp, "%sCaused by:\n", prefix);
+
+ *p = ' ';
+ print_error_description(err, fp, prefix);
+
+ *q = ' ';
+ liberror_print_backtrace(err, fp, prefix);
+ }
+
+ if (reset) {
+ if (error)
+ liberror_free_error(error);
+ else
+ liberror_reset_error();
+ }
+}
diff --git a/reset_error.c b/reset_error.c
new file mode 100644
index 0000000..502d149
--- /dev/null
+++ b/reset_error.c
@@ -0,0 +1,10 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+void
+liberror_reset_error(void)
+{
+ liberror_free_error(liberror_get_error());
+ liberror_have_error_ = 0;
+}
diff --git a/save_backtrace.c b/save_backtrace.c
new file mode 100644
index 0000000..7df2fb5
--- /dev/null
+++ b/save_backtrace.c
@@ -0,0 +1,10 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+WEAK int
+liberror_save_backtrace(struct liberror_error *error)
+{
+ (void) error;
+ return 0;
+}
diff --git a/set_error.c b/set_error.c
new file mode 100644
index 0000000..5ee54ce
--- /dev/null
+++ b/set_error.c
@@ -0,0 +1,33 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+void
+liberror_set_error(const char description[256], const char source[64], const char code_group[64], long long int code)
+{
+ struct liberror_error *cause;
+ struct liberror_error *error;
+ int have_cause;
+
+ cause = liberror_get_error();
+ have_cause = !!cause;
+ cause = liberror_copy_error(cause);
+
+ error = &liberror_error_;
+ liberror_have_error_ = 1;
+
+ memset(error, 0, sizeof(*error));
+
+ if (*description) {
+ stpcpy(error->description, description);
+ } else if (!strcmp(code_group, "errno")) {
+ if (code >= (long long int)INT_MIN && code <= (long long int)INT_MAX)
+ strerror_r((int)code, error->description, sizeof(error->description));
+ }
+ stpcpy(error->source, source);
+ stpcpy(error->code_group, code_group);
+ error->code = code;
+ liberror_save_backtrace_(error);
+ error->cause = cause;
+ error->failed_to_allocate_cause = have_cause && !cause;
+}
diff --git a/set_error_errno.c b/set_error_errno.c
new file mode 100644
index 0000000..1b8347a
--- /dev/null
+++ b/set_error_errno.c
@@ -0,0 +1,9 @@
+/* See LICENSE file for copyright and license details. */
+#include "internal.h"
+
+
+void
+liberror_set_error_errno(const char description[256], const char source[64], int code)
+{
+ liberror_set_error(description, source, "errno", (long long int)code);
+}