Mercurial > libjeffpc
changeset 632:dfbcf2a05902
error: allow hooking xstrerror errno mapping
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Mon, 05 Nov 2018 18:36:30 -0500 |
parents | 744982e99bbc |
children | 912549732d42 |
files | error.c include/jeffpc/jeffpc.h init.c tests/CMakeLists.txt tests/test_xstrerror.c |
diffstat | 5 files changed, 165 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/error.c Thu Nov 08 22:52:40 2018 -0500 +++ b/error.c Mon Nov 05 18:36:30 2018 -0500 @@ -303,6 +303,14 @@ const char *xstrerror(int e) { + if (libops.strerror) { + const char *ret; + + ret = libops.strerror(e); + if (ret) + return ret; + } + switch (e) { case 0: return "Success";
--- a/include/jeffpc/jeffpc.h Thu Nov 08 22:52:40 2018 -0500 +++ b/include/jeffpc/jeffpc.h Mon Nov 05 18:36:30 2018 -0500 @@ -32,6 +32,7 @@ void (*assfail3)(const char *a, uintmax_t lv, const char *op, uintmax_t rv, const char *f, int l, const char *fxn); const char *(*get_session)(void); + const char *(*strerror)(int); }; extern void jeffpc_init(struct jeffpc_ops *ops);
--- a/init.c Thu Nov 08 22:52:40 2018 -0500 +++ b/init.c Mon Nov 05 18:36:30 2018 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * Copyright (c) 2016-2018 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -55,4 +55,5 @@ SET_OP(assfail, default_assfail); SET_OP(assfail3, default_assfail3); SET_OP(get_session, NULL); + SET_OP(strerror, NULL); }
--- a/tests/CMakeLists.txt Thu Nov 08 22:52:40 2018 -0500 +++ b/tests/CMakeLists.txt Mon Nov 05 18:36:30 2018 -0500 @@ -64,3 +64,4 @@ build_test_bin_and_run(utf8-to-utf32) build_test_bin_and_run(uuid) build_test_bin_and_run(version) +build_test_bin_and_run(xstrerror)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_xstrerror.c Mon Nov 05 18:36:30 2018 -0500 @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2018 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <stdbool.h> + +#include <jeffpc/error.h> + +#include "test.c" + +/* + * The callback can either return a string (which gets propagated to the + * xstrerror caller) or return NULL (which lets generic xstrerror code + * handle the errno). We want to test both behaviors. If this variable is + * set to true, our callback function returns NULL. Otherwise, it returns a + * mangled int (see below). + */ +static bool return_null; + +/* + * Essentially, we want to make it obvious if the test failed because + * xstrerror returned: + * + * - NULL (bad) + * - a negated errno (bad) + * - a real string (bad) + * - a real-looking pointer (good) + * + * The easiest way to do that is to "reserve" a unique pointer for each + * possible return string. + */ +static char error_ptrs[MAX_ERRNO + 1]; + +static inline void *mangle_int(int i) +{ + ASSERT3S(i, <=, 0); + ASSERT3S(i, >=, -MAX_ERRNO); + + return &error_ptrs[-i]; +} + +static inline bool is_mangled(const char *ptr) +{ + return (ptr >= &error_ptrs[0]) && (ptr <= &error_ptrs[MAX_ERRNO]); +} + +static const char *mystrerror(int e) +{ + fprintf(stderr, "called with %d...", e); + + return return_null ? NULL : mangle_int(e); +} + +static void __check_err(int e, const char *got) +{ + if (return_null) { + if (got == NULL) + fail("expected a string, got NULL"); + if (IS_ERR(got)) + fail("expected a string, got errno %d (%p)", + PTR_ERR(got), got); + if (is_mangled(got)) + fail("expected a string, got mangled ptr"); + + /* + * It'd be nice if we could check that the string is sane, + * but we don't know what exact string to expect. + */ + } else { + void *exp = mangle_int(e); + + if (got != exp) + fail("expected %p, got %p", exp, got); + } +} + +static void __check_ok(int e, const char *got) +{ + if (return_null) { + if (got == NULL) + fail("expected 'Success', got NULL"); + if (IS_ERR(got)) + fail("expected 'Success', got errno %d (%p)", + PTR_ERR(got), got); + if (strcmp(got, "Success")) + fail("expected 'Success', got '%s'", got); + } else { + void *exp = mangle_int(0); + + if (got != exp) + fail("expected %p, got %p", exp, got); + } +} + +static void __test(int e, void (*check)(int, const char *)) +{ + const char *got; + + fprintf(stderr, "%5d %s...", e, return_null ? "null" : "mangled"); + + got = xstrerror(e); + fprintf(stderr, "returned %p...", got); + + check(e, got); + + fprintf(stderr, "ok.\n"); +} + +static void do_test(void) +{ + int i; + + /* test errors */ + for (i = -MAX_ERRNO + 1; i < 0; i++) + __test(i, __check_err); + + /* test success */ + __test(0, __check_ok); +} + +void test(void) +{ + struct jeffpc_ops init_ops = { + .strerror = mystrerror, + }; + + /* override the ops set by the generic code */ + jeffpc_init(&init_ops); + + return_null = false; + do_test(); + + return_null = true; + do_test(); +}