From 644130802a51d91b90e85a0769f642c3e0203aef Mon Sep 17 00:00:00 2001 From: Mosder Date: Wed, 16 Sep 2026 23:47:49 +0200 Subject: feat: added a simple compatibbility layer for functions not from C99 standard --- Makefile | 27 ++++++++++++++++++++------- compat/compat.c | 31 +++++++++++++++++++++++++++++++ compat/compat.h | 21 +++++++++++++++++++++ include/moslib/tester.h | 7 ++++--- src/string.c | 3 ++- src/tester.c | 46 +++++++++++++++++++--------------------------- tests/ds/hashmap_tests.c | 3 ++- tests/string_tests.c | 5 +++-- 8 files changed, 102 insertions(+), 41 deletions(-) create mode 100644 compat/compat.c create mode 100644 compat/compat.h diff --git a/Makefile b/Makefile index d9775c1..0739c71 100644 --- a/Makefile +++ b/Makefile @@ -13,17 +13,30 @@ LIB_SUDO:=$(shell test -w $(LIB_DIR) || echo sudo) INC_DIR=$(TARGET_PREFIX)/include INC_SUDO:=$(shell test -w $(INC_DIR) || echo sudo) -CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -fPIC -I./include -O2 -c -SRCS:=$(shell find src | grep "\.c$$") -CLEANUP=$(MAKE) clean +CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -fPIC -I./include -I./compat -O2 -c +SRCS:=$(shell find src compat | grep "\.c$$") +CLEANUP=$(MAKE) clean --no-print-directory -TESTS_CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -I$(INC_DIR) -Og -ggdb -c +TESTS_CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -I$(INC_DIR) -I./compat -Og -ggdb -c TESTS_LDFLAGS=-L$(LIB_DIR) -Wl,-rpath,$(LIB_DIR) -lmoslib -TESTS_SRCS:=$(shell find tests | grep "\.c$$") +TESTS_SRCS:=$(shell find tests compat | grep "\.c$$") + +define probe_fun + $(shell printf '#define $(1)\n#include <$(2)>\nint main(void){$(3);}' \ + | $(CC) $(CFLAGS) -x c - -o /dev/null 2>/dev/null \ + || echo -DNO_$(shell echo $(3) | tr a-z A-Z)) +endef + +PROBED:= \ + $(call probe_fun,_GNU_SOURCE,string.h,memmem) \ + $(call probe_fun,_XOPEN_SOURCE 500,string.h,strdup) + +CFLAGS+=$(PROBED) +TESTS_CFLAGS+=$(PROBED) both: CLEANUP=true both: clean shared static - @$(MAKE) clean + @$(MAKE) clean --no-print-directory shared: clean compile headers $(LIB_SUDO) $(CC) -shared *.o -o $(LIB_DIR)/libmoslib.so @@ -45,7 +58,7 @@ test: clean $(CC) $(TESTS_CFLAGS) $(TESTS_SRCS) $(CC) $(TESTS_LDFLAGS) *.o -o run_tests ./run_tests - @$(MAKE) clean + @$(CLEANUP) clean: rm *.o run_tests 2>/dev/null || true diff --git a/compat/compat.c b/compat/compat.c new file mode 100644 index 0000000..1112146 --- /dev/null +++ b/compat/compat.c @@ -0,0 +1,31 @@ +#include "compat.h" + +#ifdef NO_STRDUP +#include "moslib/safe.h" +#include +char *strdup(const char *s) { + char *s2 = mos_safe_malloc(strlen(s) + 1); + strcpy(s2, s); + return s2; +} +#endif // NO_STRDUP + +#ifdef NO_MEMMEM +void *memmem(const void *h, size_t h_size, const void *n, size_t n_size) { + if (n_size == 0) + return (void *)h; + if (n_size > h_size) + return NULL; + + const unsigned char *p = h; + const unsigned char *end = p + h_size - n_size + 1; + unsigned char c = *(unsigned char *)n; + while ((p = memchr(p, c, end - p))) { + if (memcmp(p, n, n_size) == 0) + return (void *)p; + p++; + } + + return NULL; +} +#endif // NO_MEMMEM diff --git a/compat/compat.h b/compat/compat.h new file mode 100644 index 0000000..b36b17f --- /dev/null +++ b/compat/compat.h @@ -0,0 +1,21 @@ +// compat.h +// Compatibility layer for functions not in standard C99 + +#ifndef MOSLIB_COMPAT_H +#define MOSLIB_COMPAT_H + +#define _XOPEN_SOURCE 500 +#define _GNU_SOURCE + +typedef void declaration; + +#ifdef NO_STRDUP +extern char *strdup(const char *s); +#endif // NO_STRDUP + +#ifdef NO_MEMMEM +#include +extern void *memmem(const void *h, size_t h_size, const void *n, size_t n_size); +#endif // NO_MEMMEM + +#endif // MOSLIB_COMPAT_H diff --git a/include/moslib/tester.h b/include/moslib/tester.h index 9f3b482..4c77d73 100644 --- a/include/moslib/tester.h +++ b/include/moslib/tester.h @@ -174,14 +174,14 @@ extern void mos_test_assert(int expression, const char *fail_message); // output // expected output of the function #define mos_test_assert_out(function, stream, output) \ - mos_test_assert_out_fn(function, stream, output, #function, #stream) + mos_test_assert_out_fn(function, &(stream), output, #function, #stream) // Suppress given output // // Arguments: // out // outpout to suppress -extern void mos_suppress_output(FILE *out); +#define mos_suppress_output(out) mos_suppress_output_fn(&(out)) // Unuppress previously suppressed outputs // Will unsupress all of the outputs that were suppressed @@ -219,11 +219,12 @@ extern MosTestGroup *mos_add_test_group_fn(MosTester *tester, const char *group_ extern void mos_test_assert_exit_fn(MosTestFn function, int code, const char *name); extern void mos_test_assert_out_fn( MosTestFn function, - FILE *stream, + FILE **stream, const char *output, const char *fn_name, const char *stream_name ); +extern void mos_suppress_output_fn(FILE **out); extern void mos_add_test_fn(MosTestGroup *group, MosTestFn test, const char *name); #endif // MOSLIB_TESTER_H diff --git a/src/string.c b/src/string.c index c744578..6625d8f 100644 --- a/src/string.c +++ b/src/string.c @@ -1,4 +1,5 @@ -#define _GNU_SOURCE +#include "compat.h" + #define MOS_FORCE_PREFIXES #include "moslib/string.h" diff --git a/src/tester.c b/src/tester.c index 42f0486..9c1c27c 100644 --- a/src/tester.c +++ b/src/tester.c @@ -1,4 +1,3 @@ -#define _XOPEN_SOURCE 600 #define MOS_FORCE_PREFIXES #include "moslib/tester.h" @@ -24,8 +23,8 @@ static int failed = 0; static FILE *dev_null = NULL; typedef struct { - int *outs; - int *ogs; + FILE ***outs; + FILE **ogs; size_t n_outs; size_t out_cap; } Outputs; @@ -132,7 +131,7 @@ void mos_test_assert_exit_fn(MosTestFn function, int code, const char *name) { void mos_test_assert_out_fn( MosTestFn function, - FILE *stream, + FILE **stream, const char *output, const char *fn_name, const char *stream_name @@ -140,21 +139,16 @@ void mos_test_assert_out_fn( size_t n = strlen(output); char *out = mos_safe_calloc(n + 2, 1); - FILE *tmp = mos_safe_tmpfile(); - - int fd = fileno(stream); - int og = mos_safe_dup(fd); - fflush(stream); - mos_safe_dup2(fileno(tmp), fd); + FILE *og = *stream; + fflush(*stream); + *stream = mos_safe_tmpfile(); function(); - fflush(stream); - rewind(tmp); - fread(out, 1, n + 1, tmp); - fclose(tmp); - - mos_safe_dup2(og, fd); - close(og); + fflush(*stream); + rewind(*stream); + fread(out, 1, n + 1, *stream); + fclose(*stream); + *stream = og; if (strcmp(out, output)) { failed = 1; @@ -166,21 +160,21 @@ void mos_test_assert_out_fn( free(out); } -void mos_suppress_output(FILE *out) { +void mos_suppress_output_fn(FILE **out) { if (!suppressed.outs || suppressed.n_outs >= suppressed.out_cap) { while (suppressed.n_outs >= suppressed.out_cap) suppressed.out_cap *= 2; suppressed.outs = mos_safe_realloc(suppressed.outs, suppressed.out_cap * sizeof(int)); suppressed.ogs = mos_safe_realloc(suppressed.ogs, suppressed.out_cap * sizeof(int)); } - suppressed.outs[suppressed.n_outs] = fileno(out); - suppressed.ogs[suppressed.n_outs++] = dup(fileno(out)); + suppressed.outs[suppressed.n_outs] = out; + suppressed.ogs[suppressed.n_outs++] = *out; if (!dev_null) - dev_null = mos_safe_fopen("/dev/null", "w"); + dev_null = mos_safe_tmpfile(); - fflush(out); - dup2(fileno(dev_null), fileno(out)); + fflush(*out); + *out = dev_null; } void mos_unsuppress_outputs(void) { @@ -190,10 +184,8 @@ void mos_unsuppress_outputs(void) { if (dev_null) fflush(dev_null); - for (size_t i = suppressed.n_outs; i-- > 0;) { - dup2(suppressed.ogs[i], suppressed.outs[i]); - close(suppressed.ogs[i]); - } + for (size_t i = suppressed.n_outs; i-- > 0;) + *suppressed.outs = suppressed.ogs; suppressed.n_outs = 0; } diff --git a/tests/ds/hashmap_tests.c b/tests/ds/hashmap_tests.c index 268bc29..822ee2a 100644 --- a/tests/ds/hashmap_tests.c +++ b/tests/ds/hashmap_tests.c @@ -1,4 +1,5 @@ -#define _GNU_SOURCE +#include "compat.h" + #define MOS_HM_LOAD_FACTOR 100 #include "hashmap_tests.h" #include "moslib/ds/hashmap.h" diff --git a/tests/string_tests.c b/tests/string_tests.c index f600400..eaa9498 100644 --- a/tests/string_tests.c +++ b/tests/string_tests.c @@ -1,6 +1,7 @@ -#define _XOPEN_SOURCE 600 -#include "string_tests.h" +#include "compat.h" + #include "moslib/string.h" +#include "string_tests.h" #include #include -- cgit v1.2.3