← Back to davo.co
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Faulkner <[email protected]>2026-08-07 23:40:47 -0500
committerDavid Faulkner <[email protected]>2026-08-07 23:40:47 -0500
commitb3e9e62599532050fc776c5e8f076915b56c2235 (patch)
treeaf252346106a61b18cc6fc6fdbd32e962d096f1c
Import official C23 code examples for Modern C (Jens Gustedt, 2024)HEADupstream-importmain
- Add official C source files, Makefile, c23-fallback.h, and LICENSE - Update README.md with study mirror notice
-rw-r--r--AUTHORS1
-rw-r--r--B9-detach.c234
-rw-r--r--B9.c224
-rw-r--r--LICENSE27
-rw-r--r--Makefile249
-rw-r--r--README.md29
-rw-r--r--Unicode-test.c32
-rw-r--r--analyze-utf8.c57
-rw-r--r--anonymous_function.c96
-rw-r--r--array_or_struct.c30
-rw-r--r--bad.c23
-rw-r--r--basic_blocks.c203
-rw-r--r--basic_blocks2.c233
-rw-r--r--bitint.c48
-rw-r--r--build-musl120
-rw-r--r--c23-fallback.h1325
-rw-r--r--cat.c26
-rw-r--r--circular.c174
-rw-r--r--circular.h123
-rw-r--r--constexpr.c29
-rw-r--r--crash.c33
-rw-r--r--crash.h37
-rw-r--r--decimal-binary.c38
-rw-r--r--embed.c57
-rw-r--r--endianness.c19
-rw-r--r--enum.c93
-rw-r--r--esc.c12
-rw-r--r--esc.h156
-rw-r--r--euclid.c15
-rw-r--r--euclid.h24
-rw-r--r--extern.c13
-rw-r--r--fibonacci.c19
-rw-r--r--fibonacci2.c26
-rw-r--r--fibonacci3.c27
-rw-r--r--fibonacci4.c30
-rw-r--r--fibonacci5.c30
-rw-r--r--fibonacci6.c29
-rw-r--r--fibonacciCache.c50
-rw-r--r--fibonacciRet.c46
-rw-r--r--fp_except.c88
-rw-r--r--generic.c62
-rw-r--r--generic.h360
-rw-r--r--getting-started.c24
-rw-r--r--heron-expanded.c37
-rw-r--r--heron.c36
-rw-r--r--heron_k.c231
-rw-r--r--heron_k.h133
-rw-r--r--life.c264
-rw-r--r--life.h83
-rw-r--r--lifetime.c89
-rw-r--r--literals.c7
-rw-r--r--locale.c22
-rw-r--r--macro_trace.c40
-rw-r--r--macro_trace.h302
-rw-r--r--mbstrings-main.c206
-rw-r--r--mbstrings.c346
-rw-r--r--mbstrings.h606
-rw-r--r--numberline.c276
-rw-r--r--precision.c8
-rw-r--r--rationals.c218
-rw-r--r--rationals.h41
-rw-r--r--reorder.h245
-rw-r--r--sequence_point.c10
-rw-r--r--shadow.c20
-rw-r--r--sighandler.c99
-rw-r--r--sighandler.h101
-rw-r--r--sighandler_test.c70
-rw-r--r--stats.c17
-rw-r--r--stats.h213
-rw-r--r--strcpy.c17
-rw-r--r--strtoul.c149
-rw-r--r--swap.c144
-rw-r--r--tendots.c25
-rw-r--r--termin.c97
-rw-r--r--termin.h54
-rw-r--r--terran.c122
-rw-r--r--test-c8.c54
-rw-r--r--test-features.c368
-rw-r--r--test-lib.c46
-rw-r--r--test-printf.c341
-rw-r--r--test-reorder.c2
-rw-r--r--test-strfrom.c26
-rw-r--r--test_circular.c22
-rw-r--r--tgstring.h485
-rw-r--r--tgstring_test.c53
-rw-r--r--timespec.c118
-rw-r--r--va_arg.c52
-rw-r--r--volatile_atomic.c162
-rw-r--r--yday.c41
89 files changed, 10669 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..0a09af9
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Jens Gustedt <[email protected]>
diff --git a/B9-detach.c b/B9-detach.c
new file mode 100644
index 0000000..d6ecfb6
--- /dev/null
+++ b/B9-detach.c
@@ -0,0 +1,234 @@
+#include "life.h"
+//#include <stdlib.h>
+#include <stdio.h>
+#include <stdatomic.h>
+//#include <limits.h>
+#include "termin.h"
+//#include "c23-fallback.h"
+//#include <unistd.h>
+
+// The keys that are used for cursor movement.
+// Other keys that are use are
+// b, B, space for birth9
+// -, + to slow down and accelerate
+// q, Q to quit
+#define GO_UP 'k'
+#define GO_DOWN 'l'
+#define GO_RIGHT ';'
+#define GO_LEFT 'j'
+#define GO_HOME 'h'
+
+// We translate escape sequences that we may receive to these standard
+// characters for further processing.
+
+#define ESCAPE '\e'
+
+char const*const termin_trans[UCHAR_MAX+1] = {
+ [GO_UP] = ESC_UP,
+ [GO_DOWN] = ESC_DOWN,
+ [GO_RIGHT] = ESC_FRWD,
+ [GO_LEFT] = ESC_BKWD,
+ [GO_HOME] = ESC_HOME,
+};
+
+static int account_thread(void*);
+
+static
+int update_thread(void* Lv) {
+ /* Nobody should ever wait for this thread. */
+ thrd_detach(thrd_current());
+ /* Delegates part of our job to an auxiliary thread */
+ thrd_create(&(thrd_t){0}, account_thread, Lv);
+ life*restrict L = Lv;
+ size_t changed = 1;
+ size_t birth9 = 0;
+ while (!L->finished && changed) {
+ // block until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished && (L->accounted < L->iteration))
+ life_wait(&L->upda, &L->mtx);
+
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ if (birth9 != L->birth9) life_torus(L);
+ life_count(L);
+ changed = life_update(L);
+ life_torus(L);
+ birth9 = L->birth9;
+ if (L->iteration != SIZE_MAX) L->iteration++;
+ else L->finished = true;
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ cnd_signal(&L->acco);
+ cnd_signal(&L->draw);
+ mtx_unlock(&L->mtx);
+ life_sleep(1.0/L->frames);
+ }
+ /* Tells everybody that the game is over */
+ L->finished = true;
+ ungetc('q', stdin);
+ return 0;
+}
+
+static
+int draw_thread(void* Lv) {
+ /* Nobody should ever wait for this thread. */
+ thrd_detach(thrd_current());
+ life*restrict L = Lv;
+ size_t x0 = 0;
+ size_t x1 = 0;
+ fputs(ESC_CLEAR ESC_CLRSCR, stdout);
+ while (!L->finished) {
+ // Blocks until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished
+ && (L->iteration <= L->drawn)
+ && (x0 == L->x0)
+ && (x1 == L->x1))
+ life_wait(&L->draw, &L->mtx);
+
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ if (L->n0 <= 30) life_draw(L);
+ else life_draw4(L);
+ L->drawn++;
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ mtx_unlock(&L->mtx);
+
+ x0 = L->x0;
+ x1 = L->x1;
+ // no need to draw too quickly
+ life_sleep(1.0/40);
+ }
+ return 0;
+}
+
+// This number of consecutive states that are already known (that is,
+// hashed in) decides that this sequence of states is cyclic.
+constexpr unsigned repetition = 10;
+
+static
+int account_thread(void* Lv) {
+ /* Nobody should ever wait for this thread. */
+ thrd_detach(thrd_current());
+ life*restrict L = Lv;
+ while (!L->finished) {
+ // Blocks until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished && (L->accounted == L->iteration))
+ life_wait(&L->acco, &L->mtx);
+
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ life_account(L);
+ if ((L->last + repetition) < L->accounted) {
+ L->finished = true;
+ }
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ cnd_signal(&L->upda);
+ mtx_unlock(&L->mtx);
+ }
+ return 0;
+}
+
+static
+int input_thread(void* Lv) {
+ /* Nobody should ever wait for this thread. */
+ thrd_detach(thrd_current());
+ termin_unbuffered();
+ life*restrict L = Lv;
+ constexpr size_t len = 32;
+ char command[len];
+ do {
+ auto c = getchar();
+ command[0] = c;
+ switch(c) {
+ case GO_LEFT : life_advance(L, 0, -1); break;
+ case GO_RIGHT: life_advance(L, 0, +1); break;
+ case GO_UP : life_advance(L, -1, 0); break;
+ case GO_DOWN : life_advance(L, +1, 0); break;
+ case GO_HOME : L->x0 = 1; L->x1 = 1; break;
+ case ESCAPE :
+ ungetc(termin_translate(termin_read_esc(len, command)), stdin);
+ continue;
+ case '+': if (L->frames < 128) L->frames++; continue;
+ case '-': if (L->frames > 1) L->frames--; continue;
+ case ' ':
+ case 'b':
+ case 'B':
+ mtx_lock(&L->mtx);
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ life_birth9(L);
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ mtx_unlock(&L->mtx);
+ break;
+ case 'q':
+ case 'Q':
+ case EOF: goto FINISH;
+ }
+ cnd_signal(&L->draw);
+ } while (!(L->finished || feof(stdin)));
+ FINISH:
+ L->finished = true;
+ return 0;
+}
+
+enum { n = 60, m = 160 };
+
+bool M[n][m] = {
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { },
+};
+
+life L = LIFE_INITIALIZER;
+
+void B9_atexit(void) {
+ /* Puts the board in a nice final picture */
+ L.iteration = L.last;
+ life_draw(&L);
+ life_destroy(&L);
+}
+
+int main(int argc, char* argv[argc+1]) {
+ /* Uses command-line arguments for the size of the board */
+ size_t n0 = 30;
+ size_t n1 = 80;
+ if (argc > 1) n0 = strtoull(argv[1], nullptr, 0);
+ if (argc > 2) n1 = strtoull(argv[2], nullptr, 0);
+ /* Create an object that holds the game's data. */
+ life_init(&L, n0, n1, M);
+ atexit(B9_atexit);
+ /* Creates four threads that operate on the same object and
+ discards their IDs */
+ thrd_create(&(thrd_t){0}, update_thread, &L);
+ thrd_create(&(thrd_t){0}, draw_thread, &L);
+ thrd_create(&(thrd_t){0}, input_thread, &L);
+ /* Ends this thread nicely and lets the threads go on nicely */
+ thrd_exit(0);
+}
diff --git a/B9.c b/B9.c
new file mode 100644
index 0000000..c0ef46e
--- /dev/null
+++ b/B9.c
@@ -0,0 +1,224 @@
+#include "life.h"
+//#include <stdlib.h>
+#include <stdio.h>
+#include <stdatomic.h>
+//#include <limits.h>
+#include "termin.h"
+//#include "c23-fallback.h"
+//#include <unistd.h>
+
+// The keys that are used for cursor movement
+// Other keys that are used are:
+// b, B, space for birth9
+// -, + to slow down and accelerate
+// q, Q to quit
+#define GO_UP 'k'
+#define GO_DOWN 'l'
+#define GO_RIGHT ';'
+#define GO_LEFT 'j'
+#define GO_HOME 'h'
+
+// We translate escape sequences that we may receive to these standard
+// characters for further processing.
+
+#define ESCAPE '\e'
+
+char const*const termin_trans[UCHAR_MAX+1] = {
+ [GO_UP] = ESC_UP,
+ [GO_DOWN] = ESC_DOWN,
+ [GO_RIGHT] = ESC_FRWD,
+ [GO_LEFT] = ESC_BKWD,
+ [GO_HOME] = ESC_HOME,
+};
+
+static
+int update_thread(void* Lv) {
+ life*restrict L = Lv;
+ size_t changed = 1;
+ size_t birth9 = 0;
+ while (!L->finished && changed) {
+ // Blocks until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished && (L->accounted < L->iteration))
+ life_wait(&L->upda, &L->mtx);
+
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ if (birth9 != L->birth9) life_torus(L);
+ life_count(L);
+ changed = life_update(L);
+ life_torus(L);
+ birth9 = L->birth9;
+ if (L->iteration != SIZE_MAX) L->iteration++;
+ else L->finished = true;
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ cnd_signal(&L->acco);
+ cnd_signal(&L->draw);
+ mtx_unlock(&L->mtx);
+ life_sleep(1.0/L->frames);
+ }
+ return 0;
+}
+
+static
+int draw_thread(void* Lv) {
+ life*restrict L = Lv;
+ size_t x0 = 0;
+ size_t x1 = 0;
+ fputs(ESC_CLEAR ESC_CLRSCR, stdout);
+ while (!L->finished) {
+ // Blocks until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished
+ && (L->iteration <= L->drawn)
+ && (x0 == L->x0)
+ && (x1 == L->x1)) {
+ life_wait(&L->draw, &L->mtx);
+ }
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ if (L->n0 <= 30) life_draw(L);
+ else life_draw4(L);
+ L->drawn++;
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ mtx_unlock(&L->mtx);
+
+ x0 = L->x0;
+ x1 = L->x1;
+ // No need to draw too quickly
+ life_sleep(1.0/40);
+ }
+ return 0;
+}
+
+// This number of consecutive states that are already known (that is,
+// hashed in) decides that this sequence of states is cyclic
+constexpr unsigned repetition = 10u;
+
+static
+int account_thread(void* Lv) {
+ life*restrict L = Lv;
+ while (!L->finished) {
+ // Blocks until there is work
+ mtx_lock(&L->mtx);
+ while (!L->finished && (L->accounted == L->iteration))
+ life_wait(&L->acco, &L->mtx);
+
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ life_account(L);
+ if ((L->last + repetition) < L->accounted) {
+ L->finished = true;
+ }
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+ cnd_signal(&L->upda);
+ mtx_unlock(&L->mtx);
+ }
+ return 0;
+}
+
+static
+int input_thread(void* Lv) {
+ termin_unbuffered();
+ life*restrict L = Lv;
+ constexpr size_t len = 32;
+ char command[len];
+ do {
+ auto c = getchar();
+ command[0] = c;
+ switch(c) {
+ case GO_LEFT : life_advance(L, 0, -1); break;
+ case GO_RIGHT: life_advance(L, 0, +1); break;
+ case GO_UP : life_advance(L, -1, 0); break;
+ case GO_DOWN : life_advance(L, +1, 0); break;
+ case GO_HOME : L->x0 = 1; L->x1 = 1; break;
+ case ESCAPE :
+ ungetc(termin_translate(termin_read_esc(len, command)), stdin);
+ continue;
+ case '+': if (L->frames < 128) L->frames++; continue;
+ case '-': if (L->frames > 1) L->frames--; continue;
+ case ' ':
+ case 'b':
+ case 'B':
+ mtx_lock(&L->mtx);
+ // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
+ life_birth9(L); /*@\label{lab:birth9}*/
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ cnd_signal(&L->draw);
+ mtx_unlock(&L->mtx);
+ continue;
+ case 'q':
+ case 'Q':
+ case EOF: goto FINISH;
+ }
+ cnd_signal(&L->draw); /*@\label{lab:signal}*/
+ } while (!(L->finished || feof(stdin)));
+ FINISH:
+ L->finished = true;
+ return 0;
+}
+
+enum { n = 60, m = 160 };
+
+bool M[n][m] = {
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { },
+ { },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
+ { 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0 },
+ { },
+};
+
+int main(int argc, char* argv[argc+1]) {
+ /* Uses command-line arguments for the size of the board */
+ size_t n0 = 30;
+ size_t n1 = 80;
+ if (argc > 1) n0 = strtoull(argv[1], nullptr, 0);
+ if (argc > 2) n1 = strtoull(argv[2], nullptr, 0);
+ /* Create an object that holds the game's data. */
+ life L = LIFE_INITIALIZER;
+ life_init(&L, n0, n1, M);
+ /* Creates four threads that all operate on that same object
+ and collects their IDs in "thrd" */
+ thrd_t thrd[4];
+ thrd_create(&thrd[0], update_thread, &L);
+ thrd_create(&thrd[1], draw_thread, &L);
+ thrd_create(&thrd[2], input_thread, &L);
+ thrd_create(&thrd[3], account_thread, &L);
+ /* Waits for the update thread to terminate */
+ thrd_join(thrd[0], nullptr);
+ /* Tells everybody that the game is over */
+ L.finished = true;
+ ungetc('q', stdin);
+ /* Waits for the other threads */
+ thrd_join(thrd[1], nullptr);
+ thrd_join(thrd[2], nullptr);
+ thrd_join(thrd[3], nullptr);
+ /* Puts the board in a nice final picture */
+ L.iteration = L.last;
+ life_draw(&L);
+ life_destroy(&L);
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..40e45ce
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+The C source code provided with this book is licensed under the
+following standard MIT license:
+
+----------------------------------------------------------------------
+Copyright © 2015-2024 Jens Gustedt
+
+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.
+----------------------------------------------------------------------
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2cd989c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,249 @@
+
+# A file to describe dependencies for compilations used by the POSIX
+# "make" utility, see http://pubs.opengroup.org/onlinepubs/9699919799/
+
+# Ensure that we use strict POSIX make rules.
+.POSIX :
+
+# empty the list of suffixes
+.SUFFIXES :
+# C files, sources
+.SUFFIXES : .c .h
+# products, object files, make dependencies, assembler
+.SUFFIXES : .o .d .s
+
+# The choice of the compiler. Commonly available on POSIX systems are
+# compilers called clang, gcc, icc or c99. Try them in this order if
+# the following does not work for you.
+#
+# Here we used a "wrapper" for gcc that comes with the "musl" C
+# library. This has the advatage to provide full support for C11
+# threads that we need for the "experience" level. On some Linux
+# distributions musl is available as packages 'musl', 'musl-dev',
+# 'musl-tools'. Other Linux distributions such as Alpine directly ship
+# with musl as their C library.
+CC ?= /usr/local/musl/bin/musl-gcc
+#CC = clang
+#CC = gcc
+#CC = icc
+#CC = c99
+
+# A collection of typical flags on a Linux box with gcc or clang
+
+# First a choice of a C standard to which your code is conforming. The
+# following should be the default for a recent compiler (2023). Other
+# options could be -std=c11 or -std=gnu11.
+STD = -std=c2x
+
+# Warning options. It is important that the compiler gives you all
+# that he knows.
+WAR = -Wall
+
+# Optimization options. The ones here are my favorite, maximal
+# optimization for the machine on which we are compiling and maximal
+# separation of functions and data into different sections.
+OPT ?= -O3 -march=native -fdata-sections -ffunction-sections
+
+# For the gcc and friends the addition of this flag produces a make
+# dependency file with suffix .d
+DEP = -MD
+
+CFLAGS = ${SILENT} ${OPT} ${WAR} ${STD} ${INC} ${DEP}
+
+# The linker flags. Here this corresponds to eliminate all of the
+# collected sections that are finally not needed in the executable.
+LDFLAGS = -Wl,--gc-sections
+# in the link step we need our own library
+LDLIBS = -L. -lmodernC
+# sometime we will need the "math" library and also "real time"
+LDLIBS += -lm -lrt
+
+ifdef CEDRO
+export CEDRO_CC := ${CC} -D__CEDRO__ -std=c2x -x c -
+export CCC := ${CEDRO}/bin/cedrocc
+else
+export CCC := ${CC}
+endif
+
+# C sources that produce an executable
+ROURCES = \
+ basic_blocks.c \
+ basic_blocks2.c \
+ bitint.c \
+ cat.c \
+ crash.c \
+ endianness.c \
+ euclid.c \
+ extern.c \
+ embed.c \
+ enum.c \
+ fibonacci2.c \
+ fibonacci3.c \
+ fibonacci4.c \
+ fibonacci5.c \
+ fibonacci6.c \
+ fibonacci.c \
+ fibonacciCache.c \
+ fibonacciRet.c \
+ fp_except.c \
+ generic.c \
+ getting-started.c \
+ heron.c \
+ heron-expanded.c \
+ heron_k.c \
+ lifetime.c \
+ locale.c \
+ macro_trace.c \
+ mbstrings-main.c \
+ numberline.c \
+ precision.c \
+ rationals.c \
+ shadow.c \
+ sighandler_test.c \
+ strcpy.c \
+ strtoul.c \
+ tendots.c \
+ terran.c \
+ test_circular.c \
+ test-features.c \
+ test-printf.c \
+ timespec.c \
+ va_arg.c \
+ yday.c
+
+
+# Comment this out if your platform does not support the C11 thread model
+TOURCES = B9.c B9-detach.c
+
+SOURCES = ${ROURCES} ${TOURCES}
+
+# Translation units (TU) that provide functionality that can be used
+# by others. These are collected into the library libmodernC.a
+RSOURCES = \
+ circular.c \
+ literals.c \
+ mbstrings.c \
+ sighandler.c \
+ stats.c
+
+# Comment this out if your platform does not support the C11 thread model
+TSOURCES = esc.c life.c termin.c
+
+LSOURCES = ${RSOURCES} ${TSOURCES}
+
+# TU that just prove a point.
+OSOURCES = \
+ anonymous_function.c \
+ atomic_or_struct.c \
+ constexpr.c \
+ volatile_atomic.c
+
+# Examples of bad TU that produce diagnostics when compiled
+BSOURCES = \
+ getting-started-badly.c \
+ tgstring_test.c
+
+# The following lines use the above to create new lists of files where
+# the .c suffixes are replaced.
+OBJECTS = ${SOURCES:.c=.o}
+
+LOBJECTS = ${LSOURCES:.c=.o}
+
+BOBJECTS = ${BSOURCES:.c=.o}
+
+OOBJECTS = ${OSOURCES:.c=.o}
+
+BINARIES = ${SOURCES:.c=}
+
+DEPENDS = ${SOURCES:.c=.d} ${LSOURCES:.c=.d}
+
+# This implicit rule tells how make is supposed to produce an object
+# file (.o suffix) from a C file (.c suffix). This is needed to
+# produce an executable file from your program.
+#
+# Here the make-special variable $< expands and to the dependency (the
+# .o object file).
+.c.o :
+ ${CCC} ${CFLAGS} -o $@ -c $<
+
+# This implicit rule tells how make is supposed to produce an assembly
+# file (.s suffix) from a C file (.c suffix). This is only needed if
+# you want to inspect what your compiler produces from your code.
+.c.s :
+ ${CC} ${CFLAGS} -o $@ -S $<
+
+# This implicit rule tells make that the dependency file (.d suffix)
+# itself depends on the object file. This is a bit of a lie, because
+# we will produce them along (see DEP above) when normally compiling.
+.d.o :
+
+# This implicit rule tells make that the final executable file (no
+# suffix) depends on the object file (.o suffix). If your platform has
+# a suffix for executables, e.g .exe, you'd have to add it here. On
+# POSIX systems it is common to use the same compiler command (here
+# ${CC}) also for linking.
+#
+# The additional make-special variable $@ expands to the target name
+# (the executable).
+.o :
+ ${CC} -o $@ ${LDFLAGS} $< ${LDLIBS}
+
+# This is the first explicit target, so per default make will try to
+# produce it. It is only here for the dependencies to all our
+# executables, so all of these will be created, if possible. Since
+# this target has no rule of its own, a file named "binaries" will
+# never be produced.
+binaries : ${BINARIES}
+
+# This rule tells make that all our executable files also depend on
+# the archive 'libmodernC.a'.
+${BINARIES} : libmodernC.a
+
+# That archive depends on all the "LOBJECTS"
+libmodernC.a : ${LOBJECTS}
+ ${AR} crf $@ $^
+
+# These target need some special command line arguments so they have
+# their own production line.
+tgstring.o : tgstring.h
+ ${CC} -D_TG_INLINE= -c -x c ${CFLAGS} -o tgstring.o tgstring.h
+
+embed : embed.o
+ ${CC} ${CFLAGS} -o $@ ${LDFLAGS} $< ${LDLIBS}
+
+# An alternative target that just has all sources compiled.
+objects : ${OBJECTS} ${LOBJECTS} ${OOBJECTS}
+
+
+# Compilation of the "bad" objects will produce a lot of
+# diagnostics. These are for demonstrative purposes, only.
+bad : ${BOBJECTS}
+
+# Linkage of the "bad" objects may produce executables that have
+# unforseeable effects if they are run.
+#
+# ***** DO NOT USE THESE ****
+#
+# These are for demonstrative purposes, only.
+verybad : ${BOBJECTS:.o=}
+
+# They also depend on the library.
+${BOBJECTS:.o=} : libmodernC.a
+
+# Cleanup the directory from all files that we produce
+clean :
+ rm -f libmodernC.a ${DEPENDS} ${OBJECTS} ${LOBJECTS} ${OOBJECTS} ${BOBJECTS} ${BINARIES} ${BOBJECTS:.o=}
+
+# Include all the produced dependency files. In POSIX, it is
+# unspecified if "include" works with more than one file, but usually
+# it does.
+#
+# The "-" in front tells make not to complain, if the dependency files
+# to not exist, yet. If they are missing, make looks into its rules to
+# see how to produce them, produces them and reads them.
+#
+# If the initial generation of make dependency files does not work for
+# you, you could launch a compilation of all objects with
+#
+# make -i
+-include ${DEPENDS}
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a7e5989
--- /dev/null
+++ b/README.md
@@ -0,0 +1,29 @@
+# Modern C (C23 Edition) — Code & Study Mirror
+
+This repository contains the official code examples accompanying Jens Gustedt's *Modern C* (C23 Edition, Manning, 2024). I am currently studying this text, and custom build configurations, notes, or experimental branches may be added over time.
+
+---
+
+# Code examples for the book Modern C
+
+This is a collection of code snippets that come with the book
+
+ Jens Gustedt, /Modern C/, C23 edition, Manning, 2024
+
+They have probably not much value outside that context.
+
+The following files that come with this distribution have more
+information:
+
+ - Makefile, for hints about how to compile the code
+ - c23-fallback.h for the transition to C23
+ - LICENSE, for the specific license of this code
+
+You may find additional resources for the book at
+
+https://gustedt.gitlabpages.inria.fr/modern-c/
+
+If you cannot or do not want to purchase a copy of the book, you may
+obtain a CC licensed version of the C17 edition at
+
+https://hal.inria.fr/hal-02383654
diff --git a/Unicode-test.c b/Unicode-test.c
new file mode 100644
index 0000000..5df61e3
--- /dev/null
+++ b/Unicode-test.c
@@ -0,0 +1,32 @@
+/**
+ ** @file Test handling of different Unicode letters in identifiers.
+ **/
+
+#include "c23-fallback.h"
+
+double Å = 1; // error, Ångström not normalized
+double Å = 2; // ok, A with circle
+double A\u030A = 3; // error, combination not normalized
+double Ω = 1; // error, Ohm not normalized
+double Ω = 2; // ok, Greek capital omega
+double K = 1; // error, Kelvin not normalized
+double K = 2;
+double fi = 1; // valid, fi does not decompose into f and i
+double fi = 2; // valid, different identifier
+typedef double ℝ; // valid
+typedef unsigned long long ℕ; // valid
+#define ℜ(Z) real(Z) // valid
+#define ℑ(Z) imag(Z) // valid
+ℕ №; // error: Numero sign is not a letter
+ℕ ℓ; // valid, ell sign is a letter
+double ʃ(double a, double b, double φ(double)); // IPA is also acceptable
+typedef double 각; // valid, Hangul generally maps to itself
+각 α = 1;
+enum : bool { 응 = true, wɛ = true, };
+char const 얜읐[] = "얜읐";
+int a١ = 1; // valid, Arabic digits are considered different
+int a1 = 2;
+int a⁰ = 1; // non-portable, gcc and clang extension
+int a𝟬 = 1; // valid, but not a good idea
+int a0 = 2;
+unsigned Ⅾ; // valid, roman digit 500, really a bad idea
diff --git a/analyze-utf8.c b/analyze-utf8.c
new file mode 100644
index 0000000..8e8f881
--- /dev/null
+++ b/analyze-utf8.c
@@ -0,0 +1,57 @@
+#include "mbstrings.h"
+#include <uchar.h>
+#include <stdio.h>
+#include <locale.h>
+
+int main(void) {
+ // Make sure to have the platform's mb encoding on input.
+ setlocale(LC_CTYPE, "");
+ // Holds the state of input/output buffering.
+ mbstate_t st = { };
+ // collects the input mb sequence
+ char ib[23];
+ // collects the current UTF-8 mb sequence
+ char8_t ob[5] = { };
+ // the number of input characters for the current code point
+ size_t in = 0;
+ while (fgets(ib, sizeof(ib), stdin)) {
+ // Run through the current line. The last character is
+ // always reserved for the string terminator.
+ for (char* p = ib; (p-ib) < sizeof(ib)-1;) {
+ size_t const n = sizeof(ib)-1-(p-ib);
+ size_t const r = mbrtoc8(ob, p, n, &st);
+ switch (r) { // Handle the special cases.
+ case mbincomplete: p += n; in += n; continue;
+ case 0: case mbstored: case mbinvalid: goto INVAL;
+ }
+ p += r; in += r;
+ char8_t* cont = ob+1; // first character is already stored
+ while (mbrtoc8(cont, "", 1, &st) == mbstored) {
+ cont++;
+ }
+ // Now we have the whole UTF-8. Analyze the result.
+ printf("%s", ((cont-ob) == 1) ? "ASCII\t" : "UTF-8\t");
+ for (char8_t* o = ob; o < cont; ++o) {
+ printf("|%02hhx", *o);
+ }
+ // fgets stopped at an end of a line
+ if (*ob == u8'\n') {
+ puts("|\t~ eol");
+ in = 0;
+ break;
+ } else if (in == (cont-ob)) {
+ printf("|\t~ '%s'\n", ob);
+ } else {
+ printf("|\t%zu→%tu\n", in, (cont-ob));
+ }
+ in = 0;
+ }
+ if (*ob != u8'\n') {
+ fputs("incomplete line\n", stderr);
+ }
+ }
+ return EXIT_SUCCESS;
+ INVAL:
+ fputs("input error, exiting\n", stderr);
+ return EXIT_FAILURE;
+}
diff --git a/anonymous_function.c b/anonymous_function.c
new file mode 100644
index 0000000..8144e6f
--- /dev/null
+++ b/anonymous_function.c
@@ -0,0 +1,96 @@
+#include <stdio.h>
+
+/**
+ ** @file
+ **
+ ** This plays with gcc features to define a weird macro that emulates
+ ** anonymous functions that can be used as closures. Don't use that
+ ** seriously, this is only meant to prove that this is syntactically
+ ** not a big deal, and that the ideas necessary for it can be found
+ ** in modern compilers.
+ **
+ **/
+
+/**
+ ** @def ANONYMOUS(RET)
+ ** @brief Use this to define an anonymous function with return type @a RET.
+ **
+ ** The @c ... argument list is the parameter list of the
+ ** function. This the expects the code of the function in <code>({
+ ** })</code> parenthesis. A simple example would be
+ **
+ ** @code
+ ** // Function header
+ ** ANONYMOUS(int) (double a, unsigned b)
+ ** // Function body
+ ** ({
+ ** return printf("we found %g for %u, average %g\n", a, b, a/b);
+ ** })
+ ** // Function arguments
+ ** (x, 23);
+ ** @endcode
+ **
+ ** Of course you would usually put such things inside a macro. See
+ ** MAX() for a more complicated example.
+ **/
+#ifdef __clang__
+# define ANONYMOUS(RET) (^ RET ANON_PART2
+# define ANON_PART2(...) (__VA_ARGS__) ANON_PART3
+# define ANON_PART3(...) __VA_ARGS__)
+#else
+# define ANONYMOUS(RET) ({ RET __anon_func ANON_PART2
+# define ANON_PART2(...) (__VA_ARGS__) ANON_PART3
+# define ANON_PART3(...) __VA_ARGS__ __anon_func; })
+#endif
+
+#define MAXU(A, B) \
+ANONYMOUS(uintmax_t) (uintmax_t a, uintmax_t b) \
+({ \
+ if (a < b) return b; \
+ else return a; \
+}) \
+((A), (B))
+
+#define MAX(A, B) \
+/* function header */ \
+ /* return type */ \
+ANONYMOUS(__typeof__(1 ? (A) : (B))) \
+ /*and then argument list */ \
+(__typeof__(A) a, __typeof__(B) b) \
+/* function body */ \
+({ \
+ if (a < b) return b; \
+ else return a; \
+ }) \
+/* function call */ \
+((A), (B))
+
+/**
+ ** Test to see if that even works when such functions are nested.
+ **/
+
+typedef void df(unsigned);
+
+df* Df[5] = { };
+
+void g(unsigned level, unsigned depth) {
+ Df[depth%5] =
+ ANONYMOUS(void) (register unsigned a)
+ ({
+ printf("from level %u, depth %u found in position %u\n",
+ level, depth,
+ MAX(2*a, depth)
+ );
+ return;
+ });
+ if (level)
+ g(level-1, depth+1);
+ else
+ for (unsigned i = 0; i < 5; ++i)
+ if (Df[i]) Df[i](i);
+}
+
+
+int main(int argc, [[maybe_unused]] char* argv[]) {
+ g(argc, 0);
+}
diff --git a/array_or_struct.c b/array_or_struct.c
new file mode 100644
index 0000000..3190fe1
--- /dev/null
+++ b/array_or_struct.c
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+#ifdef USE_STRUCT
+ typedef struct two two;
+ struct two { double e1; double e2; };
+# define FRST(X) ((X).e1)
+# define SEC(X) ((X).e2)
+#else
+ typedef double two[2];
+# define FRST(X) ((X)[0])
+# define SEC(X) ((X)[1])
+#endif
+
+void func0(void) {
+ for (two sp = { 1, 1, };
+ FRST(sp) + SEC(sp) < 10;
+ FRST(sp) += SEC(sp)) {
+ printf("two values %g %g\n", FRST(sp), SEC(sp));
+ SEC(sp) += (FRST(sp) + SEC(sp))/2;
+ }
+}
+
+void func1(void) {
+ for (register two sp = { 1, 1, };
+ FRST(sp) + SEC(sp) < 10;
+ FRST(sp) += SEC(sp)) {
+ printf("two values %g %g\n", FRST(sp), SEC(sp));
+ SEC(sp) += (FRST(sp) + SEC(sp))/2;
+ }
+}
diff --git a/bad.c b/bad.c
new file mode 100644
index 0000000..7253662
--- /dev/null
+++ b/bad.c
@@ -0,0 +1,23 @@
+/* This may look like nonsense, but really is -*- mode: C -*- */
+
+/* The main thing that this program does. */
+void main() {
+ // Declarations
+ int i;
+ double A[5] = {
+ 9.0,
+ 2.9,
+ 3.E+25,
+ .00007,
+ };
+
+ // Doing some work
+ for (i = 0; i < 5; ++i) {
+ printf("element %d is %g, \tits square is %g\n", /*@\label{printf-start-badly}*/
+ i,
+ A[i],
+ A[i]*A[i]); /*@\label{printf-end-badly}*/
+ }
+
+ return 0; /*@\label{main-return-badly}*/
+}
diff --git a/basic_blocks.c b/basic_blocks.c
new file mode 100644
index 0000000..e0fcb05
--- /dev/null
+++ b/basic_blocks.c
@@ -0,0 +1,203 @@
+#include "sighandler.h"
+#include <ctype.h>
+#include <stdio.h>
+
+/*
+ * Because the parser is implemented here, and doesn't take care of
+ * strings, character constants, or comments, we hide the { } with
+ * compile time constants.
+ */
+constexpr char left = '{';
+constexpr char right = '}';
+
+/**
+ ** @brief Initial string to indent a line
+ **/
+constexpr char head[] = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| ";
+
+/**
+ ** @brief Skips any number of spaces, tabs, or newlines in @a s
+ **/
+static
+char const* skipspace(char const s[static 1]) {
+ while (isspace(s[0])) {
+ ++s;
+ }
+ return s;
+}
+
+/**
+ ** @brief Exceptional states of the parse algorithm
+ **/
+enum state {
+ execution = 0, //!< Normal execution
+ plusL, //!< Too many left braces
+ plusR, //!< Too many right braces
+ tooDeep, //!< Nesting too deep to handle
+ eofOut, //!< End of output
+ interrupted, //!< Interrupted by a signal
+};
+
+/**
+ ** @brief Outputs end of line and eats all subsequent spaces on input
+ **/
+static
+char const* end_line(char const s[static 1], jmp_buf jmpTarget) {
+ if (putchar('\n') == EOF) longjmp(jmpTarget, eofOut);
+ return skipspace(s);
+}
+
+
+
+/**
+ ** @brief Keeps track of the last signal that interrupted us
+ **/
+static sig_atomic_t volatile interrupt = 0;
+
+static
+char const* descend(char const* act,
+ unsigned dp[restrict static 1], // Bad
+ size_t len, char buffer[static len],
+ jmp_buf jmpTarget) {
+ if (dp[0]+3 > sizeof head) longjmp(jmpTarget, tooDeep);
+ ++dp[0];/*@\label{lab:incr-rec}*/
+ NEW_LINE: // Loops on output
+ while (!act || !act[0]) { // Loops for input
+ if (interrupt) longjmp(jmpTarget, interrupted);
+ act = skipspace(fgets(buffer, len, stdin));
+ if (!act) { // End of stream
+ if (dp[0] != 1) longjmp(jmpTarget, plusL);
+ else goto ASCEND;
+ }
+ }
+ fputs(&head[sizeof head - (dp[0] + 2)], stdout); // Header
+
+ for (; act && act[0]; ++act) { // Remainder of the line
+ switch (act[0]) { /*@\label{lab:switch-char}*/
+ case left: // Descends on left brace
+ act = end_line(act+1, jmpTarget);
+ act = descend(act, dp, len, buffer, jmpTarget);/*@\label{lab:descend}*/
+ act = end_line(act+1, jmpTarget);
+ goto NEW_LINE;
+ case right: // Returns on right brace
+ if (dp[0] == 1) longjmp(jmpTarget, plusR);
+ else goto ASCEND;
+ default: // Prints char and goes on
+ putchar(act[0]);
+ }
+ }
+ goto NEW_LINE;
+ ASCEND:
+ --dp[0];/*@\label{lab:decr-rec}*/
+ return act;
+}
+
+constexpr unsigned maxline = 256;
+
+void basic_blocks(void) {
+ char buffer[maxline];
+ unsigned depth = 0;
+ char const* format =
+ "All matching %0.0d'%c' '%c' pairs have been closed correctly\n";
+ jmp_buf jmpTarget;
+ switch (setjmp(jmpTarget)) {
+ case 0:
+ descend(nullptr, &depth, maxline, buffer, jmpTarget);
+ break;
+ case plusL:
+ format =
+ "Warning: %d '%c' have not been closed properly (expected '%c')\n";
+ break;
+ case plusR:
+ format =
+ "Error: closing too many (%d) '%c' parenthesis with additional '%c'\n";
+ break;
+ case tooDeep:
+ format =
+ "Error: nesting (%d) of '%c' '%c' constructs is too deep\n";
+ break;
+ case eofOut:
+ format =
+ "Error: EOF for stdout at %d open '%c', expecting same amount of '%c'\n";
+ break;
+ case interrupted:
+ format =
+ "Interrupted at level %d of '%c' '%c' nesting\n";
+ break;
+ default:;
+ format =
+ "Error: unknown error within (%d) '%c' '%c' constructs\n";
+ }
+ fflush(stdout);
+ fprintf(stderr, format, depth, left, right);
+ if (interrupt) {
+ SH_PRINT(stderr, interrupt,
+ "is somebody trying to kill us?");
+ raise(interrupt);
+ }
+}
+
+/**
+ ** @brief A minimal signal handler
+ **
+ ** After updating the signal count, for most signals this
+ ** simply stores the signal value in "interrupt" and returns.
+ **/
+static void signal_handler(int sig) {
+ sh_count(sig);
+ switch (sig) {
+ case SIGTERM: quick_exit(EXIT_FAILURE);
+ case SIGABRT: _Exit(EXIT_FAILURE);
+#ifdef SIGCONT
+ // continue normal operation
+ case SIGCONT: return;
+#endif
+ default:
+ /* reset the handling to its default */
+ signal(sig, SIG_DFL);
+ interrupt = sig;
+ return;
+ }
+}
+
+// Will point to the command-line arguments
+static char** lastOpen = nullptr;
+
+// Checks if we were in the middle of an operation
+void doAtExit(void) {
+ if (lastOpen && lastOpen[0]) {
+ fprintf(stderr, "\n***********\nabnormal exit, last open file was %s\n", lastOpen[0]);
+ }
+}
+
+int main(int argc, char* argv[argc+1]) {
+ // Ensures that stdout is line buffered
+ if (setvbuf(stdout, nullptr, _IOLBF, maxline + sizeof head + 2)) {
+ fputs("we could not establish line buffering for stdout, terminating.", stderr);
+ return EXIT_FAILURE;
+ }
+
+ // Establishes exit handlers
+ atexit(doAtExit);
+ at_quick_exit(doAtExit);
+
+ // Establishes signal handlers
+ for (unsigned i = 1; i < sh_known; ++i)
+ sh_enable(i, signal_handler);
+
+ // If there are no command-line arguments, reads from stdin
+ lastOpen = argv;
+ if (argc < 2) goto RUN;
+
+ // Runs basic_blocks for each command-line argument
+ for (++lastOpen; lastOpen[0]; ++lastOpen) {
+ if (!freopen(lastOpen[0], "r", stdin)) {
+ perror(lastOpen[0]);
+ return EXIT_FAILURE;
+ }
+ printf("++++++++++ %s +++++++++++\n", lastOpen[0]);
+ RUN:
+ basic_blocks();
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/basic_blocks2.c b/basic_blocks2.c
new file mode 100644
index 0000000..b47a227
--- /dev/null
+++ b/basic_blocks2.c
@@ -0,0 +1,233 @@
+#include "sighandler.h"
+#include <ctype.h>
+#include <setjmp.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * Because the parser is implemented here, and doesn't take care of
+ * strings, character constants, or comments, we hide the { } with
+ * compile time constants.
+ */
+constexpr char left = '{';
+constexpr char right = '}';
+
+/**
+ ** @brief Initial string to indent a line
+ **/
+constexpr char head[] = ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>| ";
+
+/**
+ ** @brief Skips any number of spaces, tabs, or newlines in @a s
+ **/
+static
+char const* skipspace(char const s[static 1]) {
+ while (isspace(s[0])) {
+ ++s;
+ }
+ return s;
+}
+
+/**
+ ** @brief Exceptional states of the parse algorithm
+ **/
+enum {
+ execution = 0, //!< Normal execution
+ plusL, //!< Too many left braces
+ plusR, //!< Too many right braces
+ tooDeep, //!< Nesting too deep to handle
+ eofOut, //!< End of output
+ interrupted, //!< Interrupted by a signal
+};
+
+/**
+ ** @brief Outputs end of line and eats all subsequent spaces on input
+ **/
+static
+char const* end_line(char const s[static 1], jmp_buf jmpTarget) {
+ if (putchar('\n') == EOF) longjmp(jmpTarget, eofOut);
+ return skipspace(s);
+}
+
+/**
+ ** @brief Keeps track of the last signal that interrupted us
+ **/
+static sig_atomic_t volatile interrupt = 0;
+
+/**
+ ** @brief Keep track of the depth at which an error was detected.
+ **/
+static unsigned volatile deepest = 0;
+
+static
+char const* descend1(char const act[static 1],
+ unsigned depth,
+ size_t len, char buffer[restrict static len],
+ jmp_buf jmpTarget) {
+ if (depth+2 > sizeof head) longjmp(jmpTarget, tooDeep);
+ NEW_LINE: // loop on output
+ while (!act[0]) { // loop for input
+ if (interrupt) longjmp(jmpTarget, interrupted);
+ act = fgets(buffer, len, stdin);
+ if (!act) { // end of stream
+ deepest = depth;
+ longjmp(jmpTarget, plusL);
+ }
+ act = skipspace(act);
+ }
+ // act is never 0 from here on
+ fputs(&head[sizeof head - (depth + 3)], stdout); // header
+ for (; act[0]; ++act) { // remainder of the line
+ switch (act[0]) {
+ case left: // descend on left brace
+ act = end_line(act+1, jmpTarget);
+ act = descend1(act, depth+1, len, buffer, jmpTarget);
+ act = end_line(act+1, jmpTarget);
+ goto NEW_LINE;
+ case right: // return on right brace
+ return act;
+ default: // print char and go on
+ putchar(act[0]);
+ }
+ }
+ goto NEW_LINE;
+}
+
+static
+void descend0(size_t len, char buffer[static len], jmp_buf jmpTarget) {
+ char const* act = nullptr;
+ NEW_LINE: // loop on output
+ do { // loop for input
+ if (interrupt) longjmp(jmpTarget, interrupted);
+ act = fgets(buffer, len, stdin);
+ if (!act) return;
+ act = skipspace(act);
+ } while (!act[0]);
+ // act is never 0 from here on
+ fputs(&head[sizeof head - 3], stdout); // header
+ for (; act[0]; ++act) { // remainder of the line
+ switch (act[0]) {
+ case left: // descend on left brace
+ act = end_line(act+1, jmpTarget);
+ act = descend1(act, 1, len, buffer, jmpTarget);
+ act = end_line(act+1, jmpTarget);
+ goto NEW_LINE;
+ case right: // right brace is an error
+ longjmp(jmpTarget, plusR);
+ default: // print char and go on
+ if (putchar(act[0]) == EOF) longjmp(jmpTarget, eofOut);
+ }
+ }
+ goto NEW_LINE;
+}
+
+enum { maxline = 256 };
+
+void basic_blocks(void) {
+ char buffer[maxline];
+ char const* format =
+ "All matching %0.0d'%c' '%c' pairs have been closed correctly\n";
+ jmp_buf jmpTarget;
+ switch (setjmp(jmpTarget)) {
+ case 0:
+ deepest = 0;
+ descend0(maxline, buffer, jmpTarget);
+ break;
+ case plusL:
+ format =
+ "Warning: %d '%c' have not been closed properly (expected '%c')\n";
+ break;
+ case plusR:
+ format =
+ "Error: closing too many (%d) '%c' parenthesis with additional '%c'\n";
+ break;
+ case tooDeep:
+ format =
+ "Error: nesting (%d) of '%c' '%c' constructs is too deep\n";
+ break;
+ case eofOut:
+ format =
+ "Error: EOF for stdout at %d open '%c', expecting same amount of '%c'\n";
+ break;
+ case interrupted:
+ format =
+ "Interrupted at level %d of '%c' '%c' nesting\n";
+ break;
+ default:;
+ format =
+ "Error: unknown error within (%d) '%c' '%c' constructs\n";
+ }
+ fflush(stdout);
+ fprintf(stderr, format, deepest, left, right);
+ if (interrupt) {
+ SH_PRINT(stderr, interrupt,
+ "is somebody trying to kill us?");
+ raise(interrupt);
+ }
+}
+
+/**
+ ** @brief A minimal signal handler
+ **
+ ** After updating the signal count, for most signals this
+ ** simply stores the signal value in "interrupt" and returns.
+ **/
+static void signal_handler(int sig) {
+ sh_count(sig);
+ switch (sig) {
+ case SIGTERM: quick_exit(EXIT_FAILURE);
+ case SIGABRT: _Exit(EXIT_FAILURE);
+#ifdef SIGCONT
+ // continue normal operation
+ case SIGCONT: return;
+#endif
+ default:
+ /* reset the handling to its default */
+ signal(sig, SIG_DFL);
+ interrupt = sig;
+ return;
+ }
+}
+
+// Will point to the command-line arguments
+static char** lastOpen = nullptr;
+
+// Checks if we were in the middle of an operation
+void doAtExit(void) {
+ if (lastOpen && lastOpen[0]) {
+ fprintf(stderr, "\n***********\nabnormal exit, last open file was %s\n", lastOpen[0]);
+ }
+}
+
+int main(int argc, char* argv[argc+1]) {
+ // Ensures that stdout is line buffered
+ if (setvbuf(stdout, nullptr, _IOLBF, maxline + sizeof head + 2)) {
+ fputs("we could not establish line buffering for stdout, terminating.", stderr);
+ return EXIT_FAILURE;
+ }
+
+ // Establishes exit handlers
+ atexit(doAtExit);
+ at_quick_exit(doAtExit);
+
+ // Establishes signal handlers
+ for (unsigned i = 1; i < sh_known; ++i)
+ sh_enable(i, signal_handler);
+
+ // If there are no command-line arguments, reads from stdin
+ lastOpen = argv;
+ if (argc < 2) goto RUN;
+
+ // Runs basic_blocks for each command-line argument
+ for (++lastOpen; lastOpen[0]; ++lastOpen) {
+ if (!freopen(lastOpen[0], "r", stdin)) {
+ perror(lastOpen[0]);
+ return EXIT_FAILURE;
+ }
+ printf("++++++++++ %s +++++++++++\n", lastOpen[0]);
+ RUN:
+ basic_blocks();
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/bitint.c b/bitint.c
new file mode 100644
index 0000000..bf2b559
--- /dev/null
+++ b/bitint.c
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include "c23-fallback.h"
+
+#ifndef BITINT_MAXWIDTH
+# error "no _BitInt support found, aborting compilation"
+// shut off some subsequent error messages
+# include "no _BitInt support found, aborting compilation"
+#endif
+
+int main (int argc, char* argv[static argc+1]) {
+ // this doesn't fit, diagnose?
+ _BitInt(15) s15 = 0b1010101'01010101wb;
+ unsigned _BitInt(15) u15 = 0b1010101'01010101wbu;
+ // this doesn't fit, diagnose?
+ _BitInt(15) su15 = u15;
+
+ auto u65 = 0x1'FFFF'FFFF'FFFF'FFFFwbu;
+ // wrap around arithmetic is fine.
+ auto oa65 = 0x1'FFFF'FFFF'FFFF'FFFFwbu + 1wbu;
+ // constant too wide, diagnostic?
+ typeof(u65) o65 = 0x2'FFFF'FFFF'FFFF'FFFFwbu;
+
+ auto s65 = 0xFFFF'FFFF'FFFF'FFFFwb;
+ // signed arithmetic overflows, diagnostic?
+ auto sa65 = 0xFFFF'FFFF'FFFF'FFFFwb + 1wb;
+ // constant too wide, diagnostic?
+ typeof(s65) os65 = 0x1'FFFF'FFFF'FFFF'FFFFwb;
+
+ unsigned _BitInt(128) max128s = 0x7FFF'FFFF'FFFF'FFFF'FFFF'FFFF'FFFF'FFFFwb;
+ unsigned _BitInt(128) min128s = -1wb -max128s;
+
+ unsigned _BitInt(3) max3u = -1;
+ unsigned _BitInt(4) max4u = -1;
+ unsigned _BitInt(4) one4u = max4u - max3u;
+ signed _BitInt(4) max4s = max3u;
+ signed _BitInt(4) min4s = ~max4s;
+
+ printf("15 bits, signed:\t%d\n", (signed)s15);
+ printf("15 bits, unsigned:\t%u\n", (unsigned)u15);
+ printf("15 bits, u → s:\t%d\n", (signed)su15);
+ printf("4 bits:\t%u\n", (unsigned)max4u);
+ printf("4 bits:\t%u\n", (unsigned)one4u);
+ printf("4 bits:\t%d\n", (signed)max4s);
+ printf("4 bits:\t%d\n", (signed)min4s);
+ printf("4 bit arithm:\t%u\n", (unsigned)(max4u + 1wbu));
+ printf("int arithm:\t%u\n", (unsigned)(max4u + 1));
+ return s15 + su15 + u15 + u65 + oa65 + o65 + s65 + sa65 + os65 + !max128s + !min128s;
+}
diff --git a/build-musl b/build-musl
new file mode 100644
index 0000000..a3f6c58
--- /dev/null
+++ b/build-musl
@@ -0,0 +1,120 @@
+#!/bin/sh
+
+# A script to configure the musl C library
+
+# This is intended to provide you with a way to use the musl C library
+# with modern compilers such that you will be able to use most C23
+# features that such compilers already provide.
+#
+# Note that this script not suited for cross compilation.
+#
+# You need
+#
+# - a native Linux system, with musl or glibc, regardless
+#
+# - a modern C compiler with good support for C23 features such as gcc
+# (starting with version 13) or clang (starting with 17).
+
+# - a directory (denoted by sourceDir in the following) with the musl
+# source plus C23 patches as provided here:
+#
+# https://forge.icube.unistra.fr/icps/musl/-/branches
+#
+# As these patches will hopefully be integrated more and more into
+# musl's main line this is a moving target and you should make sure
+# that you always grab the latest c23 branch. As of this writing
+# this is c23-v11.
+#
+# - Root rights (so-called sudoer rights in Linux slang) to be able to
+# install the compiled library and wrappers in a system wide
+# directory, "/usr/local" per default.
+#
+# To configure, compile and install
+#
+# cd sourceDir
+# path/to/this/directory/build-musl COMPILER [FLAG] [LOGFILE]
+#
+# where
+#
+# - COMPILER is the name of the compiler, for example "gcc-13" or
+# "clang-17".
+#
+# (FLAG and LOGFILE are optional and not interesting in general.)
+#
+# For example with
+#
+# path/to/this/directory/build-musl clang-18
+#
+# the LOGFILE is musl-log-clang-18.txt in sourceDir, and the
+# installation is in /usr/local/musl-clang-18. Note that to be able to
+# install, it is usually necessary to collect your password, which is
+# done by a program referred to by the SUDO_ASKPASS variable
+# below. Per default this will result in a popup at the end of
+# compilation that blocks your screen.
+
+# The interface that you then should use is bin/cc, for the example
+# above that would be
+#
+# /usr/local/musl-clang-18/bin/cc
+#
+# You should be able to use this just as any other compiler
+# interface. To use this interface to compile in C23 mode you probably
+# have to add an option "-std=c23" or "-std=c2x".
+
+
+PREFIXDIR="${PREFIXDIR:-/usr/local}"
+LOGFILE="${LOGFILE:-musl-log}"
+SUDO_ASKPASS="${SUDO_ASKPASS:-/usr/bin/ssh-askpass}"
+
+export SUDO_ASKPASS
+
+COMMANDLINE="$*"
+
+if [ $# -gt 0 ] ; then
+ export CC="$1"
+ PREFIXDIR="${PREFIXDIR}/musl-$1"
+ LOGFILE="${LOGFILE}-$1"
+ shift
+fi
+
+if [ $# -gt 0 ] ; then
+ export CFLAGS="-std=$1"
+ PREFIXDIR="${PREFIXDIR}-$1"
+ LOGFILE="${LOGFILE}-$1"
+ shift
+fi
+
+if [ $# -gt 0 ] ; then
+ export CFLAGS="${CFLAGS} $*"
+ PREFIXDIR="${PREFIXDIR}-$1"
+ LOGFILE="${LOGFILE}-$1"
+fi
+
+LOGFILE="${LOGFILE}.txt"
+
+echo > "${LOGFILE}"
+
+echo "command line: ${COMMANDLINE}" | tee -a "${LOGFILE}"
+echo "building musl with prefix '${PREFIXDIR}', ask-pass '${SUDO_ASKPASS}'" | tee -a "${LOGFILE}"
+echo " compiler '${CC}' and flags '${CFLAGS}'" | tee -a "${LOGFILE}"
+echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}"
+./configure "--prefix=${PREFIXDIR}" 2>&1 1>>"${LOGFILE}"
+if [ $? -ne 0 ] ; then
+ echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}"
+ echo "configuration failed" | tee -a "${LOGFILE}"
+ cat "${LOGFILE}"
+ exit 1
+fi
+echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}"
+make clean 2>&1 | tee -a "${LOGFILE}"
+echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}"
+make -j 8 2>&1 | tee -a "${LOGFILE}"
+echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}"
+sudo -A make install 2>&1 | tee -a "${LOGFILE}"
+echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" | tee -a "${LOGFILE}"
+
+if [ -x "${PREFIXDIR}/bin/musl-gcc" ] ; then
+ sudo -A ln -sf "musl-gcc" "${PREFIXDIR}/bin/cc" 2>&1 | tee -a "${LOGFILE}"
+elif [ -x "${PREFIXDIR}/bin/musl-clang" ] ; then
+ sudo -A ln -sf "musl-clang" "${PREFIXDIR}/bin/cc" 2>&1 | tee -a "${LOGFILE}"
+fi
diff --git a/c23-fallback.h b/c23-fallback.h
new file mode 100644
index 0000000..3331631
--- /dev/null
+++ b/c23-fallback.h
@@ -0,0 +1,1325 @@
+#ifndef C23_FALLBACK_H
+#define C23_FALLBACK_H
+
+/** @file
+ ** @brief A header file to augment compatibility of older compilers with C23.
+ **
+ ** This has only been tested with the open source compilers
+ **
+ ** - gcc
+ ** - clang
+ **
+ ** and the C libraries
+ **
+ ** - glibc
+ ** - musl
+ **
+ ** The newer the version of these compilers or C libraries you can
+ ** get, the better. If you use another compiler or C library and have
+ ** adjustments to make to this file, please share. I will try to
+ ** include such changes in the future.
+ **
+ ** Commonly supported C23 features that are required are
+ ** - digit separators such as in `0xAB'CD`
+ ** - binary integer literals such as `0b1010` or `0B0101`
+ ** - the new attribute syntax such as `[[deprecated]]`
+ **
+ ** The first two are not easy to circumvent, so probably you should
+ ** not even try to use C23 enabled code without having support for
+ ** them.
+ **
+ ** For the attribute syntax there is a feature test
+ ** __has_c_attribute. It can be used with define (or #ifdef) to test
+ ** for the syntax itself and with #if and an argument to test for an
+ ** individual attribute, see below.
+ **
+ ** Another feature test is __has_include, which similarly can be used
+ ** to test for the preprocessor feature itself and if that is
+ ** available to test for the availability of a specific header. This
+ ** can be used to
+ **
+ ** - test for optional headers such as <complex.h>, <threads.h>, or
+ ** <stdatomic.h>. This now allows to use the corresponding feature
+ ** test macro (__STDC_NO_COMPLEX__, __STDC_NO_ATOMICS__,
+ ** __STDC_NO_THREADS__) to test for compiler support of these
+ ** features, separately from the availability of library
+ ** interfaces.
+ **
+ ** - test for new headers that come with C23, namely <stdckdint.h>
+ ** and <stdbit.h>. Even if your C library does not support these,
+ ** their functionality should mostly be available through
+ ** type-generic macros that we emulate. Prefer them over function
+ ** symbols, those may depend on linker symbols that are not yet
+ ** available.
+ **
+ ** This header here unconditionally includes a bunch of C library
+ ** headers (see below) and augments them with C23 features if
+ ** possible. You should not include them yourself, such that we are
+ ** sure in what we get. This also concerns the new C23 header
+ ** <stdckdint.h>.
+ **
+ ** Other headers also may be included conditionally, namely
+ ** <threads.h> and <stdatomic.h>.
+ **
+ ** When emulating the new features, there may be spurious warnings
+ ** that some attributes are misplaced or ignored. This obviously
+ ** means that they are not completely taken into account and that the
+ ** analysis they are expected to provide is not yet fully implemented
+ ** by your compiler. If you are flooded by these, you might switch
+ ** some of the diagnostics off by defining the macro
+ ** `C23_FALLBACK_SILENT` for example by providing the command line
+ ** argument `-DC23_FALLBACK_SILENT`.
+ **
+ ** But note also, that some errors that are found are actually real
+ ** errors. These are meant as bad code examples and also as tests if
+ ** compilers are able to detect these kind of things. If you stumble
+ ** into these, there should be a code comment close by that explains
+ ** the particular example. Compilers vary widely in what they are
+ ** able to detect, so here the more you see, the better.
+ **
+ ** And then, ..., there are the bugs. Currently, the code triggers
+ ** one particular bug of gcc-13 in `endianness.c`.
+ **
+ ** As of March 2024, gcc-14 and clang-18 work with most of the new
+ ** C23 features. Current restrictions are
+ **
+ ** - clang-18: `constexpr` is not yet implemented
+ ** - both: `#embed` is not yet implemented
+ **
+ ** As indicated above, some short comings of your C library
+ ** implementation can be worked around, but unfortunately not all. As
+ ** of this writing
+ **
+ ** - glibc: `printf` and `scanf` support only includes the new %b
+ ** specifier for binary representation, but not the %w length
+ ** modifiers.
+ **
+ ** - musl: in the current distribution there is no support for C23
+ ** features, so in particular `printf` etc will not work with the
+ ** new formats, yet.
+ **
+ ** We are currently working to have patches included to musl whence
+ ** the new edition of Modern C and the new standard appear. In the
+ ** mean time I will try to post a patch set and build recipe such
+ ** that you may easily build such modified version of musl with
+ ** complete C23 support on your own. Stay tuned.
+ **/
+
+#include <limits.h>
+
+/* Needed for musl */
+/* #ifdef __has_include */
+/* # if __has_include(<stdc-predef.h>) */
+/* # include <stdc-predef.h> */
+/* # endif */
+/* #endif */
+
+
+#if (__STDC_UTF_16__ != 1) || (__STDC_UTF_32__ != 1)
+# ifndef C23_FALLBACK_SILENT
+# warning "C23 imposes that char16_t and char32_t use UTF-16 and UTF-32 encodings"
+# endif
+#endif
+
+/**
+ ** @brief A preprocessor test for the `__VA_OPT__` feature.
+ **
+ ** Compilers before C23 might not implement `__VA_OPT__`.
+ ** @return This macro should always evaluate to `0` or `1`.
+ **/
+#define __has_va_opt C23_HAS_VA_OPT_I(C23)
+#define C23_HAS_VA_OPT_I(...) \
+ C23_HAS_VA_OPT_IIplus(__VA_OPT__(,) 1, 0,)
+#define C23_HAS_VA_OPT_IIplus(_0, _1, ...) _1
+
+#if !__has_va_opt
+# ifndef C23_FALLBACK_SILENT
+# warning "no support of C23 __VA_OPT__ found"
+# endif
+#endif
+
+#ifndef BITINT_MAXWIDTH
+# ifdef __BITINT_MAXWIDTH__
+# define BITINT_MAXWIDTH __BITINT_MAXWIDTH__
+# else
+# ifndef C23_FALLBACK_SILENT
+# warning "no support for _BitInt types found"
+# endif
+# endif
+#endif
+
+/* A simple type-generic conditional. */
+#define GENERIC_IF(COND, A, B) \
+ _Generic((char(*)[1+!!(COND)])0, \
+ char(*)[2]: (A), \
+ default: (B))
+
+/* clang has a set of feature tests that are quite comfortable,
+
+ __has_feature, to test if a specific feature is implemented
+
+ __has_extension, to test if a specific feature is implemented
+
+ __is_identifier, to test if a word is an identifier or a keyword.
+
+ In the following we construct similar tests for gcc such that we
+ may use these features independently from the C standard version
+ that is supported. */
+# define __tester2(_0, _1, ...) _1
+# define __tester1(...) __tester2(__VA_ARGS__)
+#ifndef __has_feature
+# define __has_feature(X) __tester1(__has_feature_ ## X 1, 0, )
+#endif
+#ifndef __has_extension
+# define __has_extension(X) __tester1(__has_extension_ ## X 1, 0, )
+#endif
+
+#ifndef __is_identifier
+# define __is_identifier(X) __tester1(__is_special_ ## X 0, 1, )
+# define __is_special_auto ,
+# define __is_special_break ,
+# define __is_special_case ,
+# define __is_special_char ,
+# define __is_special_const ,
+# define __is_special_continue ,
+# define __is_special_default ,
+# define __is_special_do ,
+# define __is_special_double ,
+# define __is_special_else ,
+# define __is_special_enum ,
+# define __is_special_extern ,
+# define __is_special_float ,
+# define __is_special_for ,
+# define __is_special_goto ,
+# define __is_special_if ,
+# define __is_special_inline ,
+# define __is_special_int ,
+# define __is_special_long ,
+# define __is_special_register ,
+# define __is_special_restrict ,
+# define __is_special_return ,
+# define __is_special_short ,
+# define __is_special_signed ,
+# define __is_special_sizeof ,
+# define __is_special_static ,
+# define __is_special_struct ,
+# define __is_special_switch ,
+# define __is_special_typedef ,
+# define __is_special_union ,
+# define __is_special_unsigned ,
+# define __is_special_void ,
+# define __is_special_volatile ,
+# define __is_special_while ,
+# define __is_special__Atomic ,
+# define __is_special__Complex ,
+# define __is_special__Decimal128 ,
+# define __is_special__Decimal32 ,
+# define __is_special__Decimal64 ,
+# define __is_special__Generic ,
+# define __is_special__Imaginary ,
+# define __is_special__Noreturn ,
+# if __GNUC__ > 12 || __STDC_VERSION__ >= 202311L
+# define __is_special_nullptr ,
+# define __is_special_static_assert ,
+# define __is_special_thread_local ,
+# define __is_special_constexpr ,
+# define __is_special_typeof ,
+# define __is_special_typeof_unqual ,
+# endif
+# if __GNUC__ > 13 || __STDC_VERSION__ >= 202311L
+# define __is_special__BitInt ,
+# define __is_special_alignas ,
+# define __is_special_alignof ,
+# define __is_special_bool ,
+# define __is_special_false ,
+# define __is_special_true ,
+# endif
+#endif
+#if __GNUC__ > 12 || __STDC_VERSION__ >= 202311L
+# define __has_feature_c_fixed_enum ,
+#endif
+
+// The feature for fixed underlying types for enums has been so long
+// in clang, that they don't even seem to have a feature test for it.
+#if !__has_feature(c_fixed_enum) && defined(__clang_major__) && (__clang_major__ < 8)
+# ifndef C23_FALLBACK_SILENT
+# warning "syntax for fixed underlying integer type of enumerations is not supported"
+# endif
+#endif
+
+#if __is_identifier(constexpr)
+# ifndef C23_FALLBACK_SILENT
+# warning "constexpr keyword is not supported, emulating as static const"
+# endif
+# define constexpr static const
+#endif
+
+#if __is_identifier(bool)
+# ifndef C23_FALLBACK_SILENT
+# warning "bool keyword is not supported, emulating as macro"
+# endif
+# define bool _Bool
+#endif
+#if __is_identifier(false)
+# ifndef C23_FALLBACK_SILENT
+# warning "false keyword is not supported, emulating as macro"
+# endif
+# undef false
+# define false ((bool)+0U)
+#endif
+#if __is_identifier(true)
+# ifndef C23_FALLBACK_SILENT
+# warning "true keyword is not supported, emulating as macro"
+# endif
+# undef true
+# define true ((bool)+1U)
+#endif
+
+#if __is_identifier(thread_local)
+// Maybe this is also provided by the C library implementation, but a
+// redefinition makes no harm.
+#define thread_local _Thread_local
+#endif
+
+#if __is_identifier(alignof)
+#undef alignof
+#define alignof _Alignof
+#endif
+
+#if __is_identifier(alignas)
+#undef alignas
+#define alignas _Alignas
+#endif
+
+#if __is_identifier(static_assert)
+#undef static_assert
+#define static_assert(...) __static_assert(__VA_ARGS__,)
+#define __static_assert(E, ...) _Static_assert(E)
+#endif
+
+#if __is_identifier(typeof)
+# if __GNUC__
+# undef typeof
+# define typeof __typeof__
+# else
+# warning "typeof operator only comes with C23"
+# endif
+#endif
+
+#if __STDC_VERSION__ < 202311L
+# if __GNUC__
+# if __GNUC__ < 13
+# define auto __auto_type
+# endif
+# else
+# warning "auto type inference only comes with C23"
+# endif
+#endif
+
+#ifndef __has_c_attribute
+# define __has_c_attribute(X) 0
+#endif
+
+// C23 has nullptr, use a fallback if that is not available. Here we
+// use an enumeration type and constant to do that. The idea is that
+// such an enumeration may then be used in _Generic as long the cases
+// otherwise only have pointer types. The first choice is to use a
+// _BitInt type for that, if that is already available, in an effort
+// to not clash with standard integer types. If that is not found, we
+// use a standard integer type that has the right width.
+//
+// If possible, we also use an attribute that forces warnings if any
+// other value than a null or 0 is assigned to a variable of type
+// nullptr_t.
+#if !__is_identifier(nullptr)
+// Maybe this is also provided by the C library implementation, but a
+// redefinition makes no harm.
+typedef typeof(nullptr) nullptr_t;
+#elif (__STC_VERSION__ < 202311L)
+# ifdef BITINT_MAXWIDTH
+
+# ifndef C23_FALLBACK_SILENT
+# warning "using fallback for nullptr to enumeration constant based on _BitInt type"
+# else
+# pragma GCC diagnostic ignored "-Wnon-literal-null-conversion"
+# endif
+
+enum
+# if __has_c_attribute(clang::enum_extensibility)
+[[__clang__::__enum_extensibility__(closed)]]
+# endif
+nullptr_t :
+__typeof__((unsigned _BitInt(sizeof(void*)*CHAR_BIT))0)
+{
+ nullptr,
+};
+
+# else
+
+# ifndef C23_FALLBACK_SILENT
+# warning "using fallback for nullptr to enumeration constant based on standard integer type"
+# endif
+
+enum
+# if __has_c_attribute(clang::enum_extensibility)
+[[__clang__::__enum_extensibility__(closed)]]
+# endif
+nullptr_t {
+ nullptr,
+ __nullptr_max =
+ GENERIC_IF((sizeof(void*)==sizeof(int)),
+ -1U,
+ GENERIC_IF((sizeof(void*)==sizeof(long)),
+ -1UL,
+ -1ULL)),
+};
+
+// Unfortunately such an enumeration type is not yet good enough for
+// all compilers. In particular some gcc version has nullptr and
+// __nullptr_max as different types. Force an expression that is an
+// ICE of value 0 and of the enumeration type.
+# define nullptr (1 ? nullptr : __nullptr_max)
+# endif
+#endif
+typedef typeof(nullptr) nullptr_t;
+
+static_assert(_Generic(nullptr, nullptr_t: true));
+static_assert(sizeof(void*)==sizeof(nullptr));
+static_assert(sizeof(void*)==sizeof(nullptr_t));
+
+/**********************************************************************************************/
+
+#include <inttypes.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdalign.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <wchar.h>
+
+/**********************************************************************************************/
+
+// for call_once
+#ifndef ONCE_FLAG_INIT
+# ifdef __has_include
+# if __has_include(<threads.h>)
+# ifndef C23_FALLBACK_SILENT
+# warning "including call_once function from <threads.h> instead of <stdlib.h>"
+# endif
+# include <threads.h>
+# endif
+# endif
+#endif
+
+// If call_once has not been found we construct a function that is
+// based on atomics.
+#ifndef ONCE_FLAG_INIT
+# ifdef __has_include
+# if __has_include(<stdatomic.h>)
+# include <stdatomic.h>
+# ifndef C23_FALLBACK_SILENT
+# warning "emulating call_once function by using atomics"
+# endif
+enum { once_initial, once_inter, once_final, };
+# define ONCE_FLAG_INIT once_initial
+
+typedef _Atomic(unsigned) once_flag;
+
+# if __has_c_attribute(__gnu__::__noinline__)
+[[__gnu__::__noinline__]]
+# endif
+static inline
+void call_once(once_flag* flag, void callback(void)) {
+ if (*flag != once_final) {
+ unsigned expected = once_initial;
+ if (atomic_compare_exchange_strong(flag, &expected, once_inter)) {
+ callback();
+ *flag = once_final;
+ } else {
+ while (*flag != once_final) {
+ //
+ }
+ }
+ } else {
+ // We know already that the implementation does not support threads,
+ // so the only available fence is this:
+ atomic_signal_fence(memory_order_seq_cst);
+ }
+}
+# endif
+# endif
+#endif
+
+/**********************************************************************************************/
+
+// We assume that the compiler is already able to handle the new
+// attribute syntax such as [[deprecated]]. Nevertheless, maybe
+// certain attributes themselves might not yet be implemented. We
+// provide fallback macros to corresponding gnu attributes. Here we
+// use the "safe" names for these features with double underscores, so
+// they don't interfere with application names.
+
+#if !__has_c_attribute(__unsequenced__)
+# ifndef C23_FALLBACK_SILENT
+# warning "unsequenced attribute not yet implemented"
+# endif
+# define __unsequenced__
+# if __has_c_attribute(__gnu__::__const__)
+# ifndef C23_FALLBACK_SILENT
+# warning "unsequenced attribute could be emulated via gnu attribute by using c23_unsequenced"
+# endif
+# define c23_unsequenced __attribute__((__const__))
+# endif
+#endif
+
+#if !__has_c_attribute(__reproducible__)
+# ifndef C23_FALLBACK_SILENT
+# warning "reproducible attribute not yet implemented"
+# endif
+# define __reproducible__
+# if __has_c_attribute(__gnu__::__pure__)
+# ifndef C23_FALLBACK_SILENT
+# warning "reproducible attribute could be emulated via gnu attribute by using c23_reproducible"
+# endif
+# define c23_reproducible __attribute__((__pure__))
+# endif
+#endif
+
+#if !__has_c_attribute(__deprecated__) && __has_c_attribute(__gnu__::__deprecated__)
+# ifndef C23_FALLBACK_SILENT
+# warning "emulating deprecated attribute via gnu attribute"
+# endif
+# define __deprecated__ __gnu__::__deprecated__
+#endif
+
+#if !__has_c_attribute(__nodiscard__) && __has_c_attribute(__gnu__::__warn_unused_result__)
+# ifndef C23_FALLBACK_SILENT
+# warning "emulating nodiscard attribute via gnu attribute"
+# endif
+# define __nodiscard__ __gnu__::__warn_unused_result__
+#endif
+
+#if !__has_c_attribute(__maybe_unused__) && __has_c_attribute(__gnu__::__unused__)
+# ifndef C23_FALLBACK_SILENT
+# warning "emulating maybe_unused attribute via gnu attribute"
+# endif
+# define __maybe_unused__ __gnu__::__unused__
+#endif
+
+#if !__has_c_attribute(noreturn) && __has_c_attribute(__gnu__::__noreturn__)
+# ifndef C23_FALLBACK_SILENT
+# warning "emulating noreturn attribute via gnu attribute"
+# endif
+# define noreturn __gnu__::__noreturn__
+#endif
+
+#if !__has_c_attribute(__fallthrough__) && __has_c_attribute(__gnu__::__fallthrough__)
+# warning "emulating fallthrough attribute via gnu attribute"
+# define __fallthrough__ __gnu__::__fallthrough__
+#endif
+
+#if __has_c_attribute(__deprecated__) || defined(__deprecated__)
+// Deprecate two interfaces in <time.h>
+[[__deprecated__]] char *asctime(const struct tm*);
+[[__deprecated__]] char *ctime(const time_t*);
+#endif
+
+// Gnu compilers have a malloc attribute that comes in two different
+// forms. Without argument it just certifies that a function returns a
+// pointer value that has not been seen before, and thus the pointer
+// target will not alias with anything known. The second form receives
+// one or two arguments and specifies a function that should be used
+// analogous to free. Unfortunately, clang didn't follow that second
+// development, yet. But we suppose that there will be at least a
+// partial solution in clang-18 final.
+#if __has_c_attribute(__gnu__::__malloc__) && !(defined(__clang_major__) && __clang_major__ <= 19)
+# define __gnu_free__(...) __gnu__::__malloc__(__VA_ARGS__)
+#else
+# define __gnu_free__(...)
+#endif
+
+/**********************************************************************************************/
+
+/* C23 allows to omit the second argument of va_start */
+#if __STDC_VERSION_STDARG_H__ < 202311L
+#undef va_start
+#define va_start(...) __va_start1(__VA_ARGS__ , 0, )
+#define __va_start1(...) __va_start2(__VA_ARGS__)
+#define __va_start2(v, l, ...) __builtin_va_start(v, l)
+#endif
+
+/**********************************************************************************************/
+
+/* C23 has three new tg interfaces in the new <stdkdint.h> header.
+They are modeled after similar gcc features. They are meant to do
+arithmetic with overflow check by using everything the compiler can
+get from instruction flags that already exist on most CPU.
+*/
+
+#ifdef __has_include
+# if __has_include(<stdckdint.h>)
+# include <stdckdint.h>
+# endif
+#endif
+
+/* If we don't have the header, yet, we may easily emulate it if we
+are on a compiler claiming compatibility with gcc. */
+#ifndef ckd_add
+# ifdef __GNUC__
+# ifndef C23_FALLBACK_SILENT
+# warning "emulating stdckdint.h interfaces via gnu builtins"
+# endif
+# define ckd_add(R, A, B) __builtin_add_overflow ((A), (B), (R))
+# define ckd_sub(R, A, B) __builtin_sub_overflow ((A), (B), (R))
+# define ckd_mul(R, A, B) __builtin_mul_overflow ((A), (B), (R))
+# else
+# warning "checked integer arithmetic as of C23 is not yet implemented"
+# endif
+#endif
+
+/**********************************************************************************************/
+
+// C23 has the unreachable macro in <stdlib.h>, use a fallback if that
+// is not available
+#ifndef unreachable
+# ifndef C23_FALLBACK_SILENT
+# warning "using fallback for unreachable macro"
+# endif
+# define unreachable() __builtin_unreachable()
+#endif
+
+/**********************************************************************************************/
+
+// C23 provides macros for the width of integer types. Provide a
+// fallback if they are not available
+
+#ifndef BOOL_WIDTH
+# define BOOL_WIDTH 1
+#endif
+
+#ifndef CHAR_WIDTH
+# define CHAR_WIDTH CHAR_BIT
+#endif
+
+#ifndef UCHAR_WIDTH
+# define UCHAR_WIDTH CHAR_BIT
+#endif
+
+#ifndef SCHAR_WIDTH
+# define SCHAR_WIDTH CHAR_BIT
+#endif
+
+#ifndef USHRT_WIDTH
+# if USHRT_MAX <= 0xFFFFU
+# define USHRT_WIDTH 16
+# define SHRT_WIDTH 16
+# else
+# if USHRT_MAX <= 0xFFFFFFFFU
+# define USHRT_WIDTH 32
+# define SHRT_WIDTH 32
+# else
+# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFU
+# define USHRT_WIDTH 64
+# define SHRT_WIDTH 64
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU
+# define USHRT_WIDTH 96
+# define SHRT_WIDTH 96
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if USHRT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU
+# define USHRT_WIDTH 128
+# define SHRT_WIDTH 128
+# endif
+# endif
+# endif
+# endif
+# endif
+#endif
+
+// unsigned int has a width of at least 16.
+#ifndef UINT_WIDTH
+# if UINT_MAX <= 0xFFFFU
+# define UINT_WIDTH 16
+# define INT_WIDTH 16
+# else
+# if UINT_MAX <= 0xFFFFFFFFU
+# define UINT_WIDTH 32
+# define INT_WIDTH 32
+# else
+# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFU
+# define UINT_WIDTH 64
+# define INT_WIDTH 64
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU
+# define UINT_WIDTH 96
+# define INT_WIDTH 96
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if UINT_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU
+# define UINT_WIDTH 128
+# define INT_WIDTH 128
+# endif
+# endif
+# endif
+# endif
+# endif
+#endif
+
+// size_t has a width of at least 16.
+#ifndef SIZE_WIDTH
+# if SIZE_MAX <= 0xFFFFU
+# define SIZE_WIDTH 16
+# else
+# if SIZE_MAX <= 0xFFFFFFFFU
+# define SIZE_WIDTH 32
+# else
+# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFU
+# define SIZE_WIDTH 64
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFU
+# define SIZE_WIDTH 96
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if SIZE_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFU
+# define SIZE_WIDTH 128
+# endif
+# endif
+# endif
+# endif
+# endif
+#endif
+
+// unsigned long has a width of at least 32
+#ifndef ULONG_WIDTH
+# if ULONG_MAX <= 0xFFFFFFFFUL
+# define ULONG_WIDTH 32
+# define LONG_WIDTH 32
+# else
+# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFUL
+# define ULONG_WIDTH 64
+# define LONG_WIDTH 64
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFUL
+# define ULONG_WIDTH 96
+# define LONG_WIDTH 96
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if ULONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFUL
+# define ULONG_WIDTH 128
+# define LONG_WIDTH 128
+# endif
+# endif
+# endif
+# endif
+#endif
+
+// unsigned long long has a width of at least 64.
+#ifndef ULLONG_WIDTH
+# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFULL
+# define ULLONG_WIDTH 64
+# define LLONG_WIDTH 64
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFULL
+# define ULLONG_WIDTH 96
+# define LLONG_WIDTH 96
+# else
+// This doesn't use #elif but nested #if/#else because the value might
+// be too large for the preprocessor
+# if ULLONG_MAX <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFULL
+# define ULLONG_WIDTH 128
+# define LLONG_WIDTH 128
+# endif
+# endif
+# endif
+#endif
+
+// In most cases the [u]intmax_t types will just be the long long
+// types.
+#ifndef UINTMAX_WIDTH
+# if UINTMAX_MAX == ULLONG_MAX
+# define UINTMAX_WIDTH ULLONG_WIDTH
+# define INTMAX_WIDTH LLONG_WIDTH
+# endif
+#endif
+
+#ifndef UINT128_MAX
+# ifdef __SIZEOF_INT128__
+# if defined(__clang_major__) && (__clang_major__ < 18)
+# ifndef C23_FALLBACK_SILENT
+# warning "not using compiler intrinsic for int128_t, please upgrade to clang version ≥ 18"
+# endif
+# else
+# define __has_int128
+# endif
+# endif
+#endif
+
+#ifndef UINT256_MAX
+# ifdef __SIZEOF_INT256__
+# if defined(__clang_major__) && (__clang_major__ < 18)
+# ifndef C23_FALLBACK_SILENT
+# warning "not using compiler intrinsic for int256_t, please upgrade to clang version ≥ 18"
+# endif
+# else
+# define __has_int256
+# endif
+# endif
+#endif
+
+// In C23 uint128_t may exist, even if it is wider than
+// uintmax_t. Provide it if the compiler supports it.
+#ifndef UINT128_MAX
+# ifdef __has_int128
+typedef signed __int128 int128_t;
+typedef unsigned __int128 uint128_t;
+# define UINT128_WIDTH 128
+# define INT128_WIDTH 128
+# define UINT128_MAX ((uint128_t)-1)
+# define INT128_MAX ((int128_t)(((uint128_t)-1)>>1))
+# define INT128_MIN (-INT128_MAX-1)
+# if BITINT_MAXWIDTH >= 128
+# define UINT128_C(X) ((uint128_t)+(X ## wbu))
+# define INT128_C(X) ((int128_t)+(X ## wb))
+# endif
+# endif
+#endif
+
+// In C23 uint256_t may exist, even if it is wider than
+// uintmax_t. Provide it if the compiler supports it.
+#ifndef UINT256_MAX
+# ifdef __has_int256
+typedef signed __int256 int256_t;
+typedef unsigned __int256 uint256_t;
+# define UINT256_WIDTH 256
+# define INT256_WIDTH 256
+# define UINT256_MAX ((uint256_t)-1)
+# define INT256_MAX ((int256_t)(((uint256_t)-1)>>1))
+# define INT256_MIN (-INT256_MAX-1)
+# if BITINT_MAXWIDTH >= 256
+# define UINT256_C(X) ((uint256_t)+(X ## wbu))
+# define INT256_C(X) ((int256_t)+(X ## wb))
+# endif
+# endif
+#endif
+
+/**********************************************************************************************/
+
+#if __STDC_VERSION_INTTYPES_H__ < 202311L
+# ifndef C23_FALLBACK_SILENT
+# warning "native C23 library support for %b printf or scanf formats may be missing"
+# endif
+# ifndef PRIb32
+# if __GLIBC__ > 1 && defined(__PRI64_PREFIX)
+# ifndef C23_FALLBACK_SILENT
+# warning "no C23 support for %b printf formats found, emulating with glibc features"
+# endif
+# define PRIB16 "B"
+# define PRIB32 "B"
+# define PRIB64 __PRI64_PREFIX "B"
+# define PRIB8 "B"
+# define PRIBFAST16 __PRIPTR_PREFIX "B"
+# define PRIBFAST32 __PRIPTR_PREFIX "B"
+# define PRIBFAST64 __PRI64_PREFIX "B"
+# define PRIBFAST8 "B"
+# define PRIBLEAST16 "B"
+# define PRIBLEAST32 "B"
+# define PRIBLEAST64 __PRI64_PREFIX "B"
+# define PRIBLEAST8 "B"
+# define PRIBMAX __PRI64_PREFIX "B"
+# define PRIBPTR __PRIPTR_PREFIX "B"
+# define PRIb16 "b"
+# define PRIb32 "b"
+# define PRIb64 __PRI64_PREFIX "b"
+# define PRIb8 "b"
+# define PRIbFAST16 __PRIPTR_PREFIX "b"
+# define PRIbFAST32 __PRIPTR_PREFIX "b"
+# define PRIbFAST64 __PRI64_PREFIX "b"
+# define PRIbFAST8 "b"
+# define PRIbLEAST16 "b"
+# define PRIbLEAST32 "b"
+# define PRIbLEAST64 __PRI64_PREFIX "b"
+# define PRIbLEAST8 "b"
+# define PRIbMAX __PRI64_PREFIX "b"
+# define PRIbPTR __PRIPTR_PREFIX "b"
+# define SCNb16 "hb"
+# define SCNb32 "b"
+# define SCNb64 __PRI64_PREFIX "b"
+# define SCNb8 "hhb"
+# define SCNbFAST16 __PRIPTR_PREFIX "b"
+# define SCNbFAST32 __PRIPTR_PREFIX "b"
+# define SCNbFAST64 __PRI64_PREFIX "b"
+# define SCNbFAST8 "hhb"
+# define SCNbLEAST16 "hb"
+# define SCNbLEAST32 "b"
+# define SCNbLEAST64 __PRI64_PREFIX "b"
+# define SCNbLEAST8 "hhb"
+# define SCNbMAX __PRI64_PREFIX "b"
+# define SCNbPTR __PRIPTR_PREFIX "b"
+# else
+# ifndef C23_FALLBACK_SILENT
+# warning "no C23 support for %b printf formats found"
+# endif
+# endif
+# endif
+#endif
+
+/**********************************************************************************************/
+
+// Complex arithmetic without <complex.h> header
+#ifndef __STDC_NO_COMPLEX__
+
+// gcc and related compilers implement an "I" or "i" suffix for
+// floating point constants since ages.
+# ifdef __GNUC__
+# define I (__extension__ 1.0IF)
+# define _COMPLEX_I (0.0F + I)
+
+// Otherwise use the new constexpr construct to produce a named
+// constant of complex type.
+# else
+typedef union _C23_complex _C23_complex;
+union _C23_complex {
+ // These two are guaranteed to have the same representation.
+ float _C23_v[2];
+ _Complex float _C23_c;
+};
+// Use static const. Best would be to have constexpr, but using a
+// different member than was initialized is not defined for them,
+// unfortunately.
+static _C23_complex const _C23_I = { { 0.0F, 1.0F, }, };
+# define _COMPLEX_I (_C23_I._C23_c)
+# endif
+
+#define iscomplex(X) (sizeof(1.0F*(X)) == sizeof(1.0F*(X)+_COMPLEX_I))
+#define isimaginary(X) (sizeof(1.0F*(X)+0.0F) == 2*sizeof(1.0F*(X)))
+
+# define _C23_float_Complex_CASE float _Complex: true,
+# define _C23_double_Complex_CASE double _Complex: true,
+# define _C23_long_double_Complex_CASE long double _Complex: true,
+#else
+# define _C23_float_Complex_CASE
+# define _C23_double_Complex_CASE
+# define _C23_long_double_Complex_CASE
+#endif
+
+/**********************************************************************************************/
+
+// Testing for presence of decimal floating types
+
+#if defined(__DEC32_MAX__)
+# define _C23_Decimal32_CASE _Decimal32: true,
+#else
+# define _C23_Decimal32_CASE
+#endif
+
+#if defined(__DEC64_MAX__)
+# define _C23_Decimal64_CASE _Decimal64: true,
+#else
+# define _C23_Decimal64_CASE
+#endif
+
+#if defined(__DEC128_MAX__)
+# define _C23_Decimal128_CASE _Decimal128: true,
+#else
+# define _C23_Decimal128_CASE
+#endif
+
+#define isdecimalfloating(...) (_Generic((__VA_ARGS__)+0, _C23_Decimal32_CASE _C23_Decimal64_CASE _C23_Decimal128_CASE default: false))
+#ifndef iscomplex
+# define iscomplex(...) (_Generic((__VA_ARGS__)+0, _C23_float_Complex_CASE _C23_double_Complex_CASE _C23_long_double_Complex_CASE default: false))
+#endif
+#define isstandardrealfloating(...) (_Generic((__VA_ARGS__)+0, float: true, double: true, long double: true, default: false))
+#define isstandardfloating(...) ((bool)(isstandardrealfloating(__VA_ARGS__)||iscomplex(__VA_ARGS__)))
+#ifndef isfloating
+# define isfloating(...) ((bool)(isstandardfloating(__VA_ARGS__)||isdecimalfloating(__VA_ARGS__)))
+#endif
+
+/**********************************************************************************************/
+
+// various tg-conversions and type traits
+
+#ifndef totype
+# define totype(Y, ...) ((typeof(__VA_ARGS__))Y)
+#endif
+#ifndef tominusone
+# define tominusone(...) totype(-1, __VA_ARGS__)
+#endif
+#ifndef tozero
+# define tozero(...) totype(0, __VA_ARGS__)
+#endif
+#ifndef toone
+# define toone(...) totype(1, __VA_ARGS__)
+#endif
+#ifndef iscompatible
+# define iscompatible(X, Y) (_Generic((X), typeof(Y): true, default: false))
+#endif
+#ifndef is_potentially_negative
+# define is_potentially_negative(...) (tominusone(__VA_ARGS__) < 0)
+#endif
+#ifndef is_const_target
+# define is_const_target(...) (_Generic((1 ? (__VA_ARGS__) : (void*)1), void const*: true, void const volatile*: true, default: false))
+#endif
+#ifndef is_volatile_target
+# define is_volatile_target(...) (_Generic((1 ? (__VA_ARGS__) : (void*)1), void volatile*: true, void const volatile*: true, default: false))
+#endif
+#ifndef is_const
+# define is_const(...) is_const_target(&(typeof(__VA_ARGS__)){ 0 })
+#endif
+#ifndef is_volatile
+# define is_volatile(...) is_volatile_target(&(typeof(__VA_ARGS__)){ 0 })
+#endif
+#ifndef is_null_pointer_constant
+struct do_not_use_this_otherwise;
+# define is_null_pointer_constant(...) \
+ (_Generic((1 ? (struct do_not_use_this_otherwise*)nullptr : (__VA_ARGS__)), \
+ struct do_not_use_this_otherwise*: true, \
+ default: false))
+#endif
+#ifndef is_zero_ice
+# define is_zero_ice(...) is_null_pointer_constant((void*)(uintptr_t)(!!(__VA_ARGS__)))
+#endif
+#ifndef isinteger
+# define isinteger(...) is_zero_ice(tozero(__VA_ARGS__))
+#endif
+#ifndef issigned
+# define issigned(...) ((bool)(is_potentially_negative(__VA_ARGS__) && isinteger(__VA_ARGS__) && !iscompatible(__VA_ARGS__, char)))
+#endif
+#ifndef isunsigned
+# define isunsigned(...) ((bool)(!is_potentially_negative(__VA_ARGS__) && isinteger(__VA_ARGS__) && !iscompatible(__VA_ARGS__, char)))
+#endif
+#ifndef isice
+# define isice(...) is_zero_ice(!((__VA_ARGS__) || 1))
+#endif
+#ifndef isvla
+# define isvla(...) ((bool)!isice(sizeof(__VA_ARGS__)))
+#endif
+#ifndef isxwide
+# define isxwide(...) \
+ ((bool)( \
+ isinteger(__VA_ARGS__) \
+ &&_Generic((__VA_ARGS__)+0ULL, \
+ unsigned long long: false, \
+ default: true)))
+#endif
+
+#ifndef is_pointer
+struct do_not_use_this_otherwise { char c; };
+# define get_fla(...) \
+ GENERIC_IF(isvla(__VA_ARGS__), \
+ (struct do_not_use_this_otherwise[1]){ 0 }, \
+ (__VA_ARGS__))
+# define is_pointer_nvla(...) \
+ (_Generic((typeof(__VA_ARGS__)*)0, \
+ typeof(get_fla(*(__VA_ARGS__)))**: true, \
+ default: false))
+# define is_pointer_vla(...) \
+ (_Generic((typeof(get_fla(*(__VA_ARGS__)))*)0, \
+ typeof(struct do_not_use_this_otherwise[1])*: true, \
+ default: false))
+# define is_pointer(...) ((bool)(is_pointer_nvla(__VA_ARGS__)||is_pointer_vla(__VA_ARGS__)))
+#endif
+#ifndef is_array
+# define is_array(...) ((bool)!is_pointer(__VA_ARGS__))
+#endif
+#ifndef is_fla
+# define is_fla(...) ((bool)(is_array(__VA_ARGS__)&&!isvla(__VA_ARGS__)))
+#endif
+
+#ifndef is_void_pointer
+# define is_void_pointer(...) \
+ _Generic((typeof(*(__VA_ARGS__))const volatile*)nullptr, \
+ void const volatile*: true, \
+ default: false)
+#endif
+
+/**********************************************************************************************/
+
+/* Const preserving functions as of C23. */
+
+/* These are not exactly according to the book, because the return
+ type of the string functions could sometimes be a pointer to a void
+ type and not a character type.*/
+
+#if __STDC_VERSION_STRING_H__ < 202311L
+
+//QVoid *memchr(QVoid *s, int c, size_t n);
+# ifndef memchr
+# define memchr(S, C, N) ((typeof(1 ? S : (void*)1))memchr((S), (C), (N)))
+# endif
+
+// QChar *strchr(QChar *s, int c);
+# ifndef strchr
+# define strchr(S, C) ((typeof(S))strchr((S), (C)))
+# endif
+
+// QChar *strpbrk(QChar *s1, const char *s2);
+# ifndef strpbrk
+# define strpbrk(S1, S2) ((typeof(S1))strpbrk((S1), (S2)))
+# endif
+
+// QChar *strrchr(QChar *s, int c);
+# ifndef strrchr
+# define strrchr(S, C) ((typeof(S))strrchr((S), (C)))
+# endif
+
+// QChar *strstr(QChar *s1, const char *s2);
+# ifndef strstr
+# define strstr(S1, S2) ((typeof(S1))strstr((S1), (S2)))
+# endif
+
+#endif
+
+#if __STDC_VERSION_WCHAR_H__ < 202311L
+
+//QWchar *wmemchr(QWchar *s, int c, size_t n);
+# ifndef wmemchr
+# define wmemchr(S, C, N) ((typeof(S))wmemchr((S), (C), (N)))
+# endif
+
+// QWchar *wcschr(QWchar *s, wchar_t c);
+# ifndef wcschr
+# define wcschr(S, C) ((typeof(S))wcschr((S), (C)))
+# endif
+
+// QWchar *wcspbrk(QWchar *s1, const wchar_t *s2);
+# ifndef wcspbrk
+# define wcspbrk(S1, S2) ((typeof(S1))wcspbrk((S1), (S2)))
+# endif
+
+// QWchar *wcsrchr(QWchar *s, wchar_t c);
+# ifndef wcsrchr
+# define wcsrchr(S, C) ((typeof(S))wcsrchr((S), (C)))
+# endif
+
+// QWchar *wcswcs(QWchar *s1, const wchar_t *s2);
+# ifndef wcsstr
+# define wcsstr(S1, S2) ((typeof(S1))wcsstr((S1), (S2)))
+# endif
+
+#endif
+
+#if __STDC_VERSION_STDLIB_H__ < 202311L
+
+// QVoid *bsearch(const void *key, QVoid *base, size_t nmemb, size_t size,
+// int (*compar)(const void *, const void *));
+# ifndef bsearch
+# define bsearch(KEY, BASE, NMEMB, SIZE, COMPAR) ((typeof(1 ? (BASE) : (void*)1))bsearch((KEY), (BASE), (NMEMB), (SIZE), (COMPAR)))
+# endif
+
+#endif
+
+#endif
+
+/**********************************************************************************************/
+
+#ifdef __has_include
+# if __has_include(<stdbit.h>)
+# include <stdbit.h>
+# define __has_feature_c_stdbit_h ,
+# endif
+#endif
+
+#ifndef __STDC_ENDIAN_LITTLE__
+# ifdef __GNUC__
+# define __STDC_ENDIAN_LITTLE__ __ORDER_LITTLE_ENDIAN__
+# endif
+#endif
+
+#ifndef __STDC_ENDIAN_BIG__
+# ifdef __GNUC__
+# define __STDC_ENDIAN_BIG__ __ORDER_BIG_ENDIAN__
+# endif
+#endif
+
+#ifndef __STDC_ENDIAN_NATIVE__
+# ifdef __GNUC__
+# define __STDC_ENDIAN_NATIVE__ __BYTE_ORDER__
+# else
+# ifndef C23_FALLBACK_SILENT
+# warning "native endian could not be determined"
+# endif
+# endif
+#endif
+
+/**********************************************************************************************/
+
+/* This adds the 14 type-generic bit interfaces that are added by
+ C23. The use of the other type-specific interfaces is of less
+ interest.
+
+generic_return_type stdc_leading_zeros(generic_value_type value);
+generic_return_type stdc_leading_ones(generic_value_type value);
+generic_return_type stdc_trailing_zeros(generic_value_type value);
+generic_return_type stdc_trailing_ones(generic_value_type value);
+generic_return_type stdc_first_leading_zero(generic_value_type value);
+generic_return_type stdc_first_leading_one(generic_value_type value);
+generic_return_type stdc_first_trailing_zero(generic_value_type value);
+generic_return_type stdc_count_zeros(generic_value_type value);
+generic_return_type stdc_count_ones(generic_value_type value);
+bool stdc_has_single_bit(generic_value_type value);
+generic_return_type stdc_bit_width(generic_value_type value);
+generic_value_type stdc_bit_floor(generic_value_type value);
+generic_value_type stdc_bit_ceil(generic_value_type value);
+
+*/
+
+/*
+ An auxiliary macro that shifts right by ULLONG_WIDTH, regardless
+ whether the type has a width smaller than that. For most types this
+ will just be a zero of the same type.
+ */
+
+#define shift_xright(...) \
+ GENERIC_IF(isxwide(__VA_ARGS__), \
+ (__VA_ARGS__)>>GENERIC_IF(isxwide(__VA_ARGS__), \
+ ULLONG_WIDTH, \
+ 0), \
+ tozero(__VA_ARGS__))
+
+#ifndef stdc_count_ones
+# ifdef __GNUC__
+
+# define stdc_trailing_zeros(...) \
+ ({ \
+ auto s_t_z_x = (__VA_ARGS__); \
+ static_assert(isunsigned(s_t_z_x), "bit operation needs unsigned type"); \
+ (s_t_z_x) \
+ ? _Generic((s_t_z_x), \
+ bool: __builtin_ctz((unsigned)s_t_z_x), \
+ unsigned char: __builtin_ctz((unsigned)s_t_z_x), \
+ unsigned short: __builtin_ctz((unsigned)s_t_z_x), \
+ unsigned: __builtin_ctz(s_t_z_x), \
+ unsigned long: __builtin_ctzl(s_t_z_x), \
+ unsigned long long: __builtin_ctzll(s_t_z_x), \
+ default: ({ \
+ size_t s_t_z_ret = 0; \
+ while (s_t_z_x) { \
+ unsigned long long s_t_z_z = s_t_z_x; \
+ if (s_t_z_z) { \
+ s_t_z_ret += __builtin_ctzll(s_t_z_z); \
+ s_t_z_x = 0; \
+ } else { \
+ s_t_z_ret += ULLONG_WIDTH; \
+ s_t_z_x = shift_xright(s_t_z_x); \
+ } \
+ } \
+ s_t_z_ret; \
+ })) \
+ : stdc_count_ones(tominusone(s_t_z_x)); \
+ })
+
+/* This counts the least significant 1-bits of the specific type of the
+ argument. Therefor we complement all bits. To take narrow types
+ into account, that result has to be cast back to the original
+ type. */
+# define stdc_trailing_ones(...) stdc_trailing_zeros((typeof(__VA_ARGS__))~(__VA_ARGS__))
+
+# define stdc_leading_zeros(...) \
+ ({ \
+ auto s_l_o_x = (__VA_ARGS__); \
+ static_assert(isunsigned(s_l_o_x), "bit operation needs unsigned type"); \
+ (s_l_o_x) \
+ ? _Generic((s_l_o_x), \
+ unsigned char: __builtin_clz((unsigned)s_l_o_x) - __builtin_clz((unsigned)tominusone(s_l_o_x)), \
+ unsigned short: __builtin_clz((unsigned)s_l_o_x) - __builtin_clz((unsigned)tominusone(s_l_o_x)), \
+ unsigned: __builtin_clz(s_l_o_x), \
+ unsigned long: __builtin_clzl(s_l_o_x), \
+ unsigned long long: __builtin_clzll(s_l_o_x), \
+ default: \
+ ((!isxwide(s_l_o_x)) \
+ /* Should only trigger for _BitInt types. */ \
+ ? (__builtin_clzll(s_l_o_x) - __builtin_clzll(tominusone(s_l_o_x))) \
+ /* determine the long long word with highest 1-bit . */ \
+ : ({ \
+ size_t s_l_o_w = stdc_count_ones(tominusone(s_l_o_x)) - ULLONG_WIDTH; \
+ while ((s_l_o_x+0UL) > ULLONG_MAX) { \
+ s_l_o_x = shift_xright(s_l_o_x); \
+ s_l_o_w -= ULLONG_WIDTH; \
+ } \
+ __builtin_clzll(s_l_o_x) + s_l_o_w; \
+ }))) \
+ /* This should resolve to a compile time constant. */ \
+ : stdc_count_ones(tominusone(s_l_o_x)); \
+ })
+
+
+/* This counts the most significant 1-bits of the specific type of the
+ argument. Therefor we complement all bits. To take narrow types
+ into account, that result has to be cast back to the original
+ type. */
+# define stdc_leading_ones(...) stdc_leading_zeros((typeof(__VA_ARGS__))~(__VA_ARGS__))
+
+
+# define stdc_count_ones(...) \
+ _Generic((__VA_ARGS__), \
+ bool: __builtin_popcount((unsigned)(__VA_ARGS__)), \
+ unsigned char: __builtin_popcount((unsigned)(__VA_ARGS__)), \
+ unsigned short: __builtin_popcount((unsigned)(__VA_ARGS__)), \
+ unsigned: __builtin_popcount((unsigned)(__VA_ARGS__)), \
+ unsigned long: __builtin_popcountl((unsigned long)(__VA_ARGS__)), \
+ unsigned long long: __builtin_popcountll((unsigned long long)(__VA_ARGS__)), \
+ default: ({ \
+ auto s_c_o_x = (__VA_ARGS__); \
+ static_assert(isunsigned(s_c_o_x), "bit operation needs unsigned type"); \
+ size_t s_c_o_ret = 0; \
+ while (s_c_o_x) { \
+ s_c_o_ret += __builtin_popcountll((unsigned long long)s_c_o_x); \
+ s_c_o_x = shift_xright(s_c_o_x); \
+ } \
+ s_c_o_ret; \
+ }))
+
+
+/* This counts the 0-bits of the specific type of the
+ argument. Therefor we complement all bits. To take narrow types
+ into account, that result has to be cast back to the original
+ type. */
+# define stdc_count_zeros(...) stdc_count_ones((typeof(__VA_ARGS__))~(__VA_ARGS__))
+
+# define stdc_has_single_bit(...) \
+ ({ \
+ auto s_h_s_b_x = (__VA_ARGS__); \
+ static_assert(isunsigned(s_h_s_b_x), "bit operation needs unsigned type"); \
+ (bool)(s_h_s_b_x && !((s_h_s_b_x != 1) && (s_h_s_b_x & (s_h_s_b_x - 1)))); \
+ })
+
+# define stdc_bit_width(...) \
+ ({ \
+ auto s_b_w_x = (__VA_ARGS__); \
+ static_assert(isunsigned(s_b_w_x), "bit operation needs unsigned type"); \
+ stdc_count_ones(tominusone(s_b_w_x))-stdc_leading_zeros(s_b_w_x); \
+ })
+
+# define stdc_bit_floor(...) \
+ ({ \
+ auto s_b_f_x = (__VA_ARGS__); \
+ static_assert(isunsigned(s_b_f_x), "bit operation needs unsigned type"); \
+ (!s_b_f_x) \
+ ? 0 \
+ : (toone(s_b_f_x)<<(stdc_bit_width(s_b_f_x)-1)); \
+ })
+
+# define stdc_bit_ceil(...) \
+ (toone(__VA_ARGS__)<<stdc_bit_width(__VA_ARGS__))
+
+# endif
+#endif
diff --git a/cat.c b/cat.c
new file mode 100644
index 0000000..9baadb5
--- /dev/null
+++ b/cat.c
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+enum { buf_max = 32, };
+
+int main(int argc, char* argv[argc+1]) {
+ int ret = EXIT_FAILURE;
+ char buffer[buf_max] = { };
+ for (int i = 1; i < argc; ++i) { // Processes args
+ FILE* instream = fopen(argv[i], "r"); // as filenames
+ if (instream) {
+ while (fgets(buffer, buf_max, instream)) {
+ fputs(buffer, stdout);
+ }
+ fclose(instream);
+ ret = EXIT_SUCCESS;
+ } else {
+ /* Provides some error diagnostic. */
+ fprintf(stderr, "Could not open %s: ", argv[i]);
+ perror(0);
+ errno = 0; // Resets the error code
+ }
+ }
+ return ret;
+}
diff --git a/circular.c b/circular.c
new file mode 100644
index 0000000..e69bc77
--- /dev/null
+++ b/circular.c
@@ -0,0 +1,174 @@
+#include <stdlib.h>
+#include <string.h>
+#include "circular.h"
+
+circular* circular_new(size_t len);
+void circular_delete(circular* c);
+
+#if __GNUC__ > 4 && __GNUC__ <= 14
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
+[[deprecated("implementation")]]
+circular* circular_init(circular* c, size_t cap) {
+ if (c) {
+ if (cap) {
+ *c = (circular){
+ .cap = cap,
+ .tab = malloc(sizeof(double[cap])),
+ };
+ // Allocation failed.
+ if (!c->tab) c->cap = 0;
+ } else {
+ *c = (circular){ };
+ }
+ }
+ return c;
+}
+
+[[deprecated("implementation")]]
+void circular_destroy(circular* c) {
+ if (c) {
+ free(c->tab);
+ circular_init(c, 0);
+ }
+}
+
+[[deprecated("implementation")]]
+size_t circular_getlength(circular const* c) {
+ return c ? c->len : 0;
+}
+
+[[deprecated("implementation")]]
+static size_t circular_getpos(circular const c[static 1], size_t pos) {
+ if (c->cap) {
+ pos += c->start;
+ pos %= c->cap;
+ }
+ return pos;
+}
+
+[[deprecated("implementation")]]
+circular* circular_append(circular* c, double value) {
+ if (c) {
+ double* where = circular_element(c, c->len);
+ if (where) {
+ *where = value;
+ ++c->len;
+ return c;
+ }
+ }
+ return nullptr;
+}
+
+[[deprecated("implementation")]]
+double* circular_element(circular const* c, size_t pos) {
+ double* ret = nullptr;
+ if (c) {
+ if (pos < c->cap) {
+ pos = circular_getpos(c, pos);
+ ret = &c->tab[pos];
+ }
+ }
+ return ret;
+}
+
+[[deprecated("implementation")]]
+double circular_pop(circular* c) {
+ double ret = 0.0;
+ if (c && c->len) {
+ double* p = circular_element(c, 0);
+ if (p) ret = *p;
+ ++c->start;
+ --c->len;
+ }
+ return ret;
+}
+
+[[nodiscard("returned pointer replaces function argument")]]
+[[deprecated("implementation")]]
+circular* circular_resize(circular* c, size_t nlen) {
+ if (c) {
+ size_t len = c->len;
+ if (len > nlen) return nullptr;
+ size_t olen = c->cap;
+ if (nlen != olen) {
+ size_t ostart = circular_getpos(c, 0);
+ size_t nstart = ostart;
+ double* otab = c->tab;
+ double* ntab;
+ if (nlen > olen) {
+ ntab = realloc(c->tab, sizeof(double[nlen]));
+ if (!ntab) return nullptr;
+ // non-empty, and there were already two separate chunks
+ if (ostart+len > olen) {
+ size_t ulen = olen - ostart;
+ size_t llen = len - ulen;
+ if (llen <= (nlen - olen)) {
+ /* Copy the lower one up after the old end. */ /*@\label{cpy-lowchunk}*/
+ memcpy(ntab + olen, ntab,
+ llen*sizeof(double));
+ } else {
+ /* Move the upper one up to the new end. */ /*@\label{cpy-hichunk}*/
+ nstart = nlen - ulen;
+ memmove(ntab + nstart, ntab + ostart,
+ ulen*sizeof(double));
+ }
+ }
+ } else {
+ if (ostart+len > olen) {
+ // Two separate chunks; mv the upper one down to the new end.
+ size_t ulen = olen - ostart;
+ nstart = nlen - ulen;
+ memmove(otab + nstart, otab + ostart, ulen*sizeof(double));
+ } else {
+ // A single chunk
+ if (ostart + len > nlen) {
+ // Reallocation cuts the existing chunk in two.
+ memmove(otab, otab + ostart, len*sizeof(double));
+ nstart = 0;
+ }
+ }
+ if (nlen) {
+ // Now all data is saved in the conserved part of the array.
+ ntab = realloc(c->tab, sizeof(double[nlen]));
+ // If realloc fails in this case (would be weird), just overrule it.
+ if (!ntab) ntab = otab;
+ } else {
+ // There had been no elements and we were asked to reduce
+ // the buffer to 0 elements.
+ free(c->tab);
+ ntab = nullptr;
+ nstart = 0;
+ }
+ }
+ *c = (circular){
+ .cap = nlen,
+ .start = nstart,
+ .len = len,
+ .tab = ntab,
+ };
+ }
+ }
+ return c;
+}
+
+[[deprecated("implementation")]]
+void circular_fput(circular* c, FILE* s) {
+ if (c) {
+ size_t len = circular_getlength(c);
+ double* tab = c->tab;
+ if (tab) {
+ fprintf(s, "%p+%zu (%zu+%zu):", (void*)tab, c->cap, c->start, len);
+ for (size_t i = 0; i < len; ++i) {
+ double* elp = circular_element(c, i);
+ // We know that c is not null, and so elp never will be.
+ if (!elp) unreachable();
+ fprintf(s, "\t%g", *elp);
+ }
+ fputc('\n', s);
+ return;
+ }
+ }
+ fputs("invalid circular\n", s);
+}
diff --git a/circular.h b/circular.h
new file mode 100644
index 0000000..9631ed3
--- /dev/null
+++ b/circular.h
@@ -0,0 +1,123 @@
+#ifndef CIRCULAR_H
+# define CIRCULAR_H 1
+
+# include "c23-fallback.h"
+# include <stdio.h>
+
+typedef struct circular circular;
+
+/** @brief a type for a circular buffer for @c double values
+ **
+ ** This data structure allows to add @c double values in rear and to
+ ** take them out in front. Each such structure has a maximal amount
+ ** of elements that can be stored in it.
+ **
+ **/
+/*
+ *
+ * Direct access to all the elements is deprecated.
+ */
+struct circular {
+ size_t start [[deprecated("privat")]]; /* First element */
+ size_t len [[deprecated("privat")]]; /* Number of elements*/
+ size_t cap [[deprecated("privat")]]; /* Maximum capacity */
+ double* tab [[deprecated("privat")]]; /* Data array */
+};
+
+/**
+ ** @name Initialization and destruction
+ ** @{
+ **/
+
+/** @brief Initialize a circular buffer @a c with maximally @a
+ ** cap elements.
+ **
+ ** Only use this function on an uninitialized buffer.
+ **
+ ** Each buffer that is initialized with this function must be
+ ** destroyed with a call to ::circular_destroy.
+ **/
+circular* circular_init(circular* c, size_t cap);
+
+/** @brief Destroy circular buffer @a c
+ **
+ ** @a c must have been initialized with a call to ::circular_init
+ **/
+void circular_destroy(circular* c);
+
+/** @brief Delete circular buffer @a c
+ **
+ ** @a c must have been allocated with a call to ::circular_new
+ **/
+inline
+void circular_delete(circular* c) {
+ circular_destroy(c);
+ free(c);
+}
+/**
+ ** @}
+ **/
+
+/** @brief Allocate and initialize a circular buffer with maximally
+ ** @a len elements.
+ **
+ ** Each buffer that is allocated with this function must be deleted
+ ** with a call to ::circular_delete.
+ **/
+[[nodiscard("pointer to allocated data dropped")]]
+[[__gnu__::__malloc__, __gnu_free__(circular_delete)]]
+inline
+circular* circular_new(size_t len) {
+ return circular_init(malloc(sizeof(circular)), len);
+}
+
+/**
+ ** @name Using elements of a circular
+ ** @{
+ **/
+
+/** @brief Append a new element with value @a value to the buffer @a c
+ **
+ ** @return c if the new element could be appended, null otherwise.
+ **/
+circular* circular_append(circular* c, double value);
+
+/** @brief Remove the oldest element from @a c and return its value
+ **
+ ** @return the removed element if it exists, @c 0.0 otherwise.
+ **/
+double circular_pop(circular* c);
+
+/** @brief Return a pointer to position @a pos in buffer @a c
+ **
+ ** @return a pointer to element @a pos of the buffer, null otherwise.
+ **/
+double* circular_element(circular const* c, size_t pos);
+
+
+/**
+ ** @}
+ **/
+
+/**
+ ** @name Maintain a circular
+ ** @{
+ **/
+
+/** @brief Return the number of elements stored. */
+size_t circular_getlength(circular const* c);
+
+/** @brief Resize to capacity @a cap. */
+[[nodiscard("returned pointer replaces function argument")]]
+circular* circular_resize(circular* c, size_t cap);
+
+/** @brief Print the buffer values to stream @a s. */
+void circular_fput(circular* c, FILE* s);
+
+/**
+ ** @}
+ **/
+
+
+
+#endif
diff --git a/constexpr.c b/constexpr.c
new file mode 100644
index 0000000..81bd929
--- /dev/null
+++ b/constexpr.c
@@ -0,0 +1,29 @@
+#include "c23-fallback.h"
+#include <math.h>
+#include <stdio.h>
+
+#if __is_identifier(constexpr)
+# error "constexpr is needed for this test"
+// really stop it
+# include "really stop useless compilation right now"
+#endif
+
+
+#define eatit(P) printf("" #P ": %p\n", &(P))
+
+[[__maybe_unused__]] static constexpr size_t size_max = (size_t)-1;
+
+[[__maybe_unused__]] constexpr long double π = 3.141'592'653'589'793'238'46;
+[[__maybe_unused__]] constexpr long double π² = π*π;
+
+[[__maybe_unused__]] constexpr div_t one = { .quot = 1, .rem = 0, };
+
+[[__maybe_unused__]] constexpr double E𝟏[2][2] = { {1, 0,}, {0, 1,}, };
+
+int main(void) {
+ eatit(size_max);
+ eatit(π);
+ eatit(π²);
+ eatit(one);
+ eatit(E𝟏);
+}
diff --git a/crash.c b/crash.c
new file mode 100644
index 0000000..3a99d9e
--- /dev/null
+++ b/crash.c
@@ -0,0 +1,33 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+#include <stdalign.h>
+#include <inttypes.h>
+#include <complex.h>
+#include "crash.h"
+
+void enable_alignment_check(void);
+typedef complex double cdbl;
+
+int main(void) {
+ enable_alignment_check();
+ /* An overlay of complex values and bytes. */
+ union {
+ cdbl val[2];
+ unsigned char buf[sizeof(cdbl[2])];
+ } toocomplex = {
+ .val = { 0.5 + 0.5*I, 0.75 + 0.75*I, },
+ };
+ printf("size/alignment: %zu/%zu\n",
+ sizeof(cdbl), alignof(cdbl));
+ /* Run over all offsets, and crash on misalignment. */
+ for (size_t offset = sizeof(cdbl); offset; offset /=2) {
+ printf("offset\t%zu:\t", offset);
+ fflush(stdout);
+ cdbl* bp = (cdbl*)(&toocomplex.buf[offset]); // align!
+ printf("%g\t+%gI\t", creal(*bp), cimag(*bp));
+ fflush(stdout);
+ *bp *= *bp;
+ printf("%g\t+%gI", creal(*bp), cimag(*bp));
+ fputc('\n', stdout);
+ }
+}
diff --git a/crash.h b/crash.h
new file mode 100644
index 0000000..6f522b8
--- /dev/null
+++ b/crash.h
@@ -0,0 +1,37 @@
+#ifndef CRASH_H
+#define CRASH_H 1
+
+/**
+ ** @brief enable alignment check for i386 processors
+ **
+ ** Intel's i386 processor family is quite tolerant in accepting
+ ** misalignment of data. This can lead to irritating bugs when ported
+ ** to other architectures that are not as tolerant.
+ **
+ ** This function enables a check for this problem also for this
+ ** family or processors, such that you can be sure to detect this
+ ** problem early.
+ **
+ ** I found that code on Ygdrasil's blog:
+ ** http://orchistro.tistory.com/206
+ **/
+
+inline
+void enable_alignment_check(void) {
+#if defined(__GNUC__)
+# if defined(__x86_64__)
+ __asm__("pushf\n"
+ "\torl $0x40000, (%%rsp)\n"
+ "\tpopf"
+ : : : "cc");
+# elif defined(__i386__)
+ __asm__("pushf\n"
+ "\torl $0x40000, (%%esp)\n"
+ "\tpopf"
+ : : : "cc");
+# endif
+#endif
+}
+
+
+#endif
diff --git a/decimal-binary.c b/decimal-binary.c
new file mode 100644
index 0000000..921e9d7
--- /dev/null
+++ b/decimal-binary.c
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <float.h>
+
+int main(int argc, [[maybe_unused]] char* argv[argc+1]) {
+ // This 5²¹
+ unsigned long long m10 = 476837158203125;
+ _Decimal64 f10 = m10;
+ // Because many of the 5's can be canceled by the 10⁻¹⁵, this is
+ // exact
+ _Decimal64 a10 = 2.097'152E-15DD;
+ _Decimal64 p10 = a10*f10;
+ double f2 = m10;
+ // This is not an exact representation in binary floating types
+ double a2u = 2.097'152E-15;
+ double p2 = a2u*f2;
+ printf("value of (double)5²¹ as binary\t\t\t\t%a\n", f2);
+ printf("value of (double)5⁻²¹ as binary\t\t\t\t%a\n", a2u);
+ printf("value of (double)5²¹⋅(double)5⁻²¹ as binary\t\t%a\n", p2);
+ printf("value of 5²¹ as decimal integer\t\t\t\t%llu\n", m10);
+ printf("value of (double)5²¹ as decimal\t\t\t\t%.26f\n", f2);
+ printf("value of (_Decimal64)5²¹ as decimal\t\t\t476837158203125.00000000000000000000000000\n");
+ printf("value of (double)5⁻²¹ as decimal\t\t\t%.40e\n", a2u);
+ printf("value of (_Decimal64)5⁻²¹ as decimal\t\t\t2.0971520000000000000000000000000000000000e-15\n");
+ printf("value of (double)5²¹⋅(double)5⁻²¹ as decimal\t\t%.40e\n", p2);
+ printf("value of (_Decimal64)5²¹⋅(_Decimal64)5⁻²¹ as decimal\t%.40e\n", (double)p10);
+ printf("equality of roundtrip (_Decimal64)5⁻²¹ through binary:\t\t\t\t\t%s\n", ((_Decimal64)(double)a10) == a10 ? "yes" : "no");
+ printf("equality of roundtrip (double)5⁻²¹ through decimal:\t\t\t\t\t%s\n", ((double)(_Decimal64)a2u) == a2u ? "yes" : "no");
+ printf("equality of roundtrip (_Decimal64)5²¹ through binary:\t\t\t\t\t%s\n", ((_Decimal64)(double)f10) == f10 ? "yes" : "no");
+ printf("equality of roundtrip (double)5²¹ through decimal:\t\t\t\t\t%s\n", ((double)(_Decimal64)f2) == f2 ? "yes" : "no");
+ printf("equality of roundtrip (_Decimal64)5²¹⋅(_Decimal64)5⁻²¹ through binary:\t\t\t%s\n", ((_Decimal64)(double)p10) == p10 ? "yes" : "no");
+ printf("equality of roundtrip (double)5²¹⋅(double)5⁻²¹ through decimal:\t\t\t\t%s\n", ((double)(_Decimal64)p2) == p2 ? "yes" : "no");
+ printf("equality (_Decimal64)5⁻²¹ and (double)5⁻²¹ in binary:\t\t\t\t\t%s\n", ((double)a10) == a2u ? "yes" : "no");
+ printf("equality (_Decimal64)5⁻²¹ and (double)5⁻²¹ in decimal:\t\t\t\t\t%s\n", a10 == ((_Decimal64)a2u) ? "yes" : "no");
+ printf("equality (_Decimal64)5²¹ and (double)5²¹ in binary:\t\t\t\t\t%s\n", ((double)f10) == f2 ? "yes" : "no");
+ printf("equality (_Decimal64)5²¹ and (double)5²¹ in decimal:\t\t\t\t\t%s\n", f10 == ((_Decimal64)f2) ? "yes" : "no");
+ printf("equality (_Decimal64)5²¹⋅(_Decimal64)5⁻²¹ and (double)5²¹⋅(double)5⁻²¹ in binary:\t%s\n", ((double)p10) == p2 ? "yes" : "no");
+ printf("equality (_Decimal64)5²¹⋅(_Decimal64)5⁻²¹ and (double)5²¹⋅(double)5⁻²¹ in decimal:\t%s\n", p10 == ((_Decimal64)p2) ? "yes" : "no");
+}
diff --git a/embed.c b/embed.c
new file mode 100644
index 0000000..e874762
--- /dev/null
+++ b/embed.c
@@ -0,0 +1,57 @@
+/**
+ ** @brief A toy example for a usage of the #embed directive.
+ **
+ ** We just embed the source of this program into the executable and
+ ** print it.
+ **
+ ** If the #embed directive is entirely supported, the preprocessor
+ ** should do macro expansion of `__FILE__`. If the directory of the
+ ** source file is in the include path for embed.c, the source should
+ ** just be included like that.
+ **
+ ** If the directive is not supported you could try to compile this
+ ** anyhow by using https://sentido-labs.com/en/library/cedro/. Cedro
+ ** does not allow to do macro expansion for the argument of the
+ ** directive, so we have to give this explicitly.
+ **
+ ** For the example itself to output properly, the source and
+ ** execution encoding should not be too different.
+ **/
+
+#include "c23-fallback.h"
+#include <stdio.h>
+#include <string.h>
+
+// define a character array that will contain the entire
+// source file
+static char const here[] = {
+
+// Cedro does not work with blanks between the # and any directive
+#pragma Cedro 1.0 embed
+#embed "embed.c"
+
+};
+
+// define another character array that has the same size
+static char there[sizeof here];
+
+int main(int argc, char* argv[static argc+1]) {
+ size_t ibytes = 0;
+ int cmp = 1000;
+ // Open the file in binary mode.
+ FILE* inp = fopen(__FILE__, "rb");
+ if (inp) {
+ // read the file as binary
+ ibytes = fread(there, 1, sizeof there, inp);
+ cmp = memcmp(here, there, sizeof here);
+ fclose(inp);
+ } else {
+ printf("could not open %s\n", __FILE__);
+ printf("+++++++++++++++++++++++++++++++++++++++++++++++\n");
+ }
+ size_t obytes = fwrite(here, 1, sizeof here, stdout);
+ printf("+++++++++++++++++++++++++++++++++++++++++++++++\n");
+ printf("in %zu, out %zu, bytes are %s\n",
+ ibytes, obytes,
+ cmp < 0 ? "smaller" : (cmp > 0 ? "greater" : "equal"));
+}
diff --git a/endianness.c b/endianness.c
new file mode 100644
index 0000000..ec589d7
--- /dev/null
+++ b/endianness.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+#include <inttypes.h>
+
+typedef union unsignedInspect unsignedInspect;
+union unsignedInspect {
+ unsigned val;
+ unsigned char bytes[sizeof(unsigned)];
+};
+unsignedInspect twofold = { .val = 0xAABBCCDD, };
+
+int main(void) {
+ printf("value is 0x%.08X\n", twofold.val);
+ for (size_t i = 0; i < sizeof twofold.bytes; ++i)
+ printf("byte[%zu]: 0x%.02hhX\n", i, twofold.bytes[i]);
+ unsigned val = 0xAABBCCDD;
+ unsigned char* valp = (unsigned char*)&val;
+ for (size_t i = 0; i < sizeof val; ++i)
+ printf("byte[%zu]: 0x%.02hhX\n", i, valp[i]);
+}
diff --git a/enum.c b/enum.c
new file mode 100644
index 0000000..73c21a4
--- /dev/null
+++ b/enum.c
@@ -0,0 +1,93 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+typedef enum echar echar;
+enum echar {
+ mchar = UCHAR_MAX,
+};
+
+typedef enum eshort eshort;
+enum eshort {
+ mshort = USHRT_MAX,
+};
+
+typedef enum eint eint;
+enum eint {
+ mint = UINT_MAX,
+};
+
+typedef enum elong elong;
+enum elong {
+ mlong = ULONG_MAX,
+};
+
+typedef enum ellong ellong;
+enum ellong {
+ mllong = ULLONG_MAX,
+};
+
+#ifdef UINT128_MAX
+typedef enum e128 e128;
+enum e128 {
+ m128 = UINT128_MAX,
+};
+#endif
+
+#if __has_feature(c_fixed_enum) || (__clang_major__ > 7)
+
+enum fchar : unsigned char {
+ nchar = UCHAR_MAX,
+};
+typedef enum fchar fchar;
+
+enum fshort : unsigned short {
+ nshort = USHRT_MAX,
+};
+typedef enum fshort fshort;
+
+enum fint : unsigned {
+ nint = UINT_MAX,
+};
+typedef enum fint fint;
+
+enum flong : unsigned long {
+ nlong = ULONG_MAX,
+};
+typedef enum flong flong;
+
+enum fllong : unsigned long long {
+ nllong = ULLONG_MAX,
+};
+typedef enum fllong fllong;
+
+#ifdef UINT128_MAX
+enum f128 : uint128_t {
+ n128 = UINT128_MAX,
+};
+typedef enum f128 f128;
+#endif
+#else
+# warning "test for fixed underlying integer type skipped"
+#endif
+
+int main(void) {
+ printf("echar:\t%zu\t%zu\n", sizeof(echar), sizeof(mchar));
+ printf("eshort:\t%zu\t%zu\n", sizeof(eshort), sizeof(mshort));
+ printf("eint:\t%zu\t%zu\n", sizeof(eint), sizeof(mint));
+ printf("elong:\t%zu\t%zu\n", sizeof(elong), sizeof(mlong));
+ printf("ellong:\t%zu\t%zu\n", sizeof(ellong), sizeof(mllong));
+#ifdef UINT128_MAX
+ printf("e128:\t%zu\t%zu\n", sizeof(e128), sizeof(m128));
+#endif
+
+#if __has_feature(c_fixed_enum) || (__clang_major__ > 7)
+ printf("fchar:\t%zu\t%zu\n", sizeof(fchar), sizeof(nchar));
+ printf("fshort:\t%zu\t%zu\n", sizeof(fshort), sizeof(nshort));
+ printf("fint:\t%zu\t%zu\n", sizeof(fint), sizeof(nint));
+ printf("flong:\t%zu\t%zu\n", sizeof(flong), sizeof(nlong));
+ printf("fllong:\t%zu\t%zu\n", sizeof(fllong), sizeof(nllong));
+# ifdef UINT128_MAX
+ printf("f128:\t%zu\t%zu\n", sizeof(f128), sizeof(n128));
+# endif
+#endif
+}
diff --git a/esc.c b/esc.c
new file mode 100644
index 0000000..717e556
--- /dev/null
+++ b/esc.c
@@ -0,0 +1,12 @@
+#include "esc.h"
+
+void esc_move(FILE* f, int vert, int hori) {
+ if (vert > 0) fprintf(f, ESC_CSI "%dB", vert);
+ else if (vert < 0) fprintf(f, ESC_CSI "%dA", -vert);
+ if (hori > 0) fprintf(f, ESC_CSI "%dC", hori);
+ else if (hori < 0) fprintf(f, ESC_CSI "%dD", -hori);
+}
+
+void esc_goto(FILE* f, unsigned vert, unsigned hori) {
+ fprintf(f, ESC_CSI "%u;%uH", vert, hori);
+}
diff --git a/esc.h b/esc.h
new file mode 100644
index 0000000..d355c90
--- /dev/null
+++ b/esc.h
@@ -0,0 +1,156 @@
+#ifndef ESCAPE_H
+#define ESCAPE_H 1
+
+#include <stdio.h>
+
+#define ESC_CSI "\e["
+#define ESC_SS2 "\eN"
+#define ESC_SS3 "\eO"
+#define ESC_SCI "\eZ"
+#define ESC_ROI "\e%"
+
+#define ESC_SAVE ESC_CSI "s"
+#define ESC_RESTORE ESC_CSI "u"
+#define ESC_HOME ESC_CSI "H"
+#define ESC_CLRSCR ESC_CSI "1J"
+#define ESC_CLEAR ESC_CSI "2K"
+#define ESC_UP ESC_CSI "A"
+#define ESC_DOWN ESC_CSI "B"
+#define ESC_FRWD ESC_CSI "C"
+#define ESC_BKWD ESC_CSI "D"
+#define ESC_END ESC_CSI "F"
+#define ESC_INSERT ESC_CSI "2~"
+#define ESC_DELETE ESC_CSI "3~"
+#define ESC_PGUP ESC_CSI "5~"
+#define ESC_PGDOWN ESC_CSI "6~"
+
+// F1 to F4 are special
+#define ESC_F1 ESC_SS3 "P"
+#define ESC_F2 ESC_SS3 "Q"
+#define ESC_F3 ESC_SS3 "R"
+#define ESC_F4 ESC_SS3 "S"
+#define ESC_S_F1 ESC_CSI "1;2P"
+#define ESC_S_F2 ESC_CSI "1;2Q"
+#define ESC_S_F3 ESC_CSI "1;2R"
+#define ESC_S_F4 ESC_CSI "1;2S"
+#define ESC_C_F1 ESC_CSI "1;5P"
+#define ESC_C_F2 ESC_CSI "1;5Q"
+#define ESC_C_F3 ESC_CSI "1;5R"
+#define ESC_C_F4 ESC_CSI "1;5S"
+
+#define ESC_F5 ESC_CSI "15~"
+#define ESC_F6 ESC_CSI "17~"
+#define ESC_F7 ESC_CSI "18~"
+#define ESC_F8 ESC_CSI "19~"
+#define ESC_F9 ESC_CSI "20~"
+#define ESC_F10 ESC_CSI "21~"
+#define ESC_F11 ESC_CSI "23~"
+#define ESC_F12 ESC_CSI "24~"
+
+#define ESC_S_F5 ESC_CSI "15;2~"
+#define ESC_S_F6 ESC_CSI "17;2~"
+#define ESC_S_F7 ESC_CSI "18;2~"
+#define ESC_S_F8 ESC_CSI "19;2~"
+#define ESC_S_F9 ESC_CSI "20;2~"
+#define ESC_S_F10 ESC_CSI "21;2~"
+#define ESC_S_F11 ESC_CSI "23;2~"
+#define ESC_S_F12 ESC_CSI "24;2~"
+
+#define ESC_C_F5 ESC_CSI "15;5~"
+#define ESC_C_F6 ESC_CSI "17;5~"
+#define ESC_C_F7 ESC_CSI "18;5~"
+#define ESC_C_F8 ESC_CSI "19;5~"
+#define ESC_C_F9 ESC_CSI "20;5~"
+#define ESC_C_F10 ESC_CSI "21;5~"
+#define ESC_C_F11 ESC_CSI "23;5~"
+#define ESC_C_F12 ESC_CSI "24;5~"
+
+#define ESC_HIDE ESC_CSI "?25l"
+#define ESC_SHOW ESC_CSI "?25h"
+
+#define ESC_NORMAL ESC_CSI "0m"
+#define ESC_BOLD ESC_CSI "1m"
+#define ESC_UNDERL ESC_CSI "4m"
+#define ESC_BLINK ESC_CSI "5m"
+#define ESC_INVERS ESC_CSI "7m"
+#define ESC_FRAMED ESC_CSI "51m"
+#define ESC_CIRCLED ESC_CSI "52m"
+#define ESC_OVERL ESC_CSI "53m"
+
+#define ESC_TBLACK ESC_CSI "30m"
+#define ESC_TRED ESC_CSI "38;2;255;0;0m"
+#define ESC_TGREEN ESC_CSI "38;2;0;255;0m"
+#define ESC_TYELLOW ESC_CSI "38;2;255;255;0m"
+#define ESC_TBLUE ESC_CSI "38;2;0;0;255m"
+#define ESC_TMAGENTA ESC_CSI "38;2;255;0;255m"
+#define ESC_TCYAN ESC_CSI "38;2;0;255;255m"
+#define ESC_TWHITE ESC_CSI "37m"
+
+#define ESC_BBLACK ESC_CSI "40m"
+#define ESC_BRED ESC_CSI "48;2;255;0;0m"
+#define ESC_BGREEN ESC_CSI "48;2;0;255;0m"
+#define ESC_BYELLOW ESC_CSI "48;2;255;255;0m"
+#define ESC_BBLUE ESC_CSI "48;2;0;0;255m"
+#define ESC_BMAGENTA ESC_CSI "48;2;255;0;255m"
+#define ESC_BCYAN ESC_CSI "48;2;0;255;255m"
+#define ESC_BWHITE ESC_CSI "47m"
+
+#define ESC_BLIGHTGREY ESC_CSI "48;2;220;220;220m"
+#define ESC_BGREY ESC_CSI "48;2;128;128;128m"
+
+
+enum { esc_top = 1, esc_bottom = 2, esc_left = 4, esc_right = 8, };
+
+#define ESC_BORDER \
+((char const*const[]){ \
+[0] = " ", \
+ [esc_top] = "━", \
+ [esc_bottom] = "━", \
+ [esc_bottom | esc_top] = "━", \
+ [esc_left] = "┃", \
+ [esc_left | esc_top] = "┏", \
+ [esc_left | esc_bottom] = "┗", \
+ [esc_left | esc_bottom | esc_top] = "┣", \
+ [esc_right] = "┃", \
+ [esc_right | esc_top] = "┓", \
+ [esc_right | esc_bottom] = "┛", \
+ [esc_right | esc_bottom | esc_top] = "┫", \
+ [esc_right | esc_left] = "┃", \
+ [esc_right | esc_left | esc_top] = "┳", \
+ [esc_right | esc_left | esc_bottom] = "┻", \
+ [esc_right | esc_left | esc_bottom | esc_top] = "╋", \
+ })
+
+enum { esc_nw = 1, esc_ne = 2, esc_sw = 4, esc_se = 8, };
+
+#define ESC_BLOCK \
+((char const*const[]){ \
+[0] = " ", \
+ [esc_nw] = "▘", \
+ [esc_ne] = "▝", \
+ [esc_ne | esc_nw] = "▀", \
+ [esc_sw] = "▖", \
+ [esc_sw | esc_nw] = "▌", \
+ [esc_sw | esc_ne] = "▞", \
+ [esc_sw | esc_ne | esc_nw] = "▛", \
+ [esc_se] = "▗", \
+ [esc_se | esc_nw] = "▚", \
+ [esc_se | esc_ne] = "▐", \
+ [esc_se | esc_ne | esc_nw] = "▜", \
+ [esc_se | esc_sw] = "▄", \
+ [esc_se | esc_sw | esc_nw] = "▙", \
+ [esc_se | esc_sw | esc_ne] = "▟", \
+ [esc_se | esc_sw | esc_ne | esc_nw] = "█", \
+ })
+
+/**
+ ** @brief relative cursor movement
+ **/
+void esc_move(FILE* f, int vert, int hori);
+
+/**
+ ** @brief asolute cursor movement
+ **/
+void esc_goto(FILE* f, unsigned vert, unsigned hori);
+
+#endif
diff --git a/euclid.c b/euclid.c
new file mode 100644
index 0000000..72d7648
--- /dev/null
+++ b/euclid.c
@@ -0,0 +1,15 @@
+#include "euclid.h"
+
+size_t gcd(size_t a, size_t b) [[__unsequenced__]];
+
+int main(int argc, char* argv[argc+1]) {
+ size_t prev = 0;
+ for (int i = 1; i < argc; ++i) { // Processes args
+ size_t act = strtoull(argv[i], nullptr, 0); // arg -> unsigned long long
+ if (prev)
+ printf("gcd(%zu, %zu) is %zu\n",
+ prev, act, gcd(prev, act));
+ prev = act;
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/euclid.h b/euclid.h
new file mode 100644
index 0000000..82fca4e
--- /dev/null
+++ b/euclid.h
@@ -0,0 +1,24 @@
+#ifndef EUCLID_H
+# define EUCLID_H 1
+
+# include "c23-fallback.h"
+# include <stdio.h>
+# include <assert.h>
+
+inline size_t gcd2(size_t a, size_t b) [[__unsequenced__]] {
+ assert(a <= b); /*@\label{gcd2-precondition}*/
+ if (!a) return b; /*@\label{gcd2-bottom}*/
+ size_t rem = b % a; /*@\label{gcd2-remainder}*/
+ return gcd2(rem, a); /*@\label{gcd2-recurse}*/
+}
+
+inline size_t gcd(size_t a, size_t b) [[__unsequenced__]] {
+ assert(a);
+ assert(b);
+ if (a < b)
+ return gcd2(a, b);
+ else
+ return gcd2(b, a);
+}
+
+#endif
diff --git a/extern.c b/extern.c
new file mode 100644
index 0000000..186596d
--- /dev/null
+++ b/extern.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+
+unsigned i = 1; /*@\label{line:decl-i-1}*/
+
+int main(void) {
+ unsigned i = 2; /* A new object *//*@\label{line:decl-i-2}*/
+ if (i) {
+ extern unsigned i; /* An existing object *//*@\label{line:decl-i-3}*/
+ printf("%u\n", i); /* prints 1 */
+ } else {
+ printf("%u\n", i); /* prints 2 */
+ }
+}
diff --git a/fibonacci.c b/fibonacci.c
new file mode 100644
index 0000000..640304e
--- /dev/null
+++ b/fibonacci.c
@@ -0,0 +1,19 @@
+#include <stdio.h>
+#include "c23-fallback.h"
+
+size_t fib(size_t n) [[__unsequenced__]] {
+ if (n < 3)
+ return 1;
+ else
+ return fib(n-1) + fib(n-2);
+}
+
+
+int main(int argc, char *argv[]) {
+ for (int i = 1; i < argc; ++i) { // Processes args
+ size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t
+ printf("fib(%zu) is %zu\n",
+ n, fib(n));
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/fibonacci2.c b/fibonacci2.c
new file mode 100644
index 0000000..84d3582
--- /dev/null
+++ b/fibonacci2.c
@@ -0,0 +1,26 @@
+#include <stdio.h>
+#include "c23-fallback.h"
+
+void fib2rec(size_t n, size_t buf[static 2]) [[__unsequenced__]] {
+ if (n > 2) {
+ size_t res = buf[0] + buf[1];
+ buf[1] = buf[0];
+ buf[0] = res;
+ fib2rec(n-1, buf);
+ }
+}
+
+size_t fib2(size_t n) [[__unsequenced__]] {
+ size_t res[2] = { 1, 1, };
+ fib2rec(n, res);
+ return res[0];
+}
+
+int main(int argc, char* argv[argc+1]) {
+ for (int i = 1; i < argc; ++i) { // Processes args
+ size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t
+ printf("fib(%zu) is %zu\n",
+ n, fib2(n));
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/fibonacci3.c b/fibonacci3.c
new file mode 100644
index 0000000..2ed77ef
--- /dev/null
+++ b/fibonacci3.c
@@ -0,0 +1,27 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+void fib2rec(size_t n, size_t buf[2]) {
+ if (n) {
+ size_t res = buf[0] + buf[1];
+ buf[1] = buf[0];
+ buf[0] = res;
+ fib2rec(n-1, buf);
+ }
+}
+
+
+size_t fib2(size_t n) {
+ size_t res[2] = { 1, 1, };
+ if (n > 2) fib2rec(n - 2, res);
+ return res[0];
+}
+
+int main(int argc, char* argv[argc+1]) {
+ for (int i = 1; i < argc; ++i) { // process args
+ size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t
+ printf("fib(%zu) is %zu\n",
+ n, fib2(n));
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/fibonacci4.c b/fibonacci4.c
new file mode 100644
index 0000000..ff250c7
--- /dev/null
+++ b/fibonacci4.c
@@ -0,0 +1,30 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+/**
+ ** Rewrite the recursive Fibonacci such that it alternates the use of
+ ** the buffers.
+ **/
+
+void fib2rec(size_t n, size_t buf[2]) {
+ if (n) {
+ buf[n%2] += buf[!(n%2)];
+ fib2rec(n-1, buf);
+ }
+}
+
+
+size_t fib2(size_t n) {
+ size_t res[2] = { 1, 1, };
+ if (n > 2) fib2rec(n - 2, res);
+ return res[1];
+}
+
+int main(int argc, char* argv[argc+1]) {
+ for (int i = 1; i < argc; ++i) { // process args
+ size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t
+ printf("fib(%zu) is %zu\n",
+ n, fib2(n));
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/fibonacci5.c b/fibonacci5.c
new file mode 100644
index 0000000..c642aa5
--- /dev/null
+++ b/fibonacci5.c
@@ -0,0 +1,30 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+/**
+ ** Rewrite the recursive Fibonacci such that it unrolls two
+ ** successive recursive calls.
+ **/
+
+void fib2rec(size_t n, size_t buf[2]) {
+ if (n > 1) {
+ buf[0] += buf[1];
+ buf[1] += buf[0];
+ fib2rec(n-2, buf);
+ }
+}
+
+size_t fib2(size_t n) {
+ size_t res[2] = { 1, 1, };
+ if (n > 2) fib2rec(n-1, res);
+ return res[!(n%2)];
+}
+
+int main(int argc, char* argv[argc+1]) {
+ for (int i = 1; i < argc; ++i) { // process args
+ size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t
+ printf("fib(%zu) is %zu\n",
+ n, fib2(n));
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/fibonacci6.c b/fibonacci6.c
new file mode 100644
index 0000000..2090ccd
--- /dev/null
+++ b/fibonacci6.c
@@ -0,0 +1,29 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+/**
+ ** Rewrite Fibonacci iteratively such that is proceeds in pairs of
+ ** values and hopefully doesn't spill any of the variables to memory.
+ **/
+
+size_t fib2(size_t n) {
+ register size_t x1 = 1; // F(x1) for x1 = (n+1)%2 + 1
+ register size_t x2 = n%2 ? 1 : 2; // F(x1+1)
+ for (register size_t i = (n-1)/2; i; --i) {
+ x1 += x2;
+ x2 += x1;
+ }
+ return x1; // F(y) with y = x1 + 2*((n-1)/2)
+ // = x1 + ((n-1)-((n+1)%2))
+ // = ((n+1)%2 + 1)+((n-1)-((n+1)%2))
+ // = n
+}
+
+int main(int argc, char* argv[argc+1]) {
+ for (int i = 1; i < argc; ++i) { // process args
+ size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t
+ printf("fib(%zu) is %zu\n",
+ n, fib2(n));
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/fibonacciCache.c b/fibonacciCache.c
new file mode 100644
index 0000000..b095578
--- /dev/null
+++ b/fibonacciCache.c
@@ -0,0 +1,50 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+#include <string.h>
+
+/* Compute Fibonacci number n with the help of a cache that may
+ hold previously computed values. */
+size_t fibCacheRec(size_t n, size_t cache[static n]) {
+ if (!cache[n-1]) {
+ cache[n-1]
+ = fibCacheRec(n-1, cache) + fibCacheRec(n-2, cache);
+ }
+ return cache[n-1];
+}
+
+size_t fibCache(size_t n) {
+ if (n+1 <= 3) return 1;
+ /* Set up a VLA to cache the values. */
+#if __STDC_VERSION__ > 202311L
+ /* Since C23, VLA can be default initialized. */
+ size_t cache[n] = { };
+#else
+ size_t cache[n]; memset(cache, 0, n*sizeof(*cache));
+#endif
+ /* Non-trivial initialization is replaced by assignment. */
+ cache[0] = 1; cache[1] = 1;
+ /* Call the recursive function. */
+ return fibCacheRec(n, cache);
+}
+
+double goldenRatio(size_t n) {
+ if (n+1 <= 3) return 1;
+ size_t cache[n];
+ cache[0] = 1;
+ cache[1] = 1;
+ for (size_t i = 2; i < n; ++i)
+ cache[i] = 0;
+ double ret = fibCacheRec(n, cache);
+ return ret/fibCacheRec(n-1, cache);
+}
+
+int main(int argc, char* argv[argc+1]) {
+ for (int i = 1; i < argc; ++i) { // Processes args
+ size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t
+ double golden = goldenRatio(n);
+ double control = golden*2.0-1.0;
+ printf("fib(%zu) is %zu, golden ratio %.20g, control %.20g\n",
+ n, fibCache(n), golden, control*control);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/fibonacciRet.c b/fibonacciRet.c
new file mode 100644
index 0000000..bfefa80
--- /dev/null
+++ b/fibonacciRet.c
@@ -0,0 +1,46 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+typedef struct fibonaccipair fibonaccipair;
+
+struct fibonaccipair {
+ size_t prev;
+ size_t act;
+};
+
+fibonaccipair fiboNext(fibonaccipair ret) {
+ size_t res = ret.act + ret.prev;
+ ret.prev = ret.act;
+ ret.act = res;
+ return ret;
+}
+
+fibonaccipair fibonacci2(size_t n, fibonaccipair ret) {
+ if (n) {
+ return fibonacci2(n-1, fiboNext(ret));
+ } else
+ return ret;
+}
+
+fibonaccipair fibonacci3(size_t n, fibonaccipair ret) {
+ while (n --> 0) {
+ ret = fiboNext(ret);
+ }
+ return ret;
+}
+
+size_t fibonacci(size_t n) {
+ register fibonaccipair res = { 1, 1, };
+ if (n > 2) res = fibonacci2(n - 2, res);
+ return res.act;
+}
+
+
+int main(int argc, char* argv[argc+1]) {
+ for (int i = 1; i < argc; ++i) { // process args
+ size_t const n = strtoull(argv[i], nullptr, 0); // arg -> size_t
+ printf("fibonacci(%zu) is %zu\n",
+ n, fibonacci(n));
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/fp_except.c b/fp_except.c
new file mode 100644
index 0000000..5dc63ec
--- /dev/null
+++ b/fp_except.c
@@ -0,0 +1,88 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+#include <fenv.h>
+#include <float.h>
+#ifndef INFINITY
+#include <math.h>
+#endif
+
+#if !defined(__STDC_IEC_559__) && !defined(__STDC_IEC_60559_BFP__) && !FE_ALL_EXCEPT
+#error "floating-point arithmetic is too weird"
+#endif
+
+static_assert(FE_ALL_EXCEPT, "floating point exceptions are not supported");
+
+#ifdef FENV_OFF
+#warning "switching FENV_ACCESS off"
+#pragma STDC FENV_ACCESS OFF
+#else
+#pragma STDC FENV_ACCESS ON
+#endif
+
+bool const has_inf =
+#ifdef INFINITY
+ (1.0/0.0 == INFINITY)
+#else
+ false
+#endif
+ ;
+
+int excepts[] = {
+#ifdef FE_DIVBYZERO
+ FE_DIVBYZERO,
+#endif
+#ifdef FE_INEXACT
+ FE_INEXACT,
+#endif
+#ifdef FE_INVALID
+ FE_INVALID,
+#endif
+#ifdef FE_OVERFLOW
+ FE_OVERFLOW,
+#endif
+#ifdef FE_UNDERFLOW
+ FE_UNDERFLOW,
+#endif
+};
+
+void printexcept(void) {
+ char const* name[] = {
+#ifdef FE_DIVBYZERO
+ "divbyzero",
+#endif
+#ifdef FE_INEXACT
+ "inexact",
+#endif
+#ifdef FE_INVALID
+ "invalid",
+#endif
+#ifdef FE_OVERFLOW
+ "overflow",
+#endif
+#ifdef FE_UNDERFLOW
+ "underflow",
+#endif
+ };
+ int except = fetestexcept(FE_ALL_EXCEPT);
+ if (except) {
+ printf("[");
+ for (unsigned j = 0; except; except &= ~excepts[j], ++j)
+ if (excepts[j] & except)
+ printf("%s ", name[j]);
+ printf("]");
+ }
+}
+
+int main (int argc, char* argv[static argc+1]) {
+ printf("division by zero is %sequal to INFINITY\n", has_inf ? "" : "un");
+ for (unsigned i = 1; i < argc; i++) {
+ feclearexcept(FE_ALL_EXCEPT);
+ double x = strtod(argv[i], nullptr);
+ printf("%g ", x);
+ printexcept();
+ feclearexcept(FE_ALL_EXCEPT);
+ printf(": %g ", 1.0/x);
+ printexcept();
+ puts("");
+ }
+}
diff --git a/generic.c b/generic.c
new file mode 100644
index 0000000..fcfa94d
--- /dev/null
+++ b/generic.c
@@ -0,0 +1,62 @@
+#include "generic.h"
+
+/* "Instantiate" the inline functions of the "generic.h" header. */
+signed (maxs)(signed a, signed b) [[__unsequenced__]];
+unsigned (maxu)(unsigned a, unsigned b) [[__unsequenced__]];
+unsigned (maxus)(unsigned a, signed b) [[__unsequenced__]];
+unsigned (maxsu)(signed a, unsigned b) [[__unsequenced__]];
+signed long (maxsl)(signed long a, signed long b) [[__unsequenced__]];
+unsigned long (maxul)(unsigned long a, unsigned long b) [[__unsequenced__]];
+unsigned long (maxusl)(unsigned long a, signed long b) [[__unsequenced__]];
+unsigned long (maxsul)(signed long a, unsigned long b) [[__unsequenced__]];
+signed long long (maxsll)(signed long long a, signed long long b) [[__unsequenced__]];
+unsigned long long (maxull)(unsigned long long a, unsigned long long b) [[__unsequenced__]];
+unsigned long long (maxusll)(unsigned long long a, signed long long b) [[__unsequenced__]];
+unsigned long long (maxsull)(signed long long a, unsigned long long b) [[__unsequenced__]];
+
+int main(int argc, char* argv[argc+1]){
+ if (argc > 1) {
+ printf("testing strto functions: base 10 = %lu, general base = %llu\n",
+ strtoul10(argv[2]), strtoull(argv[2], nullptr));
+ long double a = strtold(argv[1]);
+ printf("minimum of %Lg and 1.0f is %Lg\n",
+ a, min(a, 1.0F));
+ printf("minimum of %g and argc is %g\n",
+ 3.0, min(3.0, argc));
+ }
+ printf("maxof(1) = %d\n", maxof(1));
+ printf("maxof(1.0) = %g\n", maxof(1.0));
+ printf("maxof(long double) = %Lg\n", maxof(long double));
+ printf("minof(1ul) = %lu\n", minof(1ul));
+ printf("minof(1.0) = %g\n", minof(1.0));
+ printf("minof(long double) = %Lg\n", minof(long double));
+ printf("mix(1u, -1l) = %luul\n", mix(1u, -1l));
+ printf("mix(-1, 1ul) = %luul\n", mix(-1, 1ul));
+ printf("mix(-1, 1l) = %ldl\n", mix(-1, 1l));
+ printf("mix(1u, 2ul) = %luul\n", mix(1u, 2ul));
+ puts((memcpy)((char[6]){ 0 }, "hello", 6));
+ // Erroneous target buffer, should not be qualified. diagnostic?
+ puts((memcpy)((char volatile[6]){ 0 }, "he1lo", 6));
+ // Erroneous use of target buffer, using 6 where there are only 5. diagnostic?
+ puts((memcpy)((char[5]){ 0 }, "he2lo", 6));
+ // Erroneous use of source buffer, using 7 where there are only 6. diagnostic?
+ puts((memcpy)((char[7]){ 0 }, "he3lo", 7));
+ puts(memcpy((char[6]){ 0 }, "hello", 6));
+ // Erroneous target buffer, should not be qualified. diagnostic?
+ puts(memcpy((char volatile[6]){ 0 }, "he1lo", 6));
+ // Erroneous use of target buffer, using 6 where there are only 5. diagnostic?
+ puts(memcpy((char[5]){ 0 }, "he2lo", 6));
+ // Erroneous use of source buffer, using 7 where there are only 6. diagnostic?
+ puts(memcpy((char[7]){ 0 }, "he3lo", 7));
+ printf("snprintf(nullptr, 0, \"%%g\", 6.555): %d\n", snprintf(nullptr, 0, "%g", 6.555));
+ printf("snprintf(nullptr, 0, \"hoho\"): %d\n", snprintf(nullptr, 0, "hoho"));
+ // Erroneous use of nullptr when size is not zero. diagnostic?
+ printf("snprintf(nullptr, 0, \"%%g\", 6.555): %d\n", snprintf(nullptr, 20, "%g", 6.555));
+ printf("snprintf(nullptr, 0, \"hoho\"): %d\n", snprintf(nullptr, 20, "hoho"));
+ // Erroneous use of buffer, claiming 20 only providing 16. diagnostic?
+ printf("snprintf(something, 20, \"%%g\", 6.555): %d\n", snprintf((char[16]){ 0 }, 20, "%g", 6.555));
+ // Erroneous use of buffer, claiming 20 only providing 16. diagnostic?
+ printf("snprintf(something, 20, \"hoho\"): %d\n", snprintf((char[16]){ 0 }, 20, "hoho"));
+ return EXIT_SUCCESS;
+ SIZE_WIDTH;
+}
diff --git a/generic.h b/generic.h
new file mode 100644
index 0000000..c7c5174
--- /dev/null
+++ b/generic.h
@@ -0,0 +1,360 @@
+#ifndef GENERIC_H
+#define GENERIC_H 1
+
+#include <float.h>
+#include <stdio.h>
+#include "c23-fallback.h"
+
+#if !__has_va_opt
+# error "no __VA_OPT__ support found, aborting compilation"
+// shut off some subsequent error messages
+# include "no __VA_OPT__ support found, aborting compilation"
+#endif
+
+/**
+ ** @brief Type-generic minimum for floating-point values
+ **/
+#define min(A, B) \
+_Generic((A)+(B), \
+ float: minf, \
+ long double: minl, \
+ default: mind)((A), (B))
+
+static inline
+double mind(double a, double b) [[__unsequenced__]] {
+ return a < b ? a : b;
+}
+
+static inline
+long double minl(long double a, long double b) [[__unsequenced__]] {
+ return a < b ? a : b;
+}
+
+static inline
+float minf(float a, float b) [[__unsequenced__]] {
+ return a < b ? a : b;
+}
+
+/**
+ ** @brief The maximum value for the type of @a X
+ **/
+#define MAXVAL(X) \
+_Generic((X), \
+ bool: (bool)+1, \
+ char: (char)+CHAR_MAX, \
+ signed char: (signed char)+SCHAR_MAX, \
+ unsigned char: (unsigned char)+UCHAR_MAX, \
+ signed short: (signed short)+SHRT_MAX, \
+ unsigned short: (unsigned short)+USHRT_MAX, \
+ signed: INT_MAX, \
+ unsigned: UINT_MAX, \
+ signed long: LONG_MAX, \
+ unsigned long: ULONG_MAX, \
+ signed long long: LLONG_MAX, \
+ unsigned long long: ULLONG_MAX, \
+ float: FLT_MAX, \
+ double: DBL_MAX, \
+ long double: LDBL_MAX)
+
+/**
+ ** @brief The maximum promoted value for @a XT, where @a XT
+ ** can be an expression or a type name
+ **
+ ** So this is the maximum value when fed to an arithmetic
+ ** operation such as @c +.
+ **
+ ** @remark Narrow types are promoted, usually to @c signed,
+ ** or maybe to @c unsigned on rare architectures.
+ **/
+#define maxof(XT) \
+_Generic(0+(XT)+0, \
+ signed: INT_MAX, \
+ unsigned: UINT_MAX, \
+ signed long: LONG_MAX, \
+ unsigned long: ULONG_MAX, \
+ signed long long: LLONG_MAX, \
+ unsigned long long: ULLONG_MAX, \
+ float: FLT_MAX, \
+ double: DBL_MAX, \
+ long double: LDBL_MAX)
+
+#define MINVAL(X) \
+_Generic((X), \
+ bool: (bool)+0, \
+ char: (char)+CHAR_MIN, \
+ signed char: (signed char)+SCHAR_MIN, \
+ unsigned char: (unsigned char)+0, \
+ signed short: (signed short)+SHRT_MIN, \
+ unsigned short: (unsigned short)+0, \
+ signed: INT_MIN, \
+ unsigned: 0U, \
+ signed long: LONG_MIN, \
+ unsigned long: 0UL, \
+ signed long long: LLONG_MIN, \
+ unsigned long long: 0ULL, \
+ float: -FLT_MAX, \
+ double: -DBL_MAX, \
+ long double: -LDBL_MAX)
+
+#define minof(XT) \
+_Generic(0+(XT)+0, \
+ signed: INT_MIN, \
+ unsigned: 0U, \
+ signed long: LONG_MIN, \
+ unsigned long: 0UL, \
+ signed long long: LLONG_MIN, \
+ unsigned long long: 0ULL, \
+ float: -FLT_MAX, \
+ double: -DBL_MAX, \
+ long double: -LDBL_MAX)
+
+#define strtod(NPTR, ...) \
+ STRTOD_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__)
+#define STRTOD_I(NPTR) strtod(NPTR, nullptr)
+#define STRTOD_II(NPTR, ENDPTR) strtod(NPTR, ENDPTR)
+#define strtof(NPTR, ...) \
+ STRTOF_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__)
+#define STRTOF_I(NPTR) strtof(NPTR, nullptr)
+#define STRTOF_II(NPTR, ENDPTR) strtof(NPTR, ENDPTR)
+#define strtold(NPTR, ...) \
+ STRTOLD_I __VA_OPT__(I) (NPTR __VA_OPT__(,) __VA_ARGS__)
+#define STRTOLD_I(NPTR) strtold(NPTR, nullptr)
+#define STRTOLD_II(NPTR, ENDPTR) strtold(NPTR, ENDPTR)
+
+/** @brief Output the second argument, if any, or `DEF0` as a default.
+ **
+ ** Error if more than two arguments are given.
+ **/
+#define DEFAULT1(DEF0, ...) \
+ ID_I ## __VA_OPT__(Iplus_DEFAULT) \
+ (DEF0 __VA_OPT__(,) __VA_ARGS__)
+#define ID_IIplus_DEFAULT(_01, ...) ID_I(__VA_ARGS__)
+
+#define ID_()
+#define ID_I(_01) _01
+#define ID_II(_01, _2) _01, _2
+#define ID_III(_01, _2, _3) _01, _2, _3
+
+/** @brief Output comma-separated arguments from the third position
+ ** onward, fill with defaults from the first two.
+ **
+ ** Error if more than four arguments are given.
+ **/
+#define DEFAULT2(DEF0, DEF1, ...) \
+ ID_II ## __VA_OPT__(Iplus_DEFAULT) \
+ (DEF0, DEF1 __VA_OPT__(,) __VA_ARGS__)
+#define ID_IIIplus_DEFAULT(DEF0, DEF1, _01, ...) \
+ DEFAULT1(DEF0, _01), DEFAULT1(DEF1, __VA_ARGS__)
+
+/** @brief Output comma-separated arguments from the fourth position
+ ** onward, fill with defaults from the first three.
+ **
+ ** Error if more than six arguments are given.
+ **/
+#define DEFAULT3(DEF0, DEF1, DEF2, ...) \
+ ID_III ## __VA_OPT__(Iplus_DEFAULT) \
+ (DEF0, DEF1, DEF2 __VA_OPT__(,) __VA_ARGS__)
+#define ID_IIIIplus_DEFAULT(DEF0, DEF1, DEF2, _01, ...) \
+ DEFAULT1(DEF0, _01), DEFAULT2(DEF1, DEF2, __VA_ARGS__)
+
+#define CALL1(FUNC, DEF0, ...) \
+ FUNC(DEFAULT1(DEF0, __VA_ARGS__))
+#define CALL2(FUNC, DEF0, DEF1, ...) \
+ FUNC(DEFAULT2(DEF0, DEF1, __VA_ARGS__))
+#define CALL3(FUNC, DEF0, DEF1, DEF2, ...) \
+ FUNC(DEFAULT3(DEF0, DEF1, DEF2, __VA_ARGS__))
+
+#define strtoul(...) \
+ CALL3(strtoul, "0", nullptr, 0, __VA_ARGS__)
+#define strtoull(...) \
+ CALL3(strtoull, "0", nullptr, 0, __VA_ARGS__)
+#define strtol(...) \
+ CALL3(strtol, "0", nullptr, 0, __VA_ARGS__)
+#define strtoll(...) \
+ CALL3(strtoll, "0", nullptr, 0, __VA_ARGS__)
+
+/**
+ ** @brief Calls a three-parameter function with default arguments set to
+ ** specific values
+ **
+ ** @see strtoull10 for an example
+ **/
+#define strtoul10(NPTR, ...) CALL3((strtoul), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__)
+#define strtoull10(NPTR, ...) CALL3((strtoull), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__)
+#define strtol10(NPTR, ...) CALL3((strtol), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__)
+#define strtoll10(NPTR, ...) CALL3((strtoll), "0", nullptr, 10, NPTR __VA_OPT__(,) __VA_ARGS__)
+
+#define PROMOTE(XT, A) \
+ _Generic(0+(XT)+0, \
+ signed: (signed)(A), \
+ unsigned: (unsigned)(A), \
+ signed long: (signed long)(A), \
+ unsigned long: (unsigned long)(A), \
+ signed long long: (signed long long)(A), \
+ unsigned long long: (unsigned long long)(A), \
+ float: (float)(A), \
+ double: (double)(A), \
+ long double: (long double)(A))
+
+#define SIGNEDNESS(XT) (PROMOTE(XT, -1) < PROMOTE(XT, 0))
+
+inline
+signed maxs(signed a, signed b) [[__unsequenced__]] {
+ return a < b ? b : a;
+}
+
+inline
+unsigned maxu(unsigned a, unsigned b) [[__unsequenced__]] {
+ return a < b ? b : a;
+}
+
+inline
+unsigned maxus(unsigned a, signed b) [[__unsequenced__]] {
+ return b < 0 ? a : maxu(a, b);
+}
+
+inline
+unsigned maxsu(signed a, unsigned b) [[__unsequenced__]] {
+ return a < 0 ? b : maxu(a, b);
+}
+
+inline
+signed long maxsl(signed long a, signed long b) [[__unsequenced__]] {
+ return a < b ? b : a;
+}
+
+inline
+unsigned long maxul(unsigned long a, unsigned long b) [[__unsequenced__]] {
+ return a < b ? b : a;
+}
+
+inline
+unsigned long maxusl(unsigned long a, signed long b) [[__unsequenced__]] {
+ return b < 0 ? a : maxul(a, b);
+}
+
+inline
+unsigned long maxsul(signed long a, unsigned long b) [[__unsequenced__]] {
+ return a < 0 ? b : maxul(a, b);
+}
+
+inline
+signed long long maxsll(signed long long a, signed long long b) [[__unsequenced__]] {
+ return a < b ? b : a;
+}
+
+inline
+unsigned long long maxull(unsigned long long a, unsigned long long b) [[__unsequenced__]] {
+ return a < b ? b : a;
+}
+
+inline
+unsigned long long maxusll(unsigned long long a, signed long long b) [[__unsequenced__]] {
+ return b < 0 ? a : maxull(a, b);
+}
+
+inline
+unsigned long long maxsull(signed long long a, unsigned long long b) [[__unsequenced__]] {
+ return a < 0 ? b : maxull(a, b);
+}
+
+#define MAX(X, Y) \
+_Generic((X)+(Y), \
+ signed: maxs, \
+ unsigned: maxu, \
+ signed long: maxsl, \
+ unsigned long: maxul, \
+ signed long long: maxsll, \
+ unsigned long long: maxull)((X), (Y))
+
+#define MAXUS(X, Y) \
+_Generic((X)+(Y), \
+ signed: maxus, \
+ unsigned: maxus, \
+ signed long: maxusl, \
+ unsigned long: maxusl, \
+ signed long long: maxusll, \
+ unsigned long long: maxusll)((X), (Y))
+
+#define MAXSU(X, Y) \
+_Generic((X)+(Y), \
+ signed: maxsu, \
+ unsigned: maxsu, \
+ signed long: maxsul, \
+ unsigned long: maxsul, \
+ signed long long: maxsull, \
+ unsigned long long: maxsull)((X), (Y))
+
+#define mix(X, Y) \
+_Generic(&(char[SIGNEDNESS(X)-SIGNEDNESS(Y)+2]){ }, \
+ char(*)[1]: MAXUS((X), (Y)), \
+ char(*)[2]: MAX((X), (Y)), \
+ char(*)[3]: MAXSU((X), (Y)))
+
+/**
+ ** @brief Similar to `snprintf` but checks the buffer argument for
+ ** the size.
+ **
+ ** To be able to do that check, the function has the size argument
+ ** first and then the buffer, so the buffer can be specified as array
+ ** parameter that depends on the that size.
+ **/
+[[maybe_unused, __gnu__::__format__(__printf__, 3, 4)]]
+static inline
+int snprintf_swapped(size_t n, char s[restrict static n],
+ char const* restrict form, ...) {
+ va_list ap;
+ va_start(ap);
+ int ret = vsnprintf(s, n, form, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ ** @brief A type-generic interface to `snprintf` that checks arguments.
+ **
+ ** We distinguish two cases. First if the first argument is `nullptr`
+ ** the second argument is forced to `0` and the function `snprintf`
+ ** is called. If it is not `nullptr` a function `snprintf_swapped`
+ ** that just swaps the first two arguments is called. Because
+ ** that function has the size argument first and then the buffer, the
+ ** buffer can be specified as array parameter that depends on the
+ ** that size.
+ **/
+#define snprintf(S, N, F, ...) \
+_Generic \
+ ((S), \
+ nullptr_t: \
+ (snprintf)(nullptr, GENERIC_IF(isice(N), (N), 0), \
+ F __VA_OPT__(,) __VA_ARGS__), \
+ default: \
+ snprintf_swapped(_Generic((S), \
+ nullptr_t: 1, \
+ default: (N)), \
+ _Generic((S), \
+ nullptr_t: \
+ (char[1]){ 0 }, \
+ default: (S)), \
+ (F) __VA_OPT__(,) __VA_ARGS__))
+
+#define pow(X, Y) \
+ _Generic \
+ ( \
+ (void (*)(typeof((X)+(Y)+0ULL), typeof((Y)+0ULL)))nullptr, \
+ /* Second argument is an integer. */ \
+ void (*)(float, unsigned long long): pownf, \
+ void (*)(double, unsigned long long): pown, \
+ void (*)(unsigned long long, unsigned long long): pown, \
+ void (*)(long double, unsigned long long): pownl, \
+ /* Second argument is a floating point. */ \
+ void (*)(float, float): powf, \
+ void (*)(long double, float): powl, \
+ void (*)(long double, double): powl, \
+ void (*)(long double, long double): powl, \
+ /* Second argument is a floating-point, and first */ \
+ /* is double or integer. */ \
+ default: pow) \
+ ((X), (Y))
+
+
+#endif
diff --git a/getting-started.c b/getting-started.c
new file mode 100644
index 0000000..ba43f92
--- /dev/null
+++ b/getting-started.c
@@ -0,0 +1,24 @@
+/* This may look like nonsense, but really is -*- mode: C -*- */
+#include <stdlib.h> /*@\label{include-stdlib}*/
+#include <stdio.h> /*@\label{include-stdio}*/
+
+/* The main thing that this program does. */ /*@\label{C-comment}*/
+int main(int argc, [[maybe_unused]] char* argv[argc+1]) { /*@\label{main-start}*/
+ // Declarations
+ double A[5] = { /*@\label{array-declaration}*/
+ [0] = 9.0, /*@\label{designated-init}*/
+ [1] = 2.9,
+ [4] = 3.E+25, /*@\label{scientific-notation}*/
+ [3] = .00007, /*@\label{comma-terminate}*/
+ };
+
+ // Doing some work /*@\label{CPP-comment}*/
+ for (size_t i = 0; i < 5; ++i) { /*@\label{for-loop}*/
+ printf("element %zu is %g, \tits square is %g\n", /*@\label{printf-start}*/
+ i,
+ A[i],
+ A[i]*A[i]); /*@\label{printf-end}*/
+ } /*@\label{for-end}*/
+
+ return EXIT_SUCCESS; /*@\label{main-return}*/
+} /*@\label{main-end}*/
diff --git a/heron-expanded.c b/heron-expanded.c
new file mode 100644
index 0000000..bd5d77f
--- /dev/null
+++ b/heron-expanded.c
@@ -0,0 +1,37 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+int main(int argc, char* argv[argc+1]) {
+ constexpr double epsP1 = 1.0 + 1E-9;
+ constexpr double epsM1 = 1.0 - 1E-9;
+ for (int i = 1; i < argc; ++i) {
+ /* Read one command line argument as a double. */
+ double const a = strtod(argv[i], nullptr);
+ /* Compute some local constants. */
+ double fact;
+ double alow;
+ double ahig;
+ if (a < 1.0) {
+ fact = 2.0;
+ alow = a;
+ ahig = 1.0;
+ } else {
+ fact = 0.5;
+ alow = -a;
+ ahig = -1.0;
+ }
+ /* A first low quality estimate for the inverse. */
+ double x = 1.0;
+ /* Adapt x until it has the right magnitude. */
+ while (alow*x < ahig) x *= fact;
+ /* We are close, correct with the Heron factor. */
+ for (double prod = a*x;
+ ((prod < epsM1) || (epsP1 < prod));
+ prod = a*x) {
+ x *= (2.0 - prod);
+ }
+ printf("heron: a=%.5e,\tx=%.5e,\ta*x=%.12f\n",
+ a, x, a*x);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/heron.c b/heron.c
new file mode 100644
index 0000000..4dae846
--- /dev/null
+++ b/heron.c
@@ -0,0 +1,36 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+/* lower and upper iteration limits centered around 1.0 */
+constexpr double eps1m01 = 1.0 - 0x1P-01;
+constexpr double eps1p01 = 1.0 + 0x1P-01;
+constexpr double eps1m24 = 1.0 - 0x1P-24;
+constexpr double eps1p24 = 1.0 + 0x1P-24;
+
+int main(int argc, char* argv[argc+1]) {
+ for (int i = 1; i < argc; ++i) { // process args
+ double const a = strtod(argv[i], nullptr); // arg -> double
+ double x = 1.0;
+ for (;;) { // by powers of 2
+ double prod = a*x;
+ if (prod < eps1m01) {
+ x *= 2.0;
+ } else if (eps1p01 < prod) {
+ x *= 0.5;
+ } else {
+ break;
+ }
+ }
+ for (;;) { // Heron approximation
+ double prod = a*x;
+ if ((prod < eps1m24) || (eps1p24 < prod)) {
+ x *= (2.0 - prod);
+ } else {
+ break;
+ }
+ }
+ printf("heron: a=%.5e,\tx=%.5e,\ta*x=%.12f\n",
+ a, x, a*x);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/heron_k.c b/heron_k.c
new file mode 100644
index 0000000..2328a57
--- /dev/null
+++ b/heron_k.c
@@ -0,0 +1,231 @@
+#include "heron_k.h"
+
+/**
+ ** Simply works by repeated squaring.
+ **/
+double expk2(double a, unsigned k) [[__unsequenced__]] {
+ for (;;) {
+ k /= 2u;
+ if (!k) break;
+ a *= a;
+ }
+ return a;
+}
+
+double expk_rec(double a, unsigned k) [[__unsequenced__]] {
+ switch (k)
+ default: {
+ if (k % 2u)
+ case 3: {
+ double ret = expk_rec(a, k/2u);
+ ret *= ret;
+ a *= ret;
+ } else {
+ /* If k is a power of 2, do something special. */
+ if (k == (k & -k)) {
+ [[fallthrough]];
+ case 4: a = expk2(a, k);
+ } else {
+ a = expk_rec(a, k/2u);
+ [[fallthrough]];
+ case 2:
+ a *= a;
+ }
+ }
+ case 1: // only possible with C23
+ }
+ return a;
+}
+
+double frexp_np(double x, signed exp[static 1]) [[__unsequenced__]] {
+ if (x < 0.0) return -frexp_np(-x, exp);
+ signed ret = 0;
+ if (x > 0.0) {
+ if (x < 0.5) {
+ do {
+ x *= 2.0;
+ --ret;
+ } while (x < 0.5);
+ } else {
+ while (x >= 1.0) {
+ x *= 0.5;
+ ++ret;
+ }
+ }
+ }
+ exp[0] = ret;
+ return x;
+}
+
+double heron1_estimate(double a) [[__unsequenced__]] {
+ /* A first low quality estimate for the inverse. */
+ signed e0; frexp_np(a, &e0);
+ return
+ (e0 <= 0
+ ? expk_rec(2.0, 1-e0)
+ : expk_rec(0.5, e0));
+}
+
+double heron1_estimate_dir_05(double a) [[__unsequenced__]] {
+ /* A table of double powers of 2. */
+ /*Something like (2 << (2 << k)) if only that would be representable
+ in an integer type. */
+ double const tab[] = {
+ 0x1P+000,
+ 0x1P+001,
+ 0x1P+002,
+ 0x1P+004,
+ 0x1P+008,
+ 0x1P+016,
+ 0x1P+032,
+ 0x1P+064,
+#if (DBL_MAX_EXP*FLT_RDXRDX) > 128
+ 0x1P+128,
+#endif
+#if (DBL_MAX_EXP*FLT_RDXRDX) > 256
+ 0x1P+256,
+#endif
+#if (DBL_MAX_EXP*FLT_RDXRDX) > 512
+ 0x1P+512,
+#endif
+#if (DBL_MAX_EXP*FLT_RDXRDX) > 1024
+ 0x1P+1024,
+#endif
+#if (DBL_MAX_EXP*FLT_RDXRDX) > 2048
+ 0x1P+2048,
+#endif
+#if (DBL_MAX_EXP*FLT_RDXRDX) > 4096
+ 0x1P+4096,
+#endif
+ };
+ /* First estimate log2(log2(a)). This will be the most significant
+ bit of the exponent that we are looking for. */
+ constexpr size_t k_max = sizeof tab/sizeof tab[0];
+ unsigned k = 1u;
+ while ((k < k_max) && (tab[k]*a < 1.0))
+ ++k;
+ double x = tab[k-1];
+ a *= x;
+ k -= 2u;
+ /* Now test all the smaller bit positions in the exponent. */
+ for (;k;--k) {
+ if (tab[k]*a < 1.0) {
+ a *= tab[k];
+ x *= tab[k];
+ }
+ }
+ return x;
+}
+
+double heron1_estimate_dir_10(double a) [[__unsequenced__]] {
+ /* A table of inverses of double powers of 2. */
+ /* Something like 1.0/(2 << (2 << k)) if only that would be
+ representable in an integer type. */
+ double const tab[] = {
+ 0x1P-000,
+ 0x1P-001,
+ 0x1P-002,
+ 0x1P-004,
+ 0x1P-008,
+ 0x1P-016,
+ 0x1P-032,
+ 0x1P-064,
+#if (DBL_MIN_EXP*FLT_RDXRDX) < -128
+ 0x1P-128,
+#endif
+#if (DBL_MIN_EXP*FLT_RDXRDX) < -256
+ 0x1P-256,
+#endif
+#if (DBL_MIN_EXP*FLT_RDXRDX) < -512
+ 0x1P-512,
+#endif
+#if (DBL_MIN_EXP*FLT_RDXRDX) < -1024
+ 0x1P-1024,
+#endif
+#if (DBL_MIN_EXP*FLT_RDXRDX) < -2048
+ 0x1P-2048,
+#endif
+#if (DBL_MIN_EXP*FLT_RDXRDX) < -4096
+ 0x1P-4096,
+#endif
+ };
+ /* First estimate -log2(log2(a)). This will be the most significant
+ bit of the exponent that we are looking for. */
+ constexpr size_t k_max = sizeof tab/sizeof tab[0];
+ unsigned k = 1u;
+ while ((k < k_max) && (tab[k]*a >= 0.5))
+ ++k;
+ double x = tab[k-1];
+ a *= x;
+ k -= 2u;
+ /* Now test all the smaller bit positions in the exponent. */
+ for (;k;--k) {
+ if (tab[k]*a >= 0.5) {
+ a *= tab[k];
+ x *= tab[k];
+ }
+ }
+ return x;
+}
+
+double heron1_estimate_dir(double a) [[__unsequenced__]] {
+ if (a < 0.5) return heron1_estimate_dir_05(a);
+ else if (a >= 1.0) return heron1_estimate_dir_10(a);
+ else return 1.0;
+}
+
+double heron1(double a) [[__unsequenced__]] {
+ constexpr double epsP1 = 1.0 + 1E-9;
+ constexpr double epsM1 = 1.0 - 1E-9;
+ double x = heron1_estimate_dir(a);
+ /* We are close, correct with the Heron factor. */
+ for (double prod = a*x;
+ ((prod < epsM1) || (epsP1 < prod));
+ prod = a*x) {
+ x *= (2.0 - prod);
+ }
+ return x;
+}
+
+double expk(double a, signed k) [[__unsequenced__]] {
+ if (k < 0) return heron1(expk_rec(a, -k));
+ if (k) return expk_rec(a, k);
+ else return 1.0;
+}
+
+double heron(double a, signed k) [[__unsequenced__]] {
+ switch (k) {
+ case 0: return 1.0;
+ case 1: return a;
+ }
+ if (k < 0) return heron1(heron(a, -k));
+ if (a <= 0.0) return 0.0;
+ constexpr double eps = 1E-12;
+ double const k1 = heron1(k);
+ double const a1 = heron1(a);
+ double x = (1.0 + a)*k1;
+ for (;;) {
+ double xk1 = expk(x, k-1);
+ double axk1 = a*heron1(xk1);
+ if (fabs(1.0 - x*xk1*a1) < eps) break;
+ x = ((k-1)*x + axk1)*k1;
+ }
+ return x;
+}
+
+/* A normal project would place the `main` in a different compilation
+ unit. */
+int main(int argc, char* argv[argc+1]) {
+ /* Read the first command line argument for the exponent. */
+ int k = strtol(argv[1], 0, 0);
+ for (int i = 2; i < argc; ++i) {
+ /* Read one command line argument as a double. */
+ double const a = strtod(argv[i], 0);
+ double const x = heron(a, k);
+ signed e0; double m0 = frexp_np(x, &e0);
+ signed e1; double m1 = frexp(x, &e1);
+ printf("heron: a=%.5e,\tx=%.5e,\tx**k=%.20e, %gT%+d, %gT%+d\n",
+ a, x, expk(x, k), m0, e0, m1, e1);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/heron_k.h b/heron_k.h
new file mode 100644
index 0000000..7a2c9fd
--- /dev/null
+++ b/heron_k.h
@@ -0,0 +1,133 @@
+#ifndef HERON_K_H
+#define HERON_K_H 1
+
+#include <stdio.h>
+/* Math functions usually need "-lm" as compiler argument: */
+#include <math.h>
+//#include <assert.h>
+#include <float.h>
+#include "c23-fallback.h"
+
+
+/**
+ ** @file
+ ** @brief Implement a Heron process to approximate powers of `1/k`
+ **/
+
+
+/**
+ ** @def FLT_RDXRDX
+ ** @brief the radix base 2 of @c FLT_RADIX
+ **
+ ** This is needed internally for some of the code below.
+ **/
+#if FLT_RADIX == 2
+# define FLT_RDXRDX 1
+#elif FLT_RADIX == 4
+# define FLT_RDXRDX 2
+#elif FLT_RADIX == 8
+# define FLT_RDXRDX 3
+#elif FLT_RADIX == 16
+# define FLT_RDXRDX 4
+#else
+# error "encoutered platform with unusual FLT_RADIX, please report"
+/**
+ ** @def FLT_RDXRDX
+ ** @brief the radix base 2 of @c FLT_RADIX
+ **
+ ** This is needed internally for some of the code below.
+ **/
+# define FLT_RDXRDX something
+#endif
+
+/**
+ ** @brief raise @a to the power of @a k
+ **
+ ** @warning manual implementation as an exercise, never use in
+ ** production code
+ **
+ ** @pre @a k must be a power of 2.0
+ **/
+double expk2(double a, unsigned k) [[__unsequenced__]];
+
+/**
+ ** @brief raise @a to the power of @a k
+ **
+ ** @warning manual implementation as an exercise, never use in
+ ** production code
+ **
+ ** @pre @a a must be strictly greater than 0.
+ **/
+double expk_rec(double a, unsigned k) [[__unsequenced__]];
+
+/**
+ ** @brief convert floating-point number to fractional and integral components
+ **
+ ** @warning manual implementation as an exercise, never use in
+ ** production code
+ **
+ ** This implementation just repeatedly multiplies the input value by
+ ** 2.0 or 0.5.
+ **/
+double frexp_np(double x, signed exp[static 1]) [[__unsequenced__]];
+
+/**
+ ** @brief compute a good estimate for the Heron process for k=-1
+ ** @return x is a power of 2 such that 0.5 <= x*a < 1.0
+ ** @pre Supposes that a > 0
+ **/
+double heron1_estimate(double a) [[__unsequenced__]];
+
+/**
+ ** @brief compute a good estimate for the Heron process for k=-1
+ ** @return x is a power of 2 such that 0.5 <= x*a < 1.0
+ ** @pre Supposes that 0 < a < 0.5
+ **/
+double heron1_estimate_dir_05(double a) [[__unsequenced__]];
+
+/**
+ ** @brief compute a good estimate for the Heron process for k=-1
+ ** @return x is a power of 2 such that 0.5 <= x*a < 1.0
+ ** @pre Supposes that a > 1.0
+ **/
+double heron1_estimate_dir_10(double a) [[__unsequenced__]];
+
+/**
+ ** @brief compute a good estimate for the Heron process for k=-1
+ ** @return x is a power of 2 such that 0.5 <= x*a < 1.0
+ ** @pre Supposes that a > 0.0
+ **/
+double heron1_estimate_dir(double a) [[__unsequenced__]];
+
+/**
+ ** @brief use the Heron process to approximate 1.0/a
+ ** @pre Supposes that a > 0.0
+ **/
+double heron1(double a) [[__unsequenced__]];
+
+/**
+ ** @brief raise @a to the power of @a k
+ **
+ ** @warning manual implementation as an exercise, never use in
+ ** production code
+ **/
+double expk(double a, signed k) [[__unsequenced__]];
+
+/**
+ ** @brief use the Heron process to approximate @a a to the
+ ** power of `1/k`
+ **
+ ** Or in other words this computes the @f$k^{th}@f$ root of @a a.
+ ** As a special feature, if @a k is `-1` it computes the
+ ** multiplicative inverse of @a a.
+ **
+ ** @param a must be greater than `0.0`
+ ** @param k should not be `0` and otherwise be between
+ ** `DBL_MIN_EXP*FLT_RDXRDX` and
+ ** `DBL_MAX_EXP*FLT_RDXRDX`.
+ **
+ ** @see FLT_RDXRDX
+ **/
+double heron(double a, signed k) [[__unsequenced__]];
+
+#endif
diff --git a/life.c b/life.c
new file mode 100644
index 0000000..157cad1
--- /dev/null
+++ b/life.c
@@ -0,0 +1,264 @@
+#include "life.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include "termin.h"
+
+
+void life_sleep(double s) {
+ struct timespec duration = {
+ .tv_sec = s,
+ .tv_nsec = (s-(size_t)s)*1E9,
+ };
+ while (thrd_sleep(&duration, &duration)) {
+ // Empty
+ }
+}
+
+int life_wait(cnd_t* cnd, mtx_t* mtx) {
+ struct timespec now;
+ timespec_get(&now, TIME_UTC);
+ now.tv_sec += 1;
+ return cnd_timedwait(cnd, mtx, &now);
+}
+
+void life_advance(life* L, signed t0, signed t1);
+
+static
+bool life_alive(bool l, unsigned x) {
+ return x == 3 || (l && x ==2);
+}
+
+// Computes the number of neighbors and stores them in B
+void life_count(life* L) {
+ size_t const n1 = L->n1;
+ bool (*const restrict M)[n1] = (void*)L->Mv;
+ size_t const off0 = L->off0;
+ size_t const off1 = L->off1;
+ size_t const len0 = L->len0;
+ size_t const len1 = L->len1;
+ if (!L->Bv) L->Bv = malloc(sizeof(unsigned char[len0][len1]));
+ unsigned char (*restrict B)[len1] = L->Bv;
+ for (size_t j0 = 0; j0 < len0; ++j0) {
+ size_t i0 = off0 + j0;
+ for (size_t j1 = 0; j1 < len1; ++j1) {
+ size_t i1 = off1 + j1;
+ B[j0][j1] =
+ + M[i0-1][i1-1] + M[i0-1][i1] + M[i0-1][i1+1]
+ + M[i0 ][i1-1] + 0 + M[i0 ][i1+1]
+ + M[i0+1][i1-1] + M[i0+1][i1] + M[i0+1][i1+1];
+ }
+ }
+}
+
+size_t life_update(life* L) {
+ size_t const n1 = L->n1;
+ bool (*const restrict M)[n1] = (void*)L->Mv;
+ size_t const off0 = L->off0;
+ size_t const off1 = L->off1;
+ size_t const len0 = L->len0;
+ size_t const len1 = L->len1;
+ unsigned char (*restrict B)[len1] = L->Bv;
+ size_t changed = 0;
+ if (B) {
+ for (size_t j0 = 0; j0 < len0; ++j0) {
+ size_t i0 = off0 + j0;
+ for (size_t j1 = 0; j1 < len1; ++j1) {
+ size_t i1 = off1 + j1;
+ bool newval = life_alive(M[i0][i1], B[j0][j1]);
+ changed += (M[i0][i1] != newval);
+ M[i0][i1] = newval;
+ }
+ }
+ }
+ L->Bv = nullptr;
+ free(B);
+ return changed;
+}
+
+void life_torus(life* L) {
+ size_t const n0 = L->n0;
+ size_t const n1 = L->n1;
+ bool (*const restrict M)[n1] = L->Mv;
+ for (size_t i0 = 1; i0 < n0; ++i0) {
+ M[i0][0] = M[i0][n1-2];
+ M[i0][n1-1] = M[i0][1];
+ }
+ for (size_t i1 = 1; i1 < n1; ++i1) {
+ M[0] [i1] = M[n0-2][i1];
+ M[n0-1][i1] = M[1] [i1];
+ }
+ M[0] [0] = M[n0-2][n1-2];
+ M[n0-1][0] = M[1] [n1-2];
+ M[0] [n1-1] = M[n0-2][1];
+ M[n0-1][n1-1] = M[1] [1];
+}
+
+
+
+#define DOT "⦿"
+#define SPACE " "
+#define WROOK ESC_TBLUE "♖" ESC_NORMAL
+#define BROOK ESC_TBLUE "♜" ESC_NORMAL
+
+static
+void border(size_t m, int b) {
+ fputs(ESC_TRED, stdout);
+ fputs(ESC_BORDER[b & ~esc_right], stdout);
+ for (size_t j = 0; j < m; ++j)
+ fputs("━", stdout);
+ fputs(ESC_BORDER[b & ~esc_left], stdout);
+ fputs(ESC_NORMAL, stdout);
+ esc_move(stdout, 1, -(m+2));
+}
+
+void life_draw(life* L) {
+ size_t const n1 = L->n1;
+ bool (*const restrict M)[n1] = L->Mv;
+ size_t const x0 = L->x0;
+ size_t const x1 = L->x1;
+ size_t const off0 = L->off0;
+ size_t const off1 = L->off1;
+ size_t const len0 = L->len0;
+ size_t const len1 = L->len1;
+
+ fputs(ESC_SAVE ESC_HOME ESC_HIDE, stdout);
+ border(len1, esc_top|esc_right|esc_left);
+ for (size_t i0 = off0; i0 < off0+len0; ++i0) {
+ fputs(ESC_TRED, stdout);
+ fputs(ESC_BORDER[esc_left], stdout);
+ fputs(ESC_NORMAL, stdout);
+ for (size_t i1 = off1; i1 < off1+len1; ++i1) {
+ if (i1 == x1 && x0 == i0) fputs(M[i0][i1] ? WROOK : BROOK, stdout);
+ else fputs(M[i0][i1] ? DOT : SPACE, stdout);
+ }
+ fputs(ESC_TRED, stdout);
+ fputs(ESC_BORDER[esc_right], stdout);
+ fputs(ESC_NORMAL, stdout);
+ esc_move(stdout, 1, -(len1+2));
+ }
+ border(len1, esc_bottom|esc_right|esc_left);
+ esc_goto(stdout, len0+3, 1);
+ fprintf(stdout, ESC_CLEAR "%3zu FPS, %5zu iterations, %5zu birth9, %5zu constellations, " ESC_BOLD "%6.2f" ESC_NORMAL " quotient",
+ L->frames, L->iteration, L->birth9, L->constellations, L->constellations*1.0/L->birth9);
+ fputs(ESC_RESTORE ESC_SHOW, stdout);
+ esc_goto(stdout, len0+4, 1);
+}
+
+void life_draw4(life* L) {
+ size_t const n1 = L->n1;
+ bool (*const restrict M)[n1] = L->Mv;
+ size_t const x0 = L->x0;
+ size_t const x1 = L->x1;
+ size_t const off0 = L->off0;
+ size_t const off1 = L->off1;
+ size_t const len0 = L->len0;
+ size_t const len1 = L->len1;
+
+ fputs(ESC_SAVE ESC_HOME ESC_HIDE, stdout);
+ border(len1/2, esc_top|esc_right|esc_left);
+ for (size_t i0 = off0; i0 < off0+len0; i0 += 2) {
+ fputs(ESC_TRED, stdout);
+ fputs(ESC_BORDER[esc_left], stdout);
+ fputs(ESC_NORMAL, stdout);
+ for (size_t i1 = off1; i1 < off1+len1; i1 += 2) {
+ char const* str = nullptr;
+ if ((i1 == x1 || i1+1 == x1) && (x0 == i0 || x0 == i0+1)) {
+ // The cursor is a blue dot in the correct position, but the
+ // three cell positions are also hidden. We grey them out.
+ static char const*const corner[4] = {
+ [0] = ESC_TBLUE ESC_BGREY "▗" ESC_NORMAL,
+ [1] = ESC_TBLUE ESC_BGREY "▖" ESC_NORMAL,
+ [2] = ESC_TBLUE ESC_BGREY "▝" ESC_NORMAL,
+ [3] = ESC_TBLUE ESC_BGREY "▘" ESC_NORMAL,
+ };
+ unsigned ty = (x0%2)<<1 | (x1%2);
+ str = corner[ty];
+ } else {
+ unsigned val = M[i0][i1]
+ | (M[i0][i1+1] << 1)
+ | (M[i0+1][i1] << 2)
+ | (M[i0+1][i1+1] << 3);
+ str = ESC_BLOCK[val];
+ }
+ fputs(str, stdout);
+ }
+ fputs(ESC_TRED, stdout);
+ fputs(ESC_BORDER[esc_right], stdout);
+ fputs(ESC_NORMAL, stdout);
+ esc_move(stdout, 1, -(len1/2+2));
+ }
+ border(len1/2, esc_bottom|esc_right|esc_left);
+ esc_goto(stdout, len0/2+3, 1);
+ fprintf(stdout, ESC_CLEAR "%3zu FPS, %5zu iterations, %5zu birth9, %5zu constellations, " ESC_BOLD "%6.2f" ESC_NORMAL " quotient",
+ L->frames, L->iteration, L->birth9, L->constellations, L->constellations*1.0/L->birth9);
+ fputs(ESC_RESTORE ESC_SHOW, stdout);
+ esc_goto(stdout, len0+4, 1);
+}
+
+static
+bool hashin(bool (*visited)[life_maxit], size_t hash) {
+ bool ret = false;
+ hash %= life_maxit;
+ if (!(*visited)[hash]) {
+ (*visited)[hash] = true;
+ ret = true;
+ }
+ return ret;
+}
+
+void life_account(life* L) {
+ size_t const n = L->n0;
+ size_t const m = L->n1;
+ bool (*const restrict M)[m] = L->Mv;
+ // Computes a hash of the new state
+ size_t hash = 0;
+ for (size_t i = 1; i < n-2; ++i) {
+ for (size_t j = 1; j < m-2; ++j) {
+ hash = 37*hash + M[i][j] + 7;
+ }
+ }
+ bool is_new = hashin(L->visited, hash);
+ if (is_new) {
+ L->constellations++;
+ L->last = L->accounted;
+ }
+ L->accounted++;
+}
+
+void life_birth9(life* L) {
+ size_t const n0 = L->n0;
+ size_t const n1 = L->n1;
+ size_t const x = L->x0;
+ size_t const y = L->x1;
+ bool (*const restrict M)[n1] = L->Mv;
+ for (size_t i = n0-4; i < n0-1; ++i) {
+ size_t xi = ((x+i)%(n0-2)) + 1;
+ for (int j = n1-4; j < n1-1; ++j) {
+ size_t yj = ((y+j)%(n1-2)) + 1;
+ M[xi][yj] = true;
+ }
+ }
+ L->birth9++;
+}
+
+life* life_init(life* L, size_t n, size_t m, bool mat[static n][m]) {
+ if (L) {
+ L->n0 = n;
+ L->n1 = m;
+ L->off0 = 1;
+ L->len0 = n-2;
+ L->off1 = 1;
+ L->len1 = m-2;
+ L->visited = calloc(sizeof(bool), life_maxit);
+ L->Mv = mat;
+ mtx_init(&L->mtx, mtx_plain);
+ cnd_init(&L->draw);
+ cnd_init(&L->acco);
+ cnd_init(&L->upda);
+ }
+ return L;
+}
+
+void life_destroy(life* L) {
+ if (L) free(L->visited);
+}
diff --git a/life.h b/life.h
new file mode 100644
index 0000000..d5f7eec
--- /dev/null
+++ b/life.h
@@ -0,0 +1,83 @@
+#include "c23-fallback.h"
+#include <ctype.h>
+#ifndef __STDC_NO_THREADS__
+# include <threads.h>
+#else
+# error This needs C11 threads, aborting.
+#endif
+#include <stdatomic.h>
+
+
+enum { life_maxit = 1ull << 23, };
+
+typedef struct life life;
+struct life {
+ mtx_t mtx; //< Mutex that protects Mv
+ cnd_t draw; //< cnd that controls drawing
+ cnd_t acco; //< cnd that controls accounting
+ cnd_t upda; //< cnd that controls updating
+
+ void*restrict Mv; //< bool M[n0][n1];
+ bool (*visited)[life_maxit]; //< Hashing constellations
+
+ // A bunch of parameters that are not subject to change
+ size_t n0; //< Number of rows
+ size_t n1; //< Number of columns
+ size_t off0; //< Start row
+ size_t len0; //< Number of rows to be handled
+ size_t off1; //< Start column
+ size_t len1; //< Number of columns to be handled
+ void* restrict Bv; //< Unsigned char B[len0][len1];
+
+
+ // Parameters that are updated but that are protected by the mutex
+ size_t iteration; //< Current iteration
+ size_t accounted; //< Last accounted iteration
+ size_t drawn; //< Last drawn iteration
+ size_t last; //< Last iteration with new state
+ size_t birth9; //< Number of birth9 calls
+
+ // Parameters that will dynamically be changed by
+ // different threads
+ _Atomic(size_t) constellations; //< Constellations visited
+ _Atomic(size_t) x0; //< Cursor position, row
+ _Atomic(size_t) x1; //< Cursor position, column
+ _Atomic(size_t) frames; //< FPS for display
+ _Atomic(bool) finished; //< This game is finished.
+};
+
+inline
+void life_advance(life* L, signed t0, signed t1) {
+ if (t0) {
+ size_t n = L->n0-2;
+ L->x0 = ((L->x0-1)+n+t0)%n + 1;
+ }
+ if (t1) {
+ size_t n = L->n1-2;
+ L->x1 = ((L->x1-1)+n+t1)%n + 1;
+ }
+}
+
+int life_wait(cnd_t*, mtx_t*);
+void life_sleep(double s);
+
+void life_birth9(life*);
+size_t life_update(life*restrict L);
+void life_count(life*restrict L);
+void life_draw(life*);
+void life_draw4(life*);
+void life_account(life*);
+life* life_init(life*, size_t _n, size_t _m, bool[_n][_m]);
+void life_destroy(life* L);
+void life_torus(life*);
+
+#define LIFE_INITIALIZER \
+{ \
+ .visited = 0, \
+ .birth9 = 1, \
+ .x0 = 1, \
+ .x1 = 1, \
+ .frames = 20, \
+ .finished = false, \
+ .constellations = 1, \
+}
diff --git a/lifetime.c b/lifetime.c
new file mode 100644
index 0000000..ed12c10
--- /dev/null
+++ b/lifetime.c
@@ -0,0 +1,89 @@
+#include <stdio.h>
+#include "c23-fallback.h"
+
+void fgoto(unsigned n) {
+ unsigned j = 0;
+ unsigned* p = nullptr;
+ unsigned* q;
+ AGAIN:
+ if (p) printf("%u: p and q are %s, *p is %u\n",
+ j,
+ (q == p) ? "equal" : "unequal",
+ *p);
+ q = p;
+ p = &((unsigned){ j, }); /*@\label{lifetime_compound_literal}*/
+ ++j;
+ if (j <= n) goto AGAIN;
+}
+
+void fgotoblock(unsigned n) {
+ unsigned j = 0;
+ unsigned* p = nullptr;
+ unsigned* q;
+ AGAIN: // Using a compound statement creates a new object at each iteration.
+ { // A good modern compiler should complain that it is uninitialized.
+ if (p) printf("%u: p and q are %s, *p is %u\n",
+ j,
+ (q == p) ? "equal" : "unequal",
+ *p);
+ q = p;
+ p = &((unsigned){ j, });
+ ++j;
+ if (j <= n) goto AGAIN;
+ }
+}
+
+__attribute__((noinline))
+void ffor1(void) {
+ unsigned j = 1;
+ printf("%u: p and q are %s, *p is %u\n",
+ j,
+ "unequal",
+ j-1);
+}
+
+__attribute__((noinline))
+void fforn(unsigned n) {
+ ffor1();
+ for (unsigned j = 2; j <= n; ++j) {
+ printf("%u: p and q are %s, *p is %u\n",
+ j,
+ "equal",
+ j-1);
+ }
+}
+
+void ffor(unsigned n) {
+ switch (n) {
+ case 0: break;
+ case 1: ffor1(); break;
+ default: fforn(n); break;
+ }
+}
+
+void fVLA(unsigned n) {
+ unsigned volatile j = 0;
+ unsigned* p = nullptr;
+ unsigned* q;
+ AGAIN:
+ {
+ if (p) printf("%u: p and q are %s\n",
+ j,
+ (q == p) ? "equal" : "unequal");
+ q = p;
+ unsigned VLA[j+1];
+ for (unsigned i = 0; i <= j; ++i)
+ VLA[i] = j;
+ p = VLA;
+ ++j;
+ if (j <= n) goto AGAIN;
+ }
+}
+
+
+
+int main(int argc, [[maybe_unused]] char* argv[]) {
+ fgoto(argc+1);
+ fgotoblock(argc+1);
+ ffor(argc+1);
+}
diff --git a/literals.c b/literals.c
new file mode 100644
index 0000000..f2c577e
--- /dev/null
+++ b/literals.c
@@ -0,0 +1,7 @@
+char const A[] = { 'e', 'n', 'd', '\0', };
+char const B[] = { 'e', 'n', 'd', '\0', };
+char const* c = "end";
+char const* d = "end";
+char const* e = "friend";
+char const* f = (char const[]){ 'e', 'n', 'd', '\0', };
+char const* g = (char const[]){ 'e', 'n', 'd', '\0', };
diff --git a/locale.c b/locale.c
new file mode 100644
index 0000000..8e258f2
--- /dev/null
+++ b/locale.c
@@ -0,0 +1,22 @@
+#include <locale.h>
+#include <stdio.h>
+
+/* You may have to unset the LANGUAGE environment variable for this to
+ work properly. */
+
+int main(int argc, char* argv[argc+1]) {
+ perror("With C locale");
+ if (!setlocale(LC_ALL, ""))
+ perror("couldn't set locale");
+ else
+ perror("With default locale");
+ if (!setlocale(LC_MESSAGES, "C"))
+ perror("couldn't set locale");
+ else
+ perror("C locale, again");
+ if (argv[1] && !setlocale(LC_MESSAGES, argv[1]))
+ perror("couldn't set locale");
+ else
+ perror("new locale");
+ printf("commandline argument was '%s'\n", argv[1]);
+}
diff --git a/macro_trace.c b/macro_trace.c
new file mode 100644
index 0000000..1ecda7a
--- /dev/null
+++ b/macro_trace.c
@@ -0,0 +1,40 @@
+#include "macro_trace.h"
+#include <tgmath.h>
+#include <stdlib.h>
+
+char const* trace_skip(char const expr[static 1]);
+
+void trace_values(FILE* s,
+ char const func[static 1],
+ char const line[static 1],
+ char const expr[static 1],
+ char const head[static 1],
+ size_t len, long double const arr[len]);
+
+
+int main(int argc, char* argv[]) {
+ double sum = argc > 1 ? strtod(argv[1], 0) : 0x1P-1;
+ TRACE_PRINT0("my favorite variable: %g\n", sum);
+ TRACE_PRINT1("my favorite variable: %g", sum);
+ TRACE_PRINT2("my favorite variable: %g", sum);
+ TRACE_VALUE0("my favorite value:", sum);
+ TRACE_PTR0("my favorite pointer:", argv);
+ TRACE_PTR1("my favorite pointer:", argv);
+ TRACE_PRINT3("my favorite variable: %g", sum);
+ TRACE_PRINT4("my favorite variable: %g", sum);
+ TRACE_PRINT5("my favorite variable: %g", sum);
+ TRACE_PRINT5("a good expression: %g", sum*argc);
+ TRACE_PRINT6("a collection: %g, %i", sum, argc);
+ TRACE_PRINT7("a string");
+ TRACE_PRINT8("a collection: %g, %i", sum, argc);
+ TRACE_PRINT8("another string");
+ TRACE_PRINT9("a collection: %g, %i", sum*acos(0), argc);
+ TRACE_VALUES("an untyped collection:", sum, argc, acos(0)*2);
+ TRACE_VALUES("an untyped \"collection\":", sum, argc, acos(0)*2);
+ TRACE_VALUES("just one element:", acos(0)*2);
+ TRACE_VALUES("empty");
+ TRACE_VALUES("\"empty\"");
+ TRACE_VALUE1("my favorite value:", sum);
+ TRACE_VALUE1("another value:", 0x1111111111111);
+ TRACE_VALUE1("my favorite pointer:", argv);
+}
diff --git a/macro_trace.h b/macro_trace.h
new file mode 100644
index 0000000..2125f50
--- /dev/null
+++ b/macro_trace.h
@@ -0,0 +1,302 @@
+#ifndef MACRO_TRACE_H
+#define MACRO_TRACE_H 1
+#include <stdio.h>
+#include "c23-fallback.h"
+
+/**
+ ** @file
+ **
+ ** @brief A sequence of macros for tracing execution
+ **/
+
+#if !__has_va_opt
+# error "no __VA_OPT__ support found, aborting compilation"
+// shut off some subsequent error messages
+# include "no __VA_OPT__ support found, aborting compilation"
+#endif
+
+/**
+ ** @brief A simple version of the macro that just does
+ ** a @c fprintf or nothing
+ **/
+#if NDEBUG
+# define TRACE_PRINT0(F, X) do { /* nothing */ } while (false)
+#else
+# define TRACE_PRINT0(F, X) fprintf(stderr, F, X)
+#endif
+
+/**
+ ** @brief A simple version of the macro that ensures that the @c
+ ** fprintf format is a string literal
+ **
+ ** As an extra, it also adds a newline to the printout, so
+ ** the user doesn't have to specify it each time.
+ **/
+#if NDEBUG
+# define TRACE_PRINT1(F, X) do { /* nothing */ } while (false)
+#else
+# define TRACE_PRINT1(F, X) fprintf(stderr, "" F "\n", X)
+#endif
+
+/**
+ ** @brief A macro that resolves to @c 0 or @c 1 according to @c
+ ** NDEBUG being set
+ **/
+#ifdef NDEBUG
+# define TRACE_ON 0
+#else
+# define TRACE_ON 1
+#endif
+
+/**
+ ** @brief A simple version of the macro that ensures that the @c fprintf call is always evaluated
+ **/
+#define TRACE_PRINT2(F, X) \
+do { if (TRACE_ON) fprintf(stderr, "" F "\n", X); } \
+ while (false)
+
+/**
+ ** @brief Traces a value without having to specify a format
+ **/
+#define TRACE_VALUE0(HEAD, X) TRACE_PRINT2(HEAD " %Lg", (X)+0.0L)
+
+/**
+ ** @brief Traces a pointer without having to specify a format
+ **
+ ** @warning Uses a cast of @a X to @c void*
+ **/
+#define TRACE_PTR0(HEAD, X) TRACE_PRINT2(HEAD " %p", (void*)(X))
+
+/**
+ ** @brief Traces a pointer without specifying a format
+ **/
+#define TRACE_PTR1(HEAD, X) \
+ TRACE_PRINT2(HEAD " %p", ((void*){ (X) }))
+
+/**
+ ** @brief Adds the current line number to the trace
+ **/
+#define TRACE_PRINT3(F, X) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, "%lu: " F "\n", __LINE__+0UL, X); \
+} while (false)
+
+/**
+ ** @brief Adds the name of the current function to the trace
+ **/
+#define TRACE_PRINT4(F, X) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, "%s:%lu: " F "\n", \
+ __func__, __LINE__+0UL, X); \
+} while (false)
+
+#define STRINGIFY(X) #X
+#define STRGY(X) STRINGIFY(X)
+/** @brief Adds a textual version of the expression that is evaluated
+ **/
+#define TRACE_PRINT5(F, X) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, \
+ "%s:" STRGY(__LINE__) ":(" #X "): " F "\n", \
+ __func__, X); \
+} while (false)
+
+/**
+ ** @brief Allows multiple arguments to be printed in the
+ ** same trace
+ **/
+#define TRACE_PRINT6(F, ...) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, "%s:" STRGY(__LINE__) ": " F "\n", \
+ __func__, __VA_ARGS__); \
+} while (false)
+
+/** @brief Only traces with a text message; no values printed
+ **/
+#define TRACE_PRINT7(...) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, \
+ "%s:" STRGY(__LINE__) ": " __VA_ARGS__ "\n", \
+ __func__); \
+} while (false)
+
+/**
+ ** @brief Extracts the first argument from a list of arguments
+ **/
+#define TRACE_FIRST(...) TRACE_FIRST0(__VA_ARGS__, 0)
+#define TRACE_FIRST0(_0, ...) _0
+
+/**
+ ** @brief Removes the first argument from a list of arguments
+ **
+ ** @remark This is only suitable in our context,
+ ** since this adds an artificial last argument.
+ **/
+#define TRACE_LAST(...) TRACE_LAST0(__VA_ARGS__, 0)
+#define TRACE_LAST0(_0, ...) __VA_ARGS__
+
+/**
+ ** @brief Traces with or without values
+ **
+ ** This implementation uses the C23 feature
+ ** `__VA_OPT__` to add a possibly empty variable
+ ** list at the end of the `fprintf` arguments.
+ **/
+#define TRACE_PRINT8(F, ...) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, "%s:" STRGY(__LINE__) ": " F "\n", \
+ __func__ __VA_OPT__(,) __VA_ARGS__); \
+} while (false)
+
+/**
+ ** @brief Traces by first giving a textual representation of the
+ ** arguments
+ **/
+#define TRACE_PRINT9(F, ...) \
+TRACE_PRINT8("(" #__VA_ARGS__ ") " F __VA_OPT__(,) __VA_ARGS__)
+
+inline
+char const* trace_skip(char const expr[static 1]){
+ for (char const* p = expr+1; *p; ++p) {
+ // at the end of head we expect the 3 chars ``", ''
+ if (p[0] == '"') return p+3;
+ // watch out for escaped " characters
+ if (p[0] == '\\' && p[1] == '"') ++p;
+ }
+ return 0;
+}
+
+/**
+ ** @brief A function to print a list of values
+ **
+ ** @remark Only call this through the macro ::TRACE_VALUES,
+ ** which will provide the necessary contextual information.
+ **/
+inline
+void trace_values(FILE* s,
+ char const func[static 1],
+ char const line[static 1],
+ char const expr[static 1],
+ char const head[static 1],
+ size_t len, long double const arr[len]) {
+ fprintf(s, "%s:%s:(%s) %s %Lg", func, line,
+ trace_skip(expr), head, arr[0]);
+ for (size_t i = 1; i < len-1; ++i)
+ fprintf(s, ", %Lg", arr[i]);
+ fputc('\n', s);
+}
+
+/**
+ ** @brief Returns the number of arguments in the variable list
+ **
+ ** This version works for lists with up to 31 elements.
+ **
+ ** @remark An empty argument list is taken as one (empty) argument.
+ **/
+#define ALEN(...) ALEN0(__VA_ARGS__, \
+ 0x1E, 0x1F, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, \
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, \
+ 0x0E, 0x0F, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, \
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00)
+
+#define ALEN0(_00, _01, _02, _03, _04, _05, _06, _07, _08, \
+ _09, _0A, _0B, _0C, _0D, _0F, _0E, _10, _11, \
+ _12, _13, _14, _15, _16, _17, _18, _19, _1A, \
+ _1B, _1C, _1D, _1F, _1E, ...) _1E
+
+/**
+ ** @brief Traces a list of arguments without having to specify
+ ** the type of each argument
+ **
+ ** @remark This constructs a temporary array with the arguments
+ ** all converted to @c long double. Thereby implicit conversion
+ ** to that type is always guaranteed.
+ **/
+#define TRACE_VALUES(...) \
+TRACE_VALUES0(ALEN(__VA_ARGS__), \
+ #__VA_ARGS__, \
+ __VA_ARGS__ \
+ )
+
+#define TRACE_VALUES0(NARGS, EXPR, HEAD, ...) \
+do { \
+ if (TRACE_ON) { \
+ if (NARGS > 1) \
+ trace_values(stderr, __func__, STRGY(__LINE__), \
+ "" EXPR "", "" HEAD "", NARGS, \
+ (long double const[NARGS]){ __VA_ARGS__ }); \
+ else \
+ fprintf(stderr, "%s:" STRGY(__LINE__) ": %s\n", \
+ __func__, HEAD); \
+ } \
+ } while (false)
+
+/**
+ ** @brief Returns a format that is suitable for @c fprintf
+ **
+ ** @return The argument @a F must be a string literal,
+ ** so the return value will also be one.
+ **
+ **/
+#define TRACE_FORMAT(F, X) \
+_Generic((X)+0LL, \
+ unsigned long long: "" F " %llu\n", \
+ long long: "" F " %lld\n", \
+ float: "" F " %.8f\n", \
+ double: "" F " %.12f\n", \
+ long double: "" F " %.20Lf\n", \
+ default: "" F " %p\n")
+
+/**
+ ** @brief Returns a value that forcibly can be interpreted as
+ ** pointer value
+ **
+ ** That is, any pointer will be returned as such, but other
+ ** arithmetic values will result in a @c nullptr.
+ **/
+#define TRACE_POINTER(X) \
+_Generic((X)+0LL, \
+ unsigned long long: nullptr, \
+ long long: nullptr, \
+ float: nullptr, \
+ double: nullptr, \
+ long double: nullptr, \
+ default: (X))
+
+/**
+ ** @brief Returns a value that is promoted either to a wide
+ ** integer, to a floating point, or to a @c void* if @a X is a
+ ** pointer
+ **/
+#define TRACE_CONVERT(X) \
+_Generic((X)+0LL, \
+ unsigned long long: (X)+0LL, \
+ long long: (X)+0LL, \
+ float: (X)+0LL, \
+ double: (X)+0LL, \
+ long double: (X)+0LL, \
+ default: ((void*){ nullptr } = TRACE_POINTER(X)))
+
+/**
+ ** @brief Traces a value without having to specify a format
+ **
+ ** This variant works correctly with non-void pointers.
+ **
+ ** The formats are tunable by changing the specifiers in
+ ** ::TRACE_FORMAT.
+ **/
+#define TRACE_VALUE1(F, X) \
+do { \
+ if (TRACE_ON) \
+ fprintf(stderr, \
+ TRACE_FORMAT("%s:" STRGY(__LINE__) ": " F, X), \
+ __func__, TRACE_CONVERT(X)); \
+ } while (false)
+
+#endif
diff --git a/mbstrings-main.c b/mbstrings-main.c
new file mode 100644
index 0000000..56a3838
--- /dev/null
+++ b/mbstrings-main.c
@@ -0,0 +1,206 @@
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "mbstrings.h"
+
+enum {
+ g_l = 1,
+ g_r = 2,
+ g_u = 4,
+ g_d = 8,
+};
+
+#define MONTH(X) \
+ (char const*[]){ \
+ [1] = "Jan", \
+ [2] = "Feb", \
+ [3] = "Mar", \
+ [4] = "Apr", \
+ [5] = "Mai", \
+ [6] = "Jun", \
+ [7] = "Jul", \
+ [8] = "Aug", \
+ [9] = "Sep", \
+ [10] = "Oct", \
+ [11] = "Nov", \
+ [12] = "Dec", \
+ }[(X)%100]
+
+#define STRINGIFY_(X) #X
+#define STRINGIFY(X) STRINGIFY_(X)
+
+#define YEAR(X) \
+(char const[5]){ \
+ STRINGIFY(X)[0], \
+ STRINGIFY(X)[1], \
+ STRINGIFY(X)[2], \
+ STRINGIFY(X)[3], \
+ }
+
+#define VBAR "\u2502" /**< a vertical bar character */
+#define HBAR "\u2500" /**< a horizontal bar character */
+#define TOPLEFT "\u250c" /**< topleft corner character */
+#define TOPRIGHT "\u2510" /**< topright corner character */
+
+/**
+ ** @brief Draw multibyte strings @a start and @a end separated
+ ** by a horizontal line.
+ **/
+void draw_sep(char const start[static 1],
+ char const end[static 1]) {
+ fputs(start, stdout);
+ size_t slen = mbsrlen(start, 0);
+ size_t elen = 90 - mbsrlen(end, 0);
+ for (size_t i = slen; i < elen; ++i) fputs(HBAR, stdout);
+ fputs(end, stdout);
+ fputc('\n', stdout);
+}
+
+char const* g_table[] = {
+ [0] = " ",
+ [g_l] = "\u2574",
+ [g_r] = "\u2576",
+ [g_l|g_r] = "\u2500",
+ [g_u] = "\u2575",
+ [g_l|g_u] = "\u2518",
+ [g_r|g_u] = "\u2514",
+ [g_l|g_r|g_u] = "\u2534",
+ [g_d] = "\u2577",
+ [g_l|g_d] = "\u2510",
+ [g_r|g_d] = "\u250c",
+ [g_l|g_r|g_d] = "\u252c",
+ [g_u|g_d] = "\u2502",
+ [g_l|g_u|g_d] = "\u2524",
+ [g_r|g_u|g_d] = "\u251c",
+ [g_l|g_r|g_u|g_d] = "\u253c",
+};
+
+int main(void) {
+ /* The first action of the executable should be locale switch. */
+ char const* locC_ = setlocale(LC_CTYPE, 0);
+ char locC[strlen(locC_)+1];
+ strcpy(locC, locC_);
+
+ setlocale(LC_ALL, "");
+ /* Multibyte character printing only works after the locale
+ has been switched. */
+ draw_sep(TOPLEFT " © 2014 jɛnz ˈgʊzˌtɛt ", TOPRIGHT);
+
+ char const* locLoc_ = setlocale(LC_CTYPE, 0);
+ char locLoc[strlen(locLoc_)+1];
+ strcpy(locLoc, locLoc_);
+ char const strhello[] = "Hä\u0131łłö";
+ char const u8hello[] = u8"Hä\u0131łłö";
+ wchar_t const wcshello[] = L"Hä\u0131łłö";
+ char const strvowel[] = "aeiouäëïöüáéíóúàèìòùæœ\u0131";
+ char const u8vowel[] = u8"aeiouäëïöüáéíóúàèìòùæœ\u0131";
+ wchar_t const wcsvowel[] = L"aeiouäëïöüáéíóúàèìòùæœ\u0131";
+ char16_t c16butterfly[] = u"\U000272CA";
+ char32_t c32butterfly[] = U"\U000272CA";
+ wchar_t wbutterfly[] = L"\U000272CA";
+ char u8butterfly[] = u8"\U000272CA";
+ bool mbs_u8 = (sizeof strvowel != sizeof u8vowel)
+ ? false :
+ (!strcmp(strvowel, u8vowel))
+ && (!strcmp("\U000272CA", (char const*)u8"\U000272CA"));
+ printf(VBAR "%ls world: switched \"%s\" \u21E8 \"%s\", multibyte strings are%s utf8, state%s\n",
+ wcshello, locC, locLoc,
+ mbs_u8 ? "" : " not",
+ mblen(nullptr, 0) ? "full" : "less");
+ printf(VBAR "wchar_t (%ssigned) is %zu bit, wint_t (%ssigned) is %zu bit, WEOF is ",
+ (WCHAR_MIN ? "" : "un"), sizeof(wchar_t)*CHAR_BIT,
+ (WINT_MIN ? "" : "un"), sizeof(wint_t)*CHAR_BIT);
+ if (WINT_MIN) printf("%lld\n", (long long)WEOF);
+ else printf("%#llx\n", (unsigned long long)WEOF);
+ printf(VBAR "wchar_t are Unicode code points as of %s %s, ",
+# ifdef __STDC_ISO_10646__
+ MONTH(__STDC_ISO_10646__),
+ YEAR(__STDC_ISO_10646__)
+# else
+ "<unknown>",
+ "<unknown>"
+# endif
+ );
+# ifdef __STDC_MB_MIGHT_NEQ_WC__
+ fputs("the basic character set is not extended to wchar_t\n", stdout);
+# else
+ fputs("wchar_t extends the basic character set\n", stdout);
+# endif
+#ifdef __STDC_UTF_16__
+ fputs(VBAR "char16_t is UTF-16, ", stdout);
+#else
+ fputs("char16_t is not UTF-16, ", stdout);
+#endif
+#ifdef __STDC_UTF_32__
+ fputs("char32_t is UTF-32\n", stdout);
+#else
+ fputs(VBAR "char32_t is not UTF-32\n", stdout);
+#endif
+ draw_sep(g_table[g_u|g_d|g_r], g_table[g_l|g_u|g_d]);
+ printf(VBAR "testing character %ls, codepoint U+%lX, utf8 %hhx %hhx %hhx %hhx %hhx:\n",
+ wbutterfly, 0x272CALU,
+ u8butterfly[0], u8butterfly[1], u8butterfly[2], u8butterfly[3], u8butterfly[4]);
+ printf(VBAR "\twchar_t,\tlength %zu,\t%s\n",
+ (sizeof(wbutterfly)/sizeof(wbutterfly[0])) - 1,
+ (sizeof(wbutterfly)/sizeof(wbutterfly[0])) == 2
+ ? "one word encoding" : "surrogate encoding");
+ printf(VBAR "\tchar32_t,\tlength %zu,\t%s\n",
+ (sizeof(c32butterfly)/sizeof(c32butterfly[0])) - 1,
+ (sizeof(c32butterfly)/sizeof(c32butterfly[0])) == 2
+ ? "one word encoding" : "surrogate encoding");
+ printf(VBAR "\tchar16_t,\tlength %zu,\t%s\n",
+ (sizeof(c16butterfly)/sizeof(c16butterfly[0])) - 1,
+ (sizeof(c16butterfly)/sizeof(c16butterfly[0])) == 2
+ ? "one word encoding" : "surrogate encoding");
+ if (sizeof(wchar_t) == sizeof(char32_t))
+ printf(VBAR "\twchar_t and char32_t encoding %s\n",
+ (sizeof(c32butterfly) == sizeof(wbutterfly)
+ && c32butterfly[0] == wbutterfly[0]
+ && c32butterfly[1] == wbutterfly[1]) ? "are equal" : "differ");
+ else if (sizeof(wchar_t) == sizeof(char16_t))
+ printf(VBAR "\twchar_t and char16_t encoding %s\n",
+ (sizeof(c16butterfly) == sizeof(wbutterfly)
+ && c16butterfly[0] == wbutterfly[0]
+ && c16butterfly[1] == wbutterfly[1]) ? "are equal" : "differ");
+ else
+ printf(VBAR "\twchar_t, char32_t and char16_t types and encodings all differ\n");
+ char const mbs0[] = u8"ł";
+ char const* pos0 = mbsrmb(u8hello, nullptr, mbs0, 0);
+ draw_sep(g_table[g_u|g_d|g_r], g_table[g_l|g_u|g_d]);
+ printf(VBAR "search first:\tfound \"%s\",\tposition %td, %s\n", mbs0, pos0-u8hello, pos0);
+ pos0 = mbsrmb(u8hello, nullptr, mbs0, 1);
+ printf(VBAR "search second:\tfound \"%s\",\tposition %td, %s\n", mbs0, pos0-u8hello, pos0);
+ char const mbs1[] = u8"ł";
+ char const* pos1 = mbsrrmb(u8hello, nullptr, mbs1);
+ printf(VBAR "search last:\tfound \"%s\",\tposition %td, %s\n", mbs1, pos1-u8hello, pos1);
+ char const mbs2[] = u8"łö";
+ char const* pos2 = mbsrmbs(u8hello, nullptr, mbs2);
+ printf(VBAR "search needle:\tfound \"%s\",\tposition %td, %s\n", mbs2, pos2-u8hello, pos2);
+ char const mbs3[] = u8" \u2003\u2002—";
+ size_t pos3 = mbsspn_class(mbs3, iswspace);
+ printf(VBAR "skip space:\tfound \"%s\",\tposition %zu, %s\n", mbs3, pos3, mbs3+pos3);
+ draw_sep(g_table[g_u|g_d|g_r], g_table[g_l|g_u|g_d]);
+ printf(VBAR "floats (locale):\t%g (C with .)\t%g (C with ,)\t%g (with .)\t%g (with ,)\n",
+ strtod("9.3", nullptr), strtod("9,3", nullptr),
+ mbsrtod("\u20039.3", nullptr, nullptr), mbsrtod("\u20039,3", nullptr, nullptr));
+ draw_sep(g_table[g_u|g_d|g_r], g_table[g_l|g_u|g_d]);
+ printf(VBAR "vowels\t%ls\n", wcsvowel);
+ char u8vowel2[MB_LEN_MAX + sizeof u8vowel];
+ char* partial = mbsrncpy(sizeof u8vowel-2, u8vowel2, nullptr, u8vowel, nullptr);
+ if (partial[0]) partial[0] = 0;
+ printf(VBAR "partial\t%s\n", u8vowel2);
+ for (char const* s = strhello; s && s[0];) {
+ s += mbscspn(s, strvowel);
+ int vowels = mbsspn(s, strvowel);
+ if (!vowels) {
+ printf("bizarre, no vowels in remaining string %s", s);
+ break;
+ }
+ printf(VBAR "%d bytes for vowel(s) %.*s\n", vowels, vowels, s);
+ s += vowels;
+ }
+ draw_sep(g_table[g_u|g_r], g_table[g_l|g_u]);
+}
diff --git a/mbstrings.c b/mbstrings.c
new file mode 100644
index 0000000..15d681c
--- /dev/null
+++ b/mbstrings.c
@@ -0,0 +1,346 @@
+#include <errno.h>
+//#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+
+#include "mbstrings.h"
+//#include "c23-fallback.h"
+
+/**
+ ** @file
+ ** @brief implement multibyte character string helpers
+ **/
+
+size_t mbrtow(wchar_t*restrict C, char const c[restrict static 1],
+ mbstate_t*restrict state) {
+ if (!state) state = MBSTATE;
+ size_t len = -2;
+ for (size_t maxlen = MB_LEN_MAX; len >= mbincomplete; maxlen *= 2)
+ len = mbrtowc(C, c, maxlen, state);
+ if (len == mbinvalid) errno = 0;
+ return len;
+}
+
+wint_t mbtow(char const*c) {
+ wchar_t C = 0;
+ size_t len = mbrtow(&C, c, MBSTATE);
+ return (len == mbinvalid) ? WEOF : C;
+}
+
+size_t mbsrlen(char const*s, mbstate_t const*restrict state) {
+ state = state ? state : MBSTATE;
+ mbstate_t st = *state;
+ size_t mblen = mbsrtowcs(nullptr, &s, 0, &st);
+ if (mblen == mbinvalid) errno = 0;
+ return mblen;
+}
+
+wchar_t* mbsrdup(char const*s, mbstate_t*restrict state) {
+ mbstate_t st = state ? *state : *MBSTATE;
+ size_t mblen = mbsrlen(s, &st);
+ if (mblen == mbinvalid) return nullptr;
+ wchar_t* S = malloc(sizeof(wchar_t[mblen+1]));
+ /* We know that s converts well, so no error check */
+ if (S) mbsrtowcs(S, &s, mblen+1, state);
+ return S;
+}
+
+#define SURROG0 0xD800L
+#define SURROG1 (SURROG0 + 1024)
+#define SURROG2 (SURROG1 + 1024)
+
+int iswhighsurrogate(wint_t x) {
+ return (SURROG0 <= x) && (x < SURROG1);
+}
+
+int iswlowsurrogate(wint_t x) {
+ return (SURROG1 <= x) && (x < SURROG2);
+}
+
+int iswsurrogate(wint_t x) {
+ return (SURROG0 <= x) && (x < SURROG2);
+}
+
+int iswvalid(wint_t x) {
+ return x && x != WEOF && !iswsurrogate(x);
+}
+
+char const* mbsrwc(char const s[restrict static 1], mbstate_t*restrict state,
+ wchar_t C, size_t occurrence) {
+ if (!C || C == WEOF) return nullptr;
+ state = state ? state : MBSTATE;
+ char const* ret = nullptr;
+
+ mbstate_t st = *state;
+ for (size_t len = 0; s[0]; s += len) {
+ mbstate_t backup = st;
+ wchar_t S = 0;
+ len = mbrtow(&S, s, &st);
+ if (!S) break;
+ if (C == S) {
+ *state = backup;
+ ret = s;
+ if (!occurrence) break;
+ --occurrence;
+ }
+ }
+ return ret;
+}
+
+char const* mbsrmb(char const s[static 1], mbstate_t*restrict state,
+ char const c[static 1], size_t occurrence) {
+ state = state ? state : MBSTATE;
+ wint_t C = mbtow(c);
+ return mbsrwc(s, state, C, occurrence);
+}
+
+char const* mbsrrwc(char const s[restrict static 1], mbstate_t*restrict state,
+ wchar_t C) {
+ return mbsrwc(s, state, C, SIZE_MAX);
+}
+
+char const* mbsrrmb(char const s[static 1], mbstate_t*restrict state,
+ char const c[static 1]) {
+ return mbsrmb(s, state, c, SIZE_MAX);
+}
+
+char const* mbsrwcjump(char const s1[static 1], mbstate_t*restrict state,
+ size_t S2len, wchar_t const S2[S2len]) {
+ state = state ? state : MBSTATE;
+ mbstate_t st = *state;
+ for (size_t i = 0; i < S2len; ++i) {
+ wchar_t S1 = 0;
+ s1 += mbrtow(&S1, s1, &st);
+ if (S1 != S2[i]) return nullptr;
+ }
+ *state = st;
+ return s1;
+}
+
+
+char const* mbsrwcs(char const s1[static 1], mbstate_t*restrict state,
+ wchar_t const* S2) {
+ if (!S2) return nullptr;
+ state = state ? state : MBSTATE;
+ size_t S2len = wcslen(S2);
+ switch (S2len) {
+ case 0: return nullptr;
+ case 1: return mbsrwc(s1, state, S2[0], 0);
+ default:;
+ /* Don't modify shift state until we found it. */
+ mbstate_t rstate = *state;
+ while (s1 && s1[0]) {
+ s1 = mbsrwc(s1, &rstate, S2[0], 0);
+ if (s1 && s1[0]) {
+ /* s1 now is at a potential starting point */
+ char const* ret = s1;
+ mbstate_t tstate = rstate;
+ if (mbsrwcjump(s1, &tstate, S2len, S2)) {
+ *state = rstate;
+ return ret;
+ }
+ /* Advance s1 to the next mb character. */
+ s1 += mbrtow(nullptr, s1, &rstate);
+ }
+ }
+ }
+ return nullptr;
+}
+
+char const* mbsrmbs(char const s1[static 1], mbstate_t*restrict state,
+ char const* s2) {
+ state = state ? state : MBSTATE;
+ wchar_t*restrict S2 = mbsrdup(s2, nullptr);
+ if (!S2 || !S2[0]) return nullptr;
+ s1 = mbsrwcs(s1, state, S2);
+ free(S2);
+ return s1;
+}
+
+char const* mbsrwcsskip(char const s1[static 1], mbstate_t*restrict state,
+ wchar_t const* S2){
+ if (S2) {
+ state = state ? state : MBSTATE;
+ mbstate_t st = *state;
+ for (size_t len; s1[0]; *state = st, s1 += len) {
+ wchar_t S1[3] = { };
+ len = mbrtow(&S1[0], s1, &st);
+ if (!S1[0]) break;
+ if (!iswlowsurrogate(S1[0])) {
+ if (!wcschr(S2, S1[0])) break;
+ } else {
+ len += mbrtow(&S1[1], s1, &st);
+ if (!wcsstr(S2, S1)) break;
+ }
+ }
+ *state = st;
+ }
+ return s1;
+}
+
+char const* mbsrskip(char const s1[static 1], mbstate_t*restrict state,
+ char const* s2) {
+ state = state ? state : MBSTATE;
+ wchar_t*restrict S2 = mbsrdup(s2, nullptr);
+ s1 = mbsrwcsskip(s1, state, S2);
+ free(S2);
+ return s1;
+}
+
+size_t mbsspn(char const* s1, char const* s2) {
+ return s1 ? mbsrskip(s1, 0, s2)-s1 : 0;
+}
+
+char const* mbsrskip_class(char const s1[static 1], mbstate_t*restrict state,
+ wcclass_t func) {
+ state = state ? state : MBSTATE;
+ for (size_t len; s1[0]; s1 += len) {
+ wchar_t S1 = 0;
+ len = mbrtow(&S1, s1, state);
+ if (!S1 || !func(S1)) break;
+ }
+ return s1;
+}
+
+size_t mbsspn_class(char const* s1, wcclass_t func) {
+ return s1 ? mbsrskip_class(s1, 0, func)-s1 : 0;
+}
+
+char const* mbsrskip_type(char const s1[static 1], mbstate_t*restrict state, wctype_t type) {
+ state = state ? state : MBSTATE;
+ for (size_t len; s1[0]; s1 += len) {
+ wchar_t S1 = 0;
+ len = mbrtow(&S1, s1, state);
+ if (!S1 || !iswctype(S1, type)) break;
+ }
+ return s1;
+}
+
+size_t mbsspn_type(char const* s1, wctype_t type) {
+ return s1 ? mbsrskip_type(s1, 0, type)-s1 : 0;
+}
+
+size_t mbsspn_name(char const* s1, char const name[static 1]) {
+ return mbsspn_type(s1, wctype(name));
+}
+
+char const* mbsrwcscskip(char const s1[static 1], mbstate_t*restrict state,
+ wchar_t const* S2) {
+ state = state ? state : MBSTATE;
+ if (S2) {
+ mbstate_t st = *state;
+ for (size_t len; s1[0]; s1 += len) {
+ wchar_t S1[3] = { };
+ len = mbrtow(&S1[0], s1, &st);
+ if (!S1[0]) break;
+ if (!iswlowsurrogate(S1[0])) {
+ if (wcschr(S2, S1[0])) break;
+ } else {
+ len += mbrtow(&S1[1], s1, &st);
+ if (wcsstr(S2, S1)) break;
+ }
+ }
+ *state = st;
+ }
+ return s1;
+}
+
+char const* mbsrcskip(char const* s1, mbstate_t*restrict state, char const* s2) {
+ state = state ? state : MBSTATE;
+ wchar_t*restrict S2 = mbsrdup(s2, nullptr);
+ s1 = mbsrwcscskip(s1, state, S2);
+ free(S2);
+ return s1;
+}
+
+size_t mbscspn(char const* s1, char const* s2) {
+ return (s1 && s2) ? mbsrcskip(s1, 0, s2)-s1 : 0;
+}
+
+
+double mbsrtod(char const*restrict s1, mbstate_t*restrict state,
+ char**restrict endptr) {
+ state = state ? state : MBSTATE;
+ return strtod(mbsrskip_class(s1, state, iswspace), endptr);
+}
+
+long double mbsrtold(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr) {
+ state = state ? state : MBSTATE;
+ return strtold(mbsrskip_class(s1, state, iswspace), endptr);
+}
+
+long mbsrtol(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base) {
+ state = state ? state : MBSTATE;
+ return strtol(mbsrskip_class(s1, state, iswspace), endptr, base);
+}
+
+long long mbsrtoll(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base) {
+ state = state ? state : MBSTATE;
+ return strtoll(mbsrskip_class(s1, state, iswspace), endptr, base);
+}
+
+unsigned long mbsrtoul(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base) {
+ state = state ? state : MBSTATE;
+ return strtoul(mbsrskip_class(s1, state, iswspace), endptr, base);
+}
+
+unsigned long long mbsrtoull(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base) {
+ state = state ? state : MBSTATE;
+ return strtoull(mbsrskip_class(s1, state, iswspace), endptr, base);
+}
+
+
+
+char* mbsrmbsncpy(size_t n, char t[restrict n], mbstate_t const*restrict state,
+ char const s [restrict static 1]) {
+ /* Don't do anything if s wouldn't fit entirely into t */
+ char const* ep = memchr(s, 0, n);
+ if (!ep) return nullptr;
+ size_t slen = ep - s;
+ /* If the target is in non-initial state, try to reset it. */
+ if (!mbsinit(state)) {
+ /* Now state is known to be non-null. */
+ char buf[2*MB_LEN_MAX];
+ mbstate_t st = *state;
+ size_t len = wcrtomb(buf, 0, &st);
+ /* See if shift characters plus s will fit into t. */
+ if (slen+len >= n) return nullptr;
+ /* write the bytes that end the shift state */
+ memcpy(t, buf, len);
+ t += len-1;
+ n -= len-1;
+ }
+ if (slen >= n) return nullptr;
+ memcpy(t, s, slen+1);
+ return t + slen;
+}
+
+char* mbsrncpy(size_t n, char t[restrict n], mbstate_t*restrict tstate,
+ char const s [restrict static 1], mbstate_t const*restrict sstate) {
+ /* First check if the bytes can just be copied over. */
+ if (mbsinit(sstate)) {
+ char* ret = mbsrmbsncpy(n, t, tstate, s);
+ if (ret) return ret;
+ }
+ char* tt = t;
+ tstate = tstate ? tstate : MBSTATE;
+ sstate = sstate ? sstate : MBSTATE;
+ mbstate_t sst = *sstate;
+ char buf[2*MB_LEN_MAX];
+ for (size_t slen = 0; s[0] && n; s += slen) {
+ wchar_t S[2] = { };
+ slen = mbrtowc(S, s, n, &sst);
+ /* Here, wcsrtombs can't fail, but because of the different state
+ of t, tlen may be larger than slen. */
+ wchar_t const* Sp = S;
+ mbstate_t tst = *tstate;
+ size_t tlen = wcsrtombs(buf, &Sp, n, &tst);
+ if (tlen > n) break;
+ *tstate = tst;
+ memcpy(tt, buf, tlen);
+ if (!S[0]) break;
+ tt += tlen;
+ n -= tlen;
+ }
+ return tt;
+}
diff --git a/mbstrings.h b/mbstrings.h
new file mode 100644
index 0000000..221720b
--- /dev/null
+++ b/mbstrings.h
@@ -0,0 +1,606 @@
+#include "c23-fallback.h"
+#include <wctype.h>
+#include <ctype.h>
+
+#if (__STDC_UTF_16__ != 1) || (__STDC_UTF_32__ != 1)
+# error "wide character strings should use UTF encodings"
+#endif
+
+/**
+ ** @file
+ ** @brief Helper functions for multibyte strings.
+ **
+ ** These functions try to ease the use of multibyte strings. Most of
+ ** the time they pass through wide character functions to achieve
+ ** their goal. This has only use that is somewhat limited if on
+ ** platform wide characters are not of fixed width but use
+ ** surrogates. For such surrogate characters the classification
+ ** functions will fail erratically.
+ **
+ ** @warning this implementations supposes that @c wchar_t supports
+ ** some form of Unicode.
+ **/
+
+#ifndef __STDC_ISO_10646__
+# warning "wchar_t wide characters have to be Unicode code points"
+#endif
+#ifdef __STDC_MB_MIGHT_NEQ_WC__
+# warning "basic character codes must agree on char and wchar_t"
+#endif
+
+/**
+ ** @brief A type for 16 bit characters as provided by "uchar.h"
+ **
+ ** This is the base type of strings that are prefixed with @c u, such
+ ** as in <code>u"string"</code>.
+ **
+ ** There are good chances that on most platforms this is the UCS-2
+ ** encoding, thus an encoding that has surrogates.
+ **
+ ** @remark This should be provided by the header file, but the type
+ ** is fixed as is given here, and C11 compilers should tolerate a
+ ** repetition of a @c typedef, as long as it is consistent.
+ **/
+typedef uint_least16_t char16_t;
+
+/**
+ ** @brief A type for 32 bit characters as provided by "uchar.h"
+ **
+ ** This is the base type of strings that are prefixed with @c U, such
+ ** as in <code>U"string"</code>.
+ **
+ ** There are good chances that on most platforms this is the UCS-4
+ ** encoding, thus an encoding without surrogates.
+ **
+ ** @remark This should be provided by the header file, but the type
+ ** is fixed as is given here, and C11 compilers should tolerate a
+ ** repetition of a @c typedef, as long as it is consistent.
+ **/
+typedef uint_least32_t char32_t;
+
+/**
+ ** @brief A helper type to ensure proper initialization of @c
+ ** mbstate_t variables
+ **
+ ** Not to be used directly
+ ** @see MBSTATE
+ **/
+typedef union mbstate_t_ {
+ unsigned char bytes[sizeof(mbstate_t)];
+ mbstate_t state;
+} mbstate_t_;
+
+/**
+ ** @brief Provide a pointer to a properly initialized @c mbstate_t
+ **
+ ** This uses the union type to ensure that the state is initialized
+ ** with all bytes set to @c 0.
+ **/
+#define MBSTATE (&(union mbstate_t_){ .bytes = { }}.state)
+
+/**
+ ** @brief For functions that receive a wide character classification
+ ** function as an argument.
+ **/
+typedef int (*wcclass_t)(wint_t);
+
+/**
+ ** @brief Interpret a sequence of bytes in @a c as mb character
+ ** and return that as wide character through @a C
+ **
+ ** @return the length of the mb character or @c mbinvalid if an
+ ** encoding error occured.
+ **
+ ** This function can be integrated into a sequence of such
+ ** searches through a string, as long as the same @a state
+ ** argument is passed to all calls to this or similar functions.
+ **
+ ** @remark @a state of null indicates that @a c can be scanned
+ ** without considering any context.
+ **/
+size_t mbrtow(wchar_t*restrict C, char const c[restrict static 1],
+ mbstate_t*restrict state);
+
+/**
+ ** @brief Interpret a sequence of bytes in @a c as mb character and
+ ** return that as wide character.
+ **
+ ** @return the wide character converted to @c wint_t or @c WEOF if an
+ ** encoding error occurred.
+ **
+ ** This function should only be used for strings containing one
+ ** single mb character. It cannot be integrated into a sequence of
+ ** searches through a string with more characters since the @c
+ ** mbstate_t is not kept.
+ **/
+wint_t mbtow(char const*c);
+
+/**
+ ** @brief Interpret a mb string in @a mbs and return its
+ ** length when interpreted as a wide character string
+ **
+ ** @return the length of the mb string or @c mbinvalid if an
+ ** encoding error occured.
+ **
+ ** This function can be integrated into a sequence of searches
+ ** through a string, as long as a @a state argument is passed to
+ ** this function that is consistent with the mb character
+ ** starting in @a mbs. The state itself is not modified by this
+ ** function.
+ **
+ ** @remark @a state of null indicates that @a mbs can be scanned
+ ** without considering any context.
+ **/
+size_t mbsrlen(char const*restrict mbs,
+ mbstate_t const*restrict state);
+
+/**
+ ** @brief Interpret a sequence of bytes in @a s as mb string and
+ ** convert it to a wide character string.
+ **
+ ** @return a newly malloc'ed wide character string of the
+ ** appropriate length, null if an encoding error occurred.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as a @a state
+ ** argument is passed to this function that is consistent with
+ ** the mb character starting in @a c. The state itself is not
+ ** modified by this function.
+ **
+ ** @remark @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+wchar_t* mbsrdup(char const*s, mbstate_t*restrict state);
+
+/**
+ ** @brief Copy at most @a n bytes of the mb string @a s to @a t.
+ **
+ ** This is meant to be an efficient variant of ::mbsrncpy for
+ ** the case the copy operation can be done with ::memcpy.
+ **
+ ** Mb string @a s is supposed to be a complete mb string in
+ ** initial shift state. If it has no @c 0 character in the first
+ ** @a n bytes, the copy operation fails and the function returns
+ ** null.
+ **
+ ** The mb string that is produced in @a t may be the
+ ** continuation of an existing prefix who's conversion state is
+ ** described by @a state.
+ **
+ ** If @a s can not completely be copied because the adjustment
+ ** of the shift state would leave less than
+ ** <code>strlen(s)+1</code> space in @a t, the copy operation is
+ ** aborted and null is returned.
+ **
+ ** @return the address of the terminating null character in @a t
+ ** if the copy operation succeeded, null otherwise. In case of
+ ** success, @a t is in an initial shift state for the returned
+ ** position.
+ **/
+char* mbsrmbsncpy(size_t n, char t[restrict n], mbstate_t const*restrict state,
+ char const s [restrict static 1]);
+
+
+/**
+ ** @brief Copy at most @a n bytes of the mb string @a s to @a t.
+ **
+ ** This is a safe variant of ::mbsrmbsncpy that covers all
+ ** cases.
+ **
+ ** Mb string @a s is supposed to be a mb string in state @a
+ ** sstate. If it has no @c 0 character in the first @a n bytes,
+ ** the copy operation will be partial up to that point.
+ **
+ ** The mb string that is produced in @a t may be the continuation of
+ ** an existing prefix who's conversion state is described by @a
+ ** sstate.
+ **
+ ** This function copies complete mb characters as long as they
+ ** fit into the target array. To be able to do so, the
+ ** conversion state of @a t might have to be adapted to be in
+ ** sync with @a s. By that the copied part may be longer than
+ ** <code>strlen(s)+1</code>.
+ **
+ ** @return The address of the first byte in @a t after the copy
+ ** is returned; that byte is null if the whole @a s could be
+ ** copied. If nothing could be copied, @a t is returned. In that
+ ** case @c *t is not written to and @a tstate is unchanged.
+ **/
+char* mbsrncpy(size_t n, char t[restrict n], mbstate_t*restrict tstate,
+ char const s [restrict static 1], mbstate_t const*restrict sstate);
+
+
+/**
+ ** @brief Interpret a sequence of bytes in @a s as mb string and
+ ** search for wide character @a C
+ **
+ ** @return the @a occurrence'th position in @a s that starts a
+ ** mb sequence corresponding to @a C or null if an encoding
+ ** error occurred.
+ **
+ ** If the number of occurrences is less than @a occurrence the
+ ** last such position is returned. So in particular using @c
+ ** SIZE_MAX (or @c -1) will always return the last occurrence.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrwc(char const s[restrict static 1],
+ mbstate_t*restrict state,
+ wchar_t C, size_t occurrence);
+
+/**
+ ** @brief Interpret a sequence of bytes in @a s as mb string and
+ ** search for mb character represented by @a c
+ **
+ ** @return the @a occurrence'th position in @a s that starts a
+ ** mb sequence corresponding to @a c or null if an encoding
+ ** error occurred.
+ **
+ ** If the number of occurrences is less than @a occurrence the
+ ** last such position is returned. So in particular using @c
+ ** SIZE_MAX (or @c -1) will always return the last occurrence.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrmb(char const s[static 1], mbstate_t*restrict state,
+ char const c[static 1], size_t occurence);
+
+/**
+ ** @brief Interpret a sequence of bytes in @a s as mb string and
+ ** and reverse search for wide character @a C
+ **
+ ** @return the maximal position in @a s that starts a mb
+ ** sequence corresponding to @a C or null if an encoding error
+ ** occured.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrrwc(char const s[restrict static 1], mbstate_t*restrict state,
+ wchar_t C);
+
+/**
+ ** @brief Interpret a sequence of bytes in @a s as mb string and
+ ** reverse search for mb character represented by @a c
+ **
+ ** @return the maximal position in @a s that starts a mb
+ ** sequence corresponding to @a c or null if an encoding error
+ ** occured.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrrmb(char const s[static 1], mbstate_t*restrict state,
+ char const c[static 1]);
+
+/**
+ ** @brief In mb string @a s1 jump over the initial segment
+ ** corresponding to wide character string @a S2.
+ **
+ ** @return the position in @a s1 after the initial segment if @a
+ ** S2 is found or null otherwise.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark A @a state that is null indicates that @a s1 can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrwcjump(char const s1[static 1], mbstate_t*restrict state,
+ size_t S2len, wchar_t const S2[S2len]);
+
+/**
+ ** @brief In mb string @a s1 find a segment corresponding to
+ ** wide character string @a S2.
+ **
+ ** @return the position in @a s1 that starts the first such
+ ** segment if @a S2 is found or null otherwise.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark A @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrwcs(char const s1[static 1], mbstate_t*restrict state,
+ wchar_t const* S2);
+
+/**
+ ** @brief In mb string @a s1 find a segment corresponding to mb
+ ** string @a s2.
+ **
+ ** @return the position in @a s1 that starts the first such
+ ** segment if @a s2 is found or null otherwise.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark A @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrmbs(char const s1[static 1], mbstate_t*restrict state,
+ char const* s2);
+
+/**
+ ** @brief In mb string @a s1 skip over the initial part of the
+ ** string that corresponds to wide characters in string @a S2.
+ **
+ ** @return the position in @a s1 that corresponds to the the
+ ** first non-matching wide character in @a S2, which may be the
+ ** terminating @c 0 character or a position that produces an
+ ** encoding error.
+ **
+ ** @a state is updated to correspond to the returned position.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark A @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrwcsskip(char const s1[static 1], mbstate_t*restrict state,
+ wchar_t const* S2);
+
+/**
+ ** @brief In mb string @a s1 skip over the initial part of the
+ ** string that corresponds to mb characters in string @a s2.
+ **
+ ** @return the position in @a s1 that corresponds to the the
+ ** first non-matching mb character in @a s2, which may be the
+ ** terminating @c 0 character or a position that produces an
+ ** encoding error.
+ **
+ ** @a state is updated to correspond to the returned position.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark A @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrskip(char const s1[static 1], mbstate_t*restrict state,
+ char const* s2);
+
+/**
+ ** @brief In mb string @a s1 return the length of the initial
+ ** part of the string that corresponds to mb characters in
+ ** string @a s2.
+ **
+ ** @return the position in @a s1 that corresponds to the the
+ ** first non-matching mb character in @a s2, which may be the
+ ** terminating @c 0 character or a position that produces an
+ ** encoding error.
+ **
+ ** @remark This function supposes that @a s1 can be scanned
+ ** without context, that is it starts in an initial shift state.
+ **/
+size_t mbsspn(char const* s1, char const* s2);
+
+/**
+ ** @brief In mb string @a s1 skip over the initial part of the
+ ** string that verifies classification @a func.
+ **
+ ** @return the position in @a s1 that corresponds to the the
+ ** first non-matching mb character in @a s2, which may be the
+ ** terminating @c 0 character or a position that produces an
+ ** encoding error.
+ **
+ ** @a state is updated to correspond to the returned position.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark A @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrskip_class(char const s1[static 1], mbstate_t*restrict state,
+ wcclass_t func);
+
+/**
+ ** @brief In mb string @a s1 return the length of the initial
+ ** part of the string that verifies classification @a func.
+ **
+ ** @return the position in @a s1 that corresponds to the the
+ ** first non-matching mb character in @a s2, which may be the
+ ** terminating @c 0 character or a position that produces an
+ ** encoding error.
+ **
+ ** @remark This function supposes that @a s1 can be scanned
+ ** without context, that is it starts in an initial shift state.
+ **/
+size_t mbsspn_class(char const* s1, wcclass_t func);
+
+/**
+ ** @brief In mb string @a s1 skip over the initial part of the
+ ** string that verifies type @a type.
+ **
+ ** @return the position in @a s1 that corresponds to the the
+ ** first non-matching mb character in @a s2, which may be the
+ ** terminating @c 0 character or a position that produces an
+ ** encoding error.
+ **
+ ** @a state is updated to correspond to the returned position.
+ **
+ ** @remark This function can be integrated into a sequence of
+ ** such searches through a string, as long as the same @a state
+ ** argument passed to all calls to this or similar functions and
+ ** as long as the continuation of the search starts at the
+ ** position that is returned by this function.
+ **
+ ** @remark A @a state of null indicates that @a s can be scanned
+ ** without considering any context.
+ **/
+char const* mbsrskip_type(char const s1[static 1], mbstate_t*restrict state, wctype_t type);
+
+/**
+ ** @brief In mb string @a s1 return the length of the initial
+ ** part of the string that verifies type @a type.
+ **
+ ** @return the position in @a s1 that corresponds to the the
+ ** first non-matching mb character in @a s2, which may be the
+ ** terminating @c 0 character or a position that produces an
+ ** encoding error.
+ **
+ ** @remark This function supposes that @a s1 can be scanned
+ ** without context, that is it starts in an initial shift state.
+ **/
+size_t mbsspn_type(char const* s1, wctype_t type);
+
+/**
+ ** @brief In mb string @a s1 return the length of the initial
+ ** part of the string who's type corresponds to category @a
+ ** name.
+ **
+ ** Predefined standard names are @c "alnum", @c "alpha", @c
+ ** "blank", @c "cntrl", @c "digit", @c "graph", @c "lower", @c
+ ** "print", @c "punct", @c "space", @c "upper", and @c
+ ** "xdigit". Others may be provided by your platform.
+ **
+ ** @return the position in @a s1 that corresponds to the the
+ ** first non-matching mb character in @a s2, which may be the
+ ** terminating @c 0 character or a position that produces an
+ ** encoding error.
+ **
+ ** @remark This function supposes that @a s1 can be scanned
+ ** without context, that is it starts in an initial shift state.
+ **/
+size_t mbsspn_name(char const* s1, char const name[static 1]);
+
+/**
+ ** @brief In mb string @a s1 skip over the initial part of the
+ ** string that doesn't correspond to wide characters in string
+ ** @a S2.
+ **
+ ** @see mbsrwcsskip
+ **/
+char const* mbsrwcscskip(char const s1[static 1], mbstate_t*restrict state, wchar_t const* S2);
+
+/**
+ ** @brief In mb string @a s1 skip over the initial part of the
+ ** string that doesn't correspond to mb characters in string @a
+ ** s2.
+ **
+ ** @see mbsrskip
+ **/
+char const* mbsrcskip(char const* s1, mbstate_t*restrict state, char const* s2);
+
+/**
+ ** @brief In mb string @a s1 skip over the initial part of the
+ ** string that doesn't correspond to mb characters in string @a
+ ** s2.
+ **
+ ** @see mbsrskip
+ **/
+size_t mbscspn(char const* s1, char const* s2);
+
+/**
+ ** @brief Convert mb string @a s1 to @c double.
+ **
+ ** @see strtod
+ **/
+double mbsrtod(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr);
+
+/**
+ ** @brief Convert mb string @a s1 to <code>long double</code>.
+ **
+ ** @see strtold
+ **/
+long double mbsrtold(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr);
+
+/**
+ ** @brief Convert mb string @a s1 to <code>long</code> according
+ ** to base @a base.
+ **
+ ** @see strtol
+ **/
+long mbsrtol(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base);
+
+/**
+ ** @brief Convert mb string @a s1 to <code>long long</code> according
+ ** to base @a base.
+ **
+ ** @see strtoll
+ **/
+long long mbsrtoll(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base);
+
+/**
+ ** @brief Convert mb string @a s1 to <code>unsigned long</code>
+ ** according to base @a base.
+ **
+ ** @see strtoul
+ **/
+unsigned long mbsrtoul(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base);
+
+/**
+ ** @brief Convert mb string @a s1 to <code>unsigned long
+ ** long</code> according to base @a base.
+ **
+ ** @see strtoull
+ **/
+unsigned long long mbsrtoull(char const*restrict s1, mbstate_t*restrict state, char**restrict endptr, int base);
+
+/**
+ ** @brief The codes returned by multi-byte conversion functions.
+ **
+ ** In general these functions return a value of type
+ ** `size_t`. Therefore the special codes have to be at the upper end
+ ** of the range of that type.
+ **
+ ** This provides names for these constants as enumeration type. This
+ ** is only possible since C23, because previously enumeration
+ ** constants had been limited to `signed int`.
+ **/
+enum mbcode {
+ //! An invalid encoding was encountered.
+ mbinvalid = (size_t)-1,
+ //! The encoding was incomplete.
+ mbincomplete = (size_t)-2,
+ //! A previously detect output character was stored.
+ mbstored = (size_t)-3,
+};
+
+static_assert(mbinvalid == SIZE_MAX, "enum mbcode should have size_t as underlying integer type");
diff --git a/numberline.c b/numberline.c
new file mode 100644
index 0000000..9f8f7fe
--- /dev/null
+++ b/numberline.c
@@ -0,0 +1,276 @@
+#include "c23-fallback.h"
+//#include <stdlib.h>
+#include <stdio.h>
+//#include <string.h>
+
+/**
+ ** @brief interpret string @a lbuf as a sequence of numbers
+ ** represented with @a base
+ **
+ ** @return a newly allocated array of numbers as found in @a lbuf
+ ** @param lbuf is supposed to be a string
+ ** @param np if non-null, the count of numbers is stored in
+ ** @c *np
+ ** @param base value from 0 to 36, with the same interpretation
+ ** as for @c strtoul and similar functions
+ **
+ ** @remark The caller of this function is responsible to @c
+ ** free the array that is returned.
+ **/
+size_t* numberline(size_t size,
+ char const lbuf[restrict static size],
+ size_t*restrict np, int base);
+
+/**
+ ** @brief read one text line of at most <code>size-1</code>
+ ** bytes. The <code>'\n'</code> character is replaced by @c 0.
+ **
+ ** @return s if an entire line was read
+ ** successfully. Otherwise, @c 0 is returned and @a s contains
+ ** a maximal partial line that could be read. @a s is null
+ ** terminated.
+ **/
+char* fgetline(size_t size,
+ char s[restrict size],
+ FILE*restrict stream);
+
+
+#include <limits.h>
+#include <errno.h>
+#ifndef EFAULT
+# define EFAULT EDOM
+#endif
+#ifndef EOVERFLOW
+# define EOVERFLOW (EFAULT-EOF)
+# if EOVERFLOW > INT_MAX
+# error EOVERFLOW constant is too large
+# endif
+#endif
+#ifndef ENOMEM
+# define ENOMEM (EOVERFLOW+EFAULT-EOF)
+# if ENOMEM > INT_MAX
+# error ENOMEM constant is too large
+# endif
+#endif
+
+/**
+ ** @brief print a series of numbers @a nums on @a stream, using
+ ** @c printf format @a form, separated by @a sep characters and
+ ** terminated with a newline character.
+ **
+ ** @return the number of characters printed to @a stream, or a
+ ** negative error value on error.
+ **
+ ** If @a len is @c 0, an empty line is printed and @c 1 is
+ ** returned.
+ **
+ ** Possible error returns are:
+ ** - <code>EOF</code> (which is negative) if @a stream was not
+ ** ready to be written to
+ ** - <code>-EOVERFLOW</code> if more than <code>INT_MAX</code>
+ ** characters would have to be written, including the case
+ ** that @a len is greater than <code>INT_MAX</code>.
+ ** - <code>-EFAULT</code> if @a stream is a null pointer
+ ** - <code>-EFAULT</code> if @a numb is a null pointer and @a len is not zero
+ ** - <code>-ENOMEM</code> if a memory error occurred
+ **
+ ** This function leaves <code>errno</code> to the same value as
+ ** occurred on entry.
+ **/
+int fprintnumbers(FILE*restrict stream,
+ char const form[restrict static 1],
+ char const sep[restrict static 1],
+ size_t len, size_t numb[restrict len]);
+
+/**
+ ** @brief print a series of numbers @a nums in @a buf, using @c
+ ** printf format @a form, separated by @a sep characters and
+ ** terminated with a newline character.
+ **
+ ** @return the number of characters printed to @a buf.
+ **
+ ** This supposes that @a tot and @a buf are big enough and
+ ** that @a form is a format suitable to print @c size_t.
+ **/
+int sprintnumbers(size_t tot, char buf[restrict tot],
+ char const form[restrict static 1],
+ char const sep[restrict static 1],
+ size_t len, size_t nums[restrict static len]);
+
+static
+size_t numberline_inner(char const*restrict act,
+ size_t numb[restrict static 1],
+ int base){
+ size_t n = 0;
+ for (char* next = nullptr; act[0]; act = next) {
+ numb[n] = strtoull(act, &next, base);
+ if (act == next) break;
+ ++n;
+ }
+ return n;
+}
+
+size_t* numberline(size_t size,
+ char const lbuf[restrict static size],
+ size_t*restrict np, int base){
+ size_t* ret = nullptr;
+ size_t n = 0;
+ /* Check for validity of the string, first. */
+ if (memchr(lbuf, 0, size)) {
+ /* The maximum number of integers encoded.
+ To see that this may be as much look at
+ the sequence 08 08 08 08 ... and suppose
+ that base is 0. */
+ ret = malloc(sizeof(size_t[1+(2*size)/3]));
+ if (!ret) return nullptr;
+ n = numberline_inner(lbuf, ret, base);
+
+ size_t len = n ? n : 1;
+ size_t* ret2 = realloc(ret, sizeof(size_t[len]));
+ if (ret2) ret = ret2;
+ }
+ if (np) *np = n;
+ return ret;
+}
+
+char* fgetline(size_t size,
+ char s[restrict size],
+ FILE*restrict stream){
+ s[0] = 0;
+ char* ret = fgets(s, size, stream);
+ if (ret) {
+ /* Uses non-const variant of strchr */
+ char* pos = strchr(s, '\n');
+ if (pos) *pos = 0;
+ else ret = nullptr;
+ }
+ return ret;
+}
+
+static inline int error_cleanup(int err, int prev) {
+ errno = prev;
+ return -err;
+}
+
+int sprintnumbers(size_t tot, char buf[restrict tot],
+ char const form[restrict static 1],
+ char const sep[restrict static 1],
+ size_t len, size_t nums[const restrict static len]) {
+ char* p = buf; /* next position in buf */
+ size_t const seplen = strlen(sep);
+ if (len) {
+ size_t i = 0;
+ for (; i < len;) {
+ p += sprintf(p, form, nums[i]);
+ ++i;
+ if (i >= len) break;
+ memcpy(p, sep, seplen);
+ p += seplen;
+ }
+ }
+ memcpy(p, "\n", 2);
+ return (p-buf)+1;
+}
+
+int fprintnumbers(FILE*restrict stream,
+ char const form[restrict static 1],
+ char const sep[restrict static 1],
+ size_t len, size_t nums[restrict len]) {
+ if (!stream) return -EFAULT;
+ if (len && !nums) return -EFAULT;
+ if (len > INT_MAX) return -EOVERFLOW;
+
+ size_t tot = (len ? len : 1)*strlen(sep);
+ int err = errno;
+ char* buf = nullptr;
+
+ if (len) {
+ /* Count the chars for the numbers. */
+ for (size_t i = 0; i < len; ++i)
+ tot += snprintf(nullptr, 0, form, nums[i]);
+ /* We return int so we have to constrain the max size. */
+ if (tot > INT_MAX) return error_cleanup(EOVERFLOW, err);
+ }
+
+ buf = malloc(tot+1);
+ if (!buf) return error_cleanup(ENOMEM, err);
+
+ sprintnumbers(tot, buf, form, sep, len, nums);
+ /* print whole line in one go */
+ if (fputs(buf, stream) == EOF) tot = EOF;
+ free(buf);
+ return tot;
+}
+
+int fprintnumbers_opt(FILE*restrict stream,
+ char const form[restrict static 1],
+ char const sep[restrict static 1],
+ size_t len, size_t nums[restrict static len]) {
+ if (!stream) return -EFAULT;
+ if (len && !nums) return -EFAULT;
+ if (len > INT_MAX) return -EOVERFLOW;
+
+ int err = errno;
+ size_t const seplen = strlen(sep);
+
+ size_t tot = 0;
+ size_t mtot = len*(seplen+10);
+ char* buf = malloc(mtot);
+
+ if (!buf) return error_cleanup(ENOMEM, err);
+
+ for (size_t i = 0; i < len; ++i) {
+ tot += sprintf(&buf[tot], form, nums[i]);
+ ++i;
+ if (i >= len) break;
+ if (tot > mtot-20) {
+ mtot *= 2;
+ char* nbuf = realloc(buf, mtot);
+ if (nbuf) {
+ buf = nbuf;
+ } else {
+ tot = error_cleanup(ENOMEM, err);
+ goto CLEANUP;
+ }
+ }
+ memcpy(&buf[tot], sep, seplen);
+ tot += seplen;
+ if (tot > INT_MAX) {
+ tot = error_cleanup(EOVERFLOW, err);
+ goto CLEANUP;
+ }
+ }
+ buf[tot] = 0;
+
+ /* print whole line in one go */
+ if (fputs(buf, stream) == EOF) tot = EOF;
+ CLEANUP:
+ free(buf);
+ return tot;
+}
+
+int main(void) {
+ char lbuf[256];
+ for (;;) {
+ if (fgetline(sizeof lbuf, lbuf, stdin)) {
+ size_t n;
+ size_t* nums = numberline(strlen(lbuf)+1, lbuf, &n, 0);
+ if (nums) {
+ int ret = fprintnumbers(stdout, "%#zX", ",\t", n, nums);
+ if (ret < 0) return EXIT_FAILURE;
+ free(nums);
+ }
+ } else {
+ if (lbuf[0]) { /* a partial line has been read */
+ for (;;) {
+ int c = getc(stdin);
+ if (c == EOF) return EXIT_FAILURE;
+ if (c == '\n') {
+ fprintf(stderr, "line too long: %s\n", lbuf);
+ break;
+ }
+ }
+ } else break; /* regular end of input */
+ }
+ }
+}
diff --git a/precision.c b/precision.c
new file mode 100644
index 0000000..efea85b
--- /dev/null
+++ b/precision.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main(void) {
+ printf("%.20e %.20e\n",
+ ((1.0E-13 + 1.0E-13)),
+ (1.0E-13 + (1.0E-13 + 1.0)) - 1.0
+ );
+}
diff --git a/rationals.c b/rationals.c
new file mode 100644
index 0000000..71beb7e
--- /dev/null
+++ b/rationals.c
@@ -0,0 +1,218 @@
+#include "rationals.h"
+
+size_t gcd(size_t a, size_t b) [[__unsequenced__]];
+
+rat rat_get(signed sign, size_t num, size_t denom) [[__unsequenced__]] {
+ rat ret = {
+ .sign = (sign < 0),
+ .num = num,
+ .denom = denom,
+ };
+ return ret;
+}
+
+rat rat_get_normal(rat x) [[__unsequenced__]] {
+ size_t c = gcd(x.num, x.denom);
+ x.num /= c;
+ x.denom /= c;
+ return x;
+}
+
+rat rat_get_extended(rat x, size_t f) [[__unsequenced__]] {
+ x.num *= f;
+ x.denom *= f;
+ return x;
+}
+
+rat rat_get_prod(rat x, rat y) [[__unsequenced__]] {
+ rat ret = {
+ .sign = (x.sign != y.sign),
+ .num = x.num * y.num,
+ .denom = x.denom * y.denom,
+ };
+ return rat_get_normal(ret);
+}
+
+rat rat_get_sum(rat x, rat y) [[__unsequenced__]] {
+ size_t c = gcd(x.denom, y.denom);
+ size_t ax = y.denom/c;
+ size_t bx = x.denom/c;
+ x = rat_get_extended(x, ax); /*@\label{rat_get_extended_x}*/
+ y = rat_get_extended(y, bx);
+ assert(x.denom == y.denom);
+
+ if (x.sign == y.sign) {
+ x.num += y.num;
+ } else if (x.num > y.num) {
+ x.num -= y.num;
+ } else {
+ x.num = y.num - x.num;
+ x.sign = !x.sign;
+ }
+ return rat_get_normal(x);
+}
+
+rat rat_get_sum2(rat x, rat y) [[__unsequenced__]] {
+ size_t c = gcd(x.denom, y.denom);
+ size_t denom = (x.denom/c)*y.denom;
+ size_t num;
+ bool sign;
+
+ size_t ab = x.num*(y.denom/c);
+ size_t ba = y.num*(x.denom/c);
+ if (x.sign == y.sign) {
+ num = ab + ba;
+ sign = x.sign;
+ } else {
+ if (ab < ba) {
+ num = ba - ab;
+ sign = y.sign;
+ } else {
+ num = ab - ba;
+ sign = x.sign;
+ }
+ }
+ rat ret = {
+ .sign = sign,
+ .num = num,
+ .denom = denom,
+ };
+ return rat_get_normal(ret);
+}
+
+rat rat_get_prod2(rat x, rat y) [[__unsequenced__]] {
+ size_t tmp = y.denom;
+ y.denom = x.denom;
+ x.denom = tmp;
+ x = rat_get_normal(x);
+ y = rat_get_normal(y);
+ rat ret = {
+ .sign = (x.sign != y.sign),
+ .num = x.num * y.num,
+ .denom = x.denom * y.denom,
+ };
+ return ret;
+}
+
+void rat_destroy(rat* rp) [[__unsequenced__]] {
+ if (rp) *rp = (rat){ };
+}
+
+rat* rat_init(rat* rp,
+ signed sign,
+ size_t num,
+ size_t denom) [[__unsequenced__]] {
+ if (rp) *rp = rat_get(sign, num, denom);
+ return rp;
+}
+
+rat* rat_normalize(rat* rp) [[__unsequenced__]] {
+ if (rp) *rp = rat_get_normal(*rp);
+ return rp;
+}
+
+rat* rat_extend(rat* rp, size_t f) [[__unsequenced__]] {
+ if (rp) *rp = rat_get_extended(*rp, f);
+ return rp;
+}
+
+rat* rat_sumup(rat* rp, rat y) [[__unsequenced__]] {
+ size_t c = gcd(rp->denom, y.denom);
+ size_t ax = y.denom/c;
+ size_t bx = rp->denom/c;
+ rat_extend(rp, ax); /*@\label{rat_extend_x}*/
+ y = rat_get_extended(y, bx);
+ assert(rp->denom == y.denom);
+
+ if (rp->sign == y.sign) {
+ rp->num += y.num;
+ } else if (rp->num > y.num) {
+ rp->num -= y.num;
+ } else {
+ rp->num = y.num - rp->num;
+ rp->sign = !rp->sign;
+ }
+ return rat_normalize(rp); /*@\label{rat_normalize_x}*/
+}
+
+rat* rat_rma(rat* rp, rat x, rat y) [[__unsequenced__]] {
+ return rat_sumup(rp, rat_get_prod(x, y));
+}
+
+char const* rat_print(size_t len, char tmp[len], rat const* x) {
+ if (x) {
+ snprintf(tmp, len, "%c%zu/%zu", (x->sign ? '-' : '+'), x->num, x->denom);
+ } else {
+ tmp[0] = 0;
+ }
+ return tmp;
+}
+
+#define RAT_PRINT_MAX 256
+
+#define RAT_PRINT(X) rat_print(RAT_PRINT_MAX, (char[RAT_PRINT_MAX]){ }, (rat[1]){ (X) })
+
+int main(void) {
+ rat third = rat_get(+1, 1, 3);
+ rat forth = rat_get(-1, 1, 4);
+ printf("%s, prod is %s, 3x %s\n",
+ RAT_PRINT(third),
+ RAT_PRINT(rat_get_prod(third, third)),
+ RAT_PRINT(rat_get_prod(third, rat_get_prod(third, third)))
+ );
+ printf("%s, prod is %s, 3x %s\n",
+ RAT_PRINT(forth),
+ RAT_PRINT(rat_get_prod(forth, forth)),
+ RAT_PRINT(rat_get_prod(forth, rat_get_prod(forth, forth)))
+ );
+ printf("%s, prod is %s, 3x %s\n",
+ RAT_PRINT(forth),
+ RAT_PRINT(rat_get_prod(third, forth)),
+ RAT_PRINT(rat_get_prod(forth, rat_get_prod(third, forth)))
+ );
+ printf("%s, prod is %s, 3x %s\n",
+ RAT_PRINT(third),
+ RAT_PRINT(rat_get_prod2(third, third)),
+ RAT_PRINT(rat_get_prod2(third, rat_get_prod2(third, third)))
+ );
+ printf("%s, prod is %s, 3x %s\n",
+ RAT_PRINT(forth),
+ RAT_PRINT(rat_get_prod2(forth, forth)),
+ RAT_PRINT(rat_get_prod2(forth, rat_get_prod2(forth, forth)))
+ );
+ printf("%s, prod is %s, 3x %s\n",
+ RAT_PRINT(forth),
+ RAT_PRINT(rat_get_prod2(third, forth)),
+ RAT_PRINT(rat_get_prod2(forth, rat_get_prod2(third, forth)))
+ );
+ printf("%s, sum is %s, 3x %s\n",
+ RAT_PRINT(third),
+ RAT_PRINT(rat_get_sum(third, third)),
+ RAT_PRINT(rat_get_sum(third, rat_get_sum(third, third)))
+ );
+ printf("%s, sum is %s, 3x %s\n",
+ RAT_PRINT(forth),
+ RAT_PRINT(rat_get_sum(forth, forth)),
+ RAT_PRINT(rat_get_sum(forth, rat_get_sum(forth, forth)))
+ );
+ printf("%s, sum is %s, 3x %s\n",
+ RAT_PRINT(forth),
+ RAT_PRINT(rat_get_sum(third, forth)),
+ RAT_PRINT(rat_get_sum(forth, rat_get_sum(third, forth)))
+ );
+ printf("%s, sum is %s, 3x %s\n",
+ RAT_PRINT(third),
+ RAT_PRINT(rat_get_sum2(third, third)),
+ RAT_PRINT(rat_get_sum2(third, rat_get_sum2(third, third)))
+ );
+ printf("%s, sum is %s, 3x %s\n",
+ RAT_PRINT(forth),
+ RAT_PRINT(rat_get_sum2(forth, forth)),
+ RAT_PRINT(rat_get_sum2(forth, rat_get_sum2(forth, forth)))
+ );
+ printf("%s, sum is %s, 3x %s\n",
+ RAT_PRINT(forth),
+ RAT_PRINT(rat_get_sum2(third, forth)),
+ RAT_PRINT(rat_get_sum2(forth, rat_get_sum2(third, forth)))
+ );
+}
diff --git a/rationals.h b/rationals.h
new file mode 100644
index 0000000..d69b515
--- /dev/null
+++ b/rationals.h
@@ -0,0 +1,41 @@
+#ifndef RATIONALS_H
+# define RATIONALS_H 1
+# include <stdbool.h>
+# include "euclid.h"
+
+typedef struct rat rat;
+
+struct rat {
+ bool sign;
+ size_t num;
+ size_t denom;
+};
+
+/* Functions that return a value of type rat. */
+rat rat_get(signed sign, size_t num, size_t denom) [[__unsequenced__]];
+rat rat_get_normal(rat x) [[__unsequenced__]];
+rat rat_get_extended(rat x, size_t f) [[__unsequenced__]];
+rat rat_get_prod(rat x, rat y) [[__unsequenced__]];
+rat rat_get_sum(rat x, rat y) [[__unsequenced__]];
+
+
+/* Functions that operate on pointers to rat. */
+void rat_destroy(rat* rp) [[__unsequenced__]];
+rat* rat_init(rat* rp,
+ signed sign,
+ size_t num, size_t denom) [[__unsequenced__]];
+rat* rat_normalize(rat* rp) [[__unsequenced__]];
+rat* rat_extend(rat* rp, size_t f) [[__unsequenced__]];
+rat* rat_sumup(rat* rp, rat y) [[__unsequenced__]];
+rat* rat_rma(rat* rp, rat x, rat y) [[__unsequenced__]];
+
+/* Functions that are implemented as exercises. */
+/** @brief Print @a x into @a tmp and return tmp. **/
+char const* rat_print(size_t len, char tmp[len], rat const* x);
+/** @brief Print @a x normalize and print. **/
+char const* rat_normalize_print(size_t len, char tmp[len],
+ rat const* x);
+rat* rat_dotproduct(rat rp[static 1], size_t n,
+ rat const A[n], rat const B[n]);
+
+#endif
diff --git a/reorder.h b/reorder.h
new file mode 100644
index 0000000..5cb546b
--- /dev/null
+++ b/reorder.h
@@ -0,0 +1,245 @@
+#ifndef __REORDER_H__
+#define __REORDER_H__
+
+#include <string.h>
+#include <stdlib.h>
+
+#define rodr_convert(T, ...) (T){ (__VA_ARGS__), }
+
+[[__maybe_unused__]]
+static inline
+void* rodr_memcpy(size_t n,
+ unsigned char s1[restrict static n],
+ unsigned char const s2[restrict static n])
+ [[__unsequenced__]];
+
+[[__maybe_unused__]]
+static inline
+void* rodr_memccpy(size_t n,
+ unsigned char s1[restrict static n],
+ unsigned char const s2[restrict static n],
+ int c)
+ [[__unsequenced__]];
+
+[[__maybe_unused__]]
+static inline
+void* rodr_memmove(size_t n,
+ unsigned char s1[static n],
+ unsigned char const s2[static n]);
+
+[[__maybe_unused__]]
+static inline
+int rodr_memcmp(size_t n,
+ unsigned char s1[restrict static n],
+ unsigned char const s2[restrict static n])
+ [[__unsequenced__]];
+
+[[__maybe_unused__]]
+static inline
+void* rodr_memchr(size_t n, unsigned char const s[static n], int c)
+ [[__unsequenced__]];
+
+[[__maybe_unused__]]
+static inline
+void* rodr_memset(size_t n, unsigned char s[static n], int c)
+ [[__unsequenced__]];
+
+[[__maybe_unused__]]
+static inline
+void rodr_qsort(size_t nmemb, size_t size,
+ unsigned char base[static nmemb*size],
+ int (*compar)(const void *, const void *));
+
+[[__maybe_unused__]]
+static inline
+void* rodr_bsearch(size_t nmemb, size_t size,
+ unsigned char const key[static 1],
+ unsigned char const base[static nmemb*size],
+ int (*compar)(const void *, const void *));
+
+/**
+ ** @brief Similar to `memcpy` but checks the buffer argument for the
+ ** size.
+ **
+ ** To be able to do that check, the function has the size argument
+ ** first and then the buffer, so the buffer can be specified as array
+ ** parameter that depends on the that size.
+ **
+ ** Here there is an additional complication, namely that the original
+ ** functions has `void` pointers. We go around this by using
+ ** `unsigned char` (which then can be the base of an array). Then, in
+ ** the macro interface we have to convert the arguments to `void`
+ ** pointers such that this function here can be called without
+ ** troubles.
+ **/
+static inline
+void* rodr_memcpy(size_t n,
+ unsigned char s1[restrict static n],
+ unsigned char const s2[restrict static n]) {
+ // This captures a possible pre-existing macro for memcpy
+ return memcpy(s1, s2, n);
+}
+
+static inline
+void* rodr_memccpy(size_t n,
+ unsigned char s1[restrict static n],
+ unsigned char const s2[restrict static n],
+ int c) {
+ // This captures a possible pre-existing macro for memccpy
+ return memccpy(s1, s2, c, n);
+}
+
+static inline
+void* rodr_memmove(size_t n,
+ unsigned char s1[static n],
+ unsigned char const s2[static n]) {
+ // This captures a possible pre-existing macro for memmove
+ return memmove(s1, s2, n);
+}
+
+static inline
+int rodr_memcmp(size_t n,
+ unsigned char s1[restrict static n],
+ unsigned char const s2[restrict static n]) {
+ // This captures a possible pre-existing macro for memcmp
+ return memcmp(s1, s2, n);
+}
+
+
+static inline
+void* rodr_memchr(size_t n, unsigned char const s[static n], int c) {
+ // This avoids a possible pre-existing macro for memchr
+ return (memchr)(s, c, n);
+}
+
+static inline
+void* rodr_memset(size_t n, unsigned char s[static n], int c) {
+ // This captures a possible pre-existing macro for memset
+ return memset(s, c, n);
+}
+
+
+static inline
+void rodr_qsort(size_t nmemb, size_t size,
+ unsigned char base[static nmemb*size],
+ int (*compar)(const void *, const void *)) {
+ // This captures a possible pre-existing macro for qsort
+ qsort(base, nmemb, size, compar);
+}
+
+static inline
+void* rodr_bsearch(size_t nmemb, size_t size,
+ unsigned char const key[static 1],
+ unsigned char const base[static nmemb*size],
+ int (*compar)(const void *, const void *)) {
+ // This avoids a possible pre-existing macro for bsearch
+ return (bsearch)(key, base, nmemb, size, compar);
+}
+
+#undef memcpy
+/**
+ ** @brief A macro replacement for `memcpy` that also propagates the
+ ** requirements for the size of the buffer arguments.
+ **
+ ** There is a complication, namely that the original functions has
+ ** `void` pointers. We go around this by using `unsigned char` (which
+ ** then can be the base of an array) in the `rodr_memcpy`
+ ** interface. So here we have to convert the arguments to `void`
+ ** pointers such that this function can be called without troubles.
+ **
+ ** We don't want to use simple casts to `void*`, for example, because
+ ** that would cast away all other type checks that we still want to
+ ** maintain. Therefore we use compound literals where the arguments
+ ** are initializers. By that, only an argument type that has an
+ ** implicit conversion to the corresponding `void` pointer type can
+ ** be used, all others will be diagnosed.
+ **/
+#define memcpy(S1, S2, N) \
+rodr_memcpy((N), \
+ /* Make sure no qualification gets lost */ \
+ rodr_convert(void*, S1), \
+ /* Make sure no volatile gets lost */ \
+ rodr_convert(void const*, S2))
+
+#undef memccpy
+#define memccpy(S1, S2, C, N) \
+ rodr_memccpy((N), \
+ /* Make sure no qualification gets lost */ \
+ rodr_convert(void*, S1), \
+ /* Make sure no volatile gets lost */ \
+ rodr_convert(void const*, S2), \
+ (C))
+
+#define memmove(S1, S2, N) \
+ rodr_memmove((N), \
+ /* Make sure no qualification gets lost */ \
+ rodr_convert(void*, S1), \
+ /* Make sure no volatile gets lost */ \
+ rodr_convert(void const*, S2))
+
+#define memcmp(S1, S2, N) \
+ rodr_memcmp((N), \
+ /* Make sure no qualification gets lost */ \
+ rodr_convert(void*, S1), \
+ /* Make sure no volatile gets lost */ \
+ rodr_convert(void const*, S2))
+
+
+#undef memchr
+#define memchr(S, C, N) \
+ ((typeof(1 ? (S) : (void*)1)) \
+ rodr_memchr((N), \
+ /* Make sure no volatile gets lost */ \
+ rodr_convert(void const*, S), \
+ (C)))
+
+#undef memset
+#define memset(S, C, N) \
+ rodr_memset((N), \
+ /* Make sure no qualification gets lost */ \
+ rodr_convert(void*, S), \
+ (C))
+
+#undef qsort
+#define qsort(BASE, NMEMB, SIZE, COMPAR) \
+ rodr_qsort((NMEMB), \
+ (SIZE), \
+ /* Make sure no qualification gets lost */ \
+ rodr_convert(void*, BASE), \
+ (COMPAR))
+
+#undef bsearch
+#define bsearch(KEY, BASE, NMEMB, SIZE, COMPAR) \
+ ((typeof(1 ? (BASE) : (void*)1)) \
+ rodr_bsearch((NMEMB), \
+ (SIZE), \
+ /* Make sure no volatile gets lost */ \
+ rodr_convert(void const*, KEY), \
+ /* Make sure no volatile gets lost */ \
+ rodr_convert(void const*, BASE), \
+ (COMPAR)))
+
+#if __STDC_VERSION__ >= 202311L
+
+[[__maybe_unused__]]
+static inline
+void* rodr_memset_explicit(size_t n, unsigned char s[static n], int c)
+ [[__unsequenced__]];
+
+static inline
+void* rodr_memset_explicit(size_t n, unsigned char s[static n], int c) {
+ // This captures a possible pre-existing macro for memset_explicit
+ return memset_explicit(s, c, n);
+}
+
+#undef memset_explicit
+#define memset_explicit(S, C, N) \
+ rodr_memset_explicit((N), \
+ /* Make sure no qualification gets lost */ \
+ rodr_convert(void*, S), \
+ (C))
+
+#endif
+
+
+#endif
diff --git a/sequence_point.c b/sequence_point.c
new file mode 100644
index 0000000..6048cb6
--- /dev/null
+++ b/sequence_point.c
@@ -0,0 +1,10 @@
+#include<stdio.h>
+
+unsigned add(unsigned* x, unsigned const* y) [[unsequenced]] {
+ return *x += *y;
+}
+int main(void) {
+ unsigned a = 3;
+ unsigned b = 5;
+ printf("a = %u, b = %u\n", add(&a, &b), add(&b, &a));
+}
diff --git a/shadow.c b/shadow.c
new file mode 100644
index 0000000..f996fc8
--- /dev/null
+++ b/shadow.c
@@ -0,0 +1,20 @@
+#include "c23-fallback.h"
+
+void squareIt(double* p) [[__unsequenced__]] {
+ *p *= *p;
+}
+int main(void) {
+ double x = 35.0; /*@\label{line:decl-x}*/
+ double* xp = &x; /*@\label{line:decl-xp}*/
+ {
+ squareIt(&x); /* Refers to double x *//*@\label{line:use-x-1}*/
+ /*@…*/
+ [[maybe_unused]] int x = 0; /* Shadow double x *//*@\label{line:decl-x-2}*/
+ /*@…*/
+ squareIt(xp); /* Valid use of double x *//*@\label{line:use-xp}*/
+ /*@…*/
+ } /*@\label{line:end-x-2}*/
+ /*@…*/
+ squareIt(&x); /* Refers to double x *//*@\label{line:use-x-2}*/
+ /*@…*/
+}
diff --git a/sighandler.c b/sighandler.c
new file mode 100644
index 0000000..bb8fd11
--- /dev/null
+++ b/sighandler.c
@@ -0,0 +1,99 @@
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sighandler.h"
+
+#define SH_PAIR(X, D) [X] = { .name = #X, .desc = "" D "", }
+
+sh_pair const sh_pairs[] = {
+ /* Execution errors */
+ SH_PAIR(SIGFPE, "erroneous arithmetic operation"),
+ SH_PAIR(SIGILL, "invalid instruction"),
+ SH_PAIR(SIGSEGV, "invalid access to storage"),
+#ifdef SIGBUS
+ SH_PAIR(SIGBUS, "bad address"),
+#endif
+ /* Job control */
+ SH_PAIR(SIGABRT, "abnormal termination"),
+ SH_PAIR(SIGINT, "interactive attention signal"),
+ SH_PAIR(SIGTERM, "termination request"),
+#ifdef SIGKILL
+ SH_PAIR(SIGKILL, "kill signal"),
+#endif
+#ifdef SIGQUIT
+ SH_PAIR(SIGQUIT, "keyboard quit"),
+#endif
+#ifdef SIGSTOP
+ SH_PAIR(SIGSTOP, "stop process"),
+#endif
+#ifdef SIGCONT
+ SH_PAIR(SIGCONT, "continue if stopped"),
+#endif
+#ifdef SIGINFO
+ SH_PAIR(SIGINFO, "status information request"),
+#endif
+};
+
+size_t const sh_known = (sizeof sh_pairs/sizeof sh_pairs[0]);
+
+#if ATOMIC_LONG_LOCK_FREE > 1
+_Atomic(unsigned long) sh_counts[sizeof sh_pairs/sizeof sh_pairs[0]];
+# define SH_COU " (%lu times),"
+#else
+# define SH_COU "%0.0lu,"
+#endif
+
+void sh_count(int);
+unsigned long sh_counted(int);
+
+#define SH_HEAD "\r%s:%zu: "
+#define SH_DOC "\t%s,\t%s"
+
+
+void sh_print(FILE* io, int sig,
+ char const* filename, size_t line,
+ char const* string) {
+ char const* doc =
+ (sig < sh_known && sh_pairs[sig].name)
+ ? sh_pairs[sig].desc
+ : "unknown signal number";
+ if (errno) {
+ char const* err = strerror(errno);
+ errno = 0;
+ if (!sig)
+ fprintf(io, SH_HEAD "\t%s:\t%s\n", filename, line,
+ string, err);
+ else if (sig < sh_known && sh_pairs[sig].name)
+ fprintf(io, SH_HEAD "%s" SH_COU SH_DOC ":\t%s\n", filename, line,
+ sh_pairs[sig].name, sh_counted(sig), doc, string, err);
+ else
+ fprintf(io, SH_HEAD "#%d" SH_COU SH_DOC ":\t%s\n", filename, line,
+ sig, sh_counted(sig), doc, string, err);
+ } else {
+ if (!sig)
+ fprintf(io, SH_HEAD "\t%s\n", filename, line,
+ string);
+ else if (sig < sh_known && sh_pairs[sig].name)
+ fprintf(io, SH_HEAD "%s" SH_COU SH_DOC "\n", filename, line,
+ sh_pairs[sig].name, sh_counted(sig), doc, string);
+ else
+ fprintf(io, SH_HEAD "#%d" SH_COU SH_DOC "\n", filename, line,
+ sig, sh_counted(sig), doc, string);
+ }
+}
+
+sh_handler* sh_enable(int sig, sh_handler* hnd) {
+ sh_handler* ret = signal(sig, hnd);
+ if (ret == SIG_ERR) {
+ SH_PRINT(stderr, sig, "failed");
+ errno = 0;
+ } else if (ret == SIG_IGN) {
+ SH_PRINT(stderr, sig, "previously ignored");
+ } else if (ret && ret != SIG_DFL) {
+ SH_PRINT(stderr, sig, "previously set otherwise");
+ } else {
+ SH_PRINT(stderr, sig, "ok");
+ }
+ return ret;
+}
diff --git a/sighandler.h b/sighandler.h
new file mode 100644
index 0000000..4b7768d
--- /dev/null
+++ b/sighandler.h
@@ -0,0 +1,101 @@
+#ifndef SIGHANDLER_H
+#define SIGHANDLER_H 1
+
+#include "c23-fallback.h"
+#include <stdio.h>
+#ifndef __STDC_NO_ATOMIC__
+# include <stdatomic.h>
+#endif
+
+/**
+ ** @brief A pair of strings to hold signal information
+ **/
+struct sh_pair {
+ char const* name;
+ char const* desc;
+};
+typedef struct sh_pair sh_pair;
+
+/**
+ ** @brief For each possible signal, this holds a pair of strings with
+ ** signal information.
+ **
+ ** The size of this array can be queried with `sh_known`.
+ **
+ ** @see SH_PRINT to use that information.
+ **
+ ** Conditionally, this also adds some commonly used extensions.
+ **/
+extern sh_pair const sh_pairs[];
+
+extern size_t const sh_known;
+
+#if ATOMIC_LONG_LOCK_FREE > 1
+/**
+ ** @brief Keep track of the number of calls into a
+ ** signal handler for each possible signal.
+ **
+ ** Don't use this array directly.
+ **
+ ** @see sh_count to update this information.
+ ** @see SH_PRINT to use that information.
+ **/
+extern _Atomic(unsigned long) sh_counts[];
+
+/**
+ ** @brief Use this in your signal handler to keep track of the
+ ** number of calls to the signal @a sig.
+ **
+ ** @see sh_counted to use that information.
+ **/
+inline
+void sh_count(int sig) {
+ if (sig < sh_known) ++sh_counts[sig];
+}
+
+inline
+unsigned long sh_counted(int sig){
+ return (sig < sh_known) ? sh_counts[sig] : 0;
+}
+
+#else
+inline
+void sh_count(int sig) {
+ // empty
+}
+
+inline
+unsigned long sh_counted(int sig){
+ return 0;
+}
+#endif
+
+/**
+ ** @brief Prototype of signal handlers
+ **/
+typedef void sh_handler(int);
+
+/**
+ ** @brief Pretty-prints signal information
+ **
+ ** @see PRINT_SIG for a convenient user interface
+ **/
+void sh_print(FILE* io, int sig,
+ char const* filename, size_t line,
+ char const* string);
+
+/**
+ ** @brief Pretty-prints signal information
+ **
+ ** This prints signal information @a STRING for signal @a SIG to
+ ** stream @a IO.
+ **/
+#define SH_PRINT(IO, SIG, STRING) sh_print(IO, SIG, __FILE__, __LINE__, STRING)
+
+/**
+ ** @brief Enables a signal handler and catches the errors
+ **/
+sh_handler* sh_enable(int sig, sh_handler* hnd);
+
+
+#endif
diff --git a/sighandler_test.c b/sighandler_test.c
new file mode 100644
index 0000000..431ff9a
--- /dev/null
+++ b/sighandler_test.c
@@ -0,0 +1,70 @@
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "sighandler.h"
+
+#ifndef SIGNALS
+#define SIGNALS sh_known
+#endif
+
+/**
+ ** @brief A simple signal handler mechanism.
+ **
+ ** This just raises a flag when a signal is caught.
+ **
+ ** All real processing of the signal has to be done
+ ** in an event loop.
+ **/
+static sig_atomic_t volatile received;
+
+static void signal_handler(int sig) {
+ received = sig;
+ switch (sig) {
+#ifdef SIGQUIT
+ case SIGQUIT: exit(EXIT_FAILURE);
+#endif
+ case SIGTERM: quick_exit(EXIT_FAILURE);
+ case SIGABRT: _Exit(EXIT_FAILURE);
+ default:
+ /* reset the handling to its default */
+ signal(sig, SIG_DFL);
+ return;
+ case SIGINT: return;
+ }
+}
+
+static
+void signal_atexit(void) {
+ SH_PRINT(stderr, received, "atexit handler");
+}
+
+static
+void signal_at_quick_exit(void) {
+ SH_PRINT(stderr, received, "at_quick_exit handler");
+}
+
+int main(int argc, [[maybe_unused]] char* argv[argc+1]) {
+ atexit(signal_atexit);
+ at_quick_exit(signal_at_quick_exit);
+ for (unsigned i = 1; i < SIGNALS; ++i)
+ sh_enable(i, signal_handler);
+ SH_PRINT(stderr, SIGNALS-1, "highest known signal number");
+
+ size_t const Gi = (1ull<<30);
+ for(size_t step = 0; step < 10*Gi; ++step) {
+ if (!(step%Gi)) printf("step %zu\n", step);
+ switch (received) {
+ case SIGINT:
+ fprintf(stderr, "\ryou called?\n");
+ received = 0;
+ case 0:
+ continue;
+ }
+ SH_PRINT(stderr, received, "is somebody trying to kill us?");
+ raise(received);
+ break;
+ }
+ SH_PRINT(stderr, received, "we survived");
+ return EXIT_SUCCESS;
+}
diff --git a/stats.c b/stats.c
new file mode 100644
index 0000000..82afd7e
--- /dev/null
+++ b/stats.c
@@ -0,0 +1,17 @@
+#include "stats.h"
+
+#define EXTERNAL(NAME) extern typeof(NAME) NAME
+
+EXTERNAL(stats_collect);
+EXTERNAL(stats_collect0);
+EXTERNAL(stats_collect1);
+EXTERNAL(stats_collect2);
+EXTERNAL(stats_collect3);
+EXTERNAL(stats_samples);
+EXTERNAL(stats_mean);
+EXTERNAL(stats_sdev);
+EXTERNAL(stats_rsdev);
+EXTERNAL(stats_skew);
+EXTERNAL(stats_var_unbiased);
+EXTERNAL(stats_sdev_unbiased);
+EXTERNAL(stats_rsdev_unbiased);
diff --git a/stats.h b/stats.h
new file mode 100644
index 0000000..c70b4ba
--- /dev/null
+++ b/stats.h
@@ -0,0 +1,213 @@
+#include "c23-fallback.h"
+#include <tgmath.h>
+#include <stdio.h>
+
+/**
+ ** @file
+ ** @brief Collect some simple statistics online as we go.
+ **
+ ** This uses a generalization of Welford's trick to compute running
+ ** mean and variance. See
+ **
+ ** Philippe Pébay. Formulas for robust, one-pass parallel computation
+ ** of covariances and arbitrary-order statistical moments. Technical
+ ** Report SAND2008-6212, SANDIA, 2008. URL
+ ** http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf.
+ **/
+
+/**
+ ** @brief A simple data structure to collect the 0th to 3rd moment of
+ ** a statistic.
+ **
+ ** @warning Since this also uses a @c double for the number of
+ ** samples, the validity of all this is restricted to about
+ ** @f$2^{50} \approx 10^{15}@f$ samples.
+ **/
+struct stats {
+ double moment[4];
+};
+typedef struct stats stats;
+
+
+/**
+ ** @brief Return the number of samples that had been entered into the
+ ** statistic @a c.
+ **/
+inline
+double stats_samples(stats c[static 1]) [[__unsequenced__]] {
+ return c->moment[0];
+}
+
+/**
+ ** @brief Return the mean value of the samples that had been entered
+ ** into the statistic @a c.
+ **/
+inline
+double stats_mean(stats c[static 1]) [[__unsequenced__]] {
+ return c->moment[1];
+}
+
+/**
+ ** @brief Return the variance of the samples that had been entered
+ ** into the statistic @a c.
+ **/
+inline
+double stats_var(stats c[static 1]) [[__reproducible__]] {
+ return c->moment[2]/stats_samples(c);
+}
+
+// Clang has this notorious design flaw in <tgmath.h> where they
+// declare interfaces as being static. In some cases this results in
+// an avalanche of confusing messages of static functions that would
+// be used in an inline function.
+#if __clang_major__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wstatic-in-inline"
+#endif
+
+/**
+ ** @brief Return the standard deviation of the samples that had been
+ ** entered into the statistic @a c.
+ **/
+inline
+double stats_sdev(stats c[static 1]) [[__reproducible__]] {
+ return sqrt(stats_var(c));
+}
+
+/**
+ ** @brief Return the relative standard deviation of the samples that
+ ** had been entered into the statistic @a c.
+ **/
+inline
+double stats_rsdev(stats c[static 1]) [[__reproducible__]] {
+ return sqrt(stats_var(c))/stats_mean(c);
+}
+
+/**
+ ** @brief Return the normalized skew of the samples that had been
+ ** entered into the statistic @a c.
+ **/
+inline
+double stats_skew(stats c[static 1]) [[__reproducible__]] {
+ double var = stats_var(c);
+ return (c->moment[3]/pow(var, 1.5))/stats_samples(c);
+}
+
+/**
+ ** @brief Return the unbiased variance of the samples that had been
+ ** entered into the statistic @a c.
+ **
+ ** Use Bessel's correction to have an estimation of the unbiased
+ ** variance of the overall population.
+ **/
+inline
+double stats_var_unbiased(stats c[static 1]) [[__reproducible__]] {
+ return c->moment[2]/(stats_samples(c)-1);
+}
+
+/**
+ ** @brief Return the unbiased standard deviation of the samples that
+ ** had been entered into the statistic @a c.
+ **
+ ** Use Bessel's correction to have an less biased estimation of the
+ ** variance of the overall population.
+ **/
+inline
+double stats_sdev_unbiased(stats c[static 1]) [[__reproducible__]] {
+ return sqrt(stats_var_unbiased(c));
+}
+
+#if __clang_major__
+#pragma clang diagnostic pop
+#endif
+
+
+
+/**
+ ** @brief Return the unbiased relative standard deviation of the
+ ** samples that had been entered into the statistic @a c.
+ **/
+inline
+double stats_rsdev_unbiased(stats c[static 1]) [[__reproducible__]] {
+ return stats_rsdev(c)*(1+1/(4*stats_samples(c)));
+}
+
+/**
+ ** @brief Add value @a val to the statistic @a c.
+ **
+ ** @c moments is the number of statistic moments that is collected,
+ ** it has to be between `0` and `3`, including.
+ **/
+inline
+void stats_collect(stats c[static 1],
+ double val, unsigned moments)
+ [[__reproducible__]] {
+ double n = stats_samples(c);
+ double n0 = n-1;
+ double n1 = n+1;
+ double delta0 = 1;
+ double delta = val - stats_mean(c);
+ double delta1 = delta/n1;
+ double delta2 = delta1*delta*n;
+ switch (moments) {
+ default:
+ c->moment[3] += (delta2*n0 - 3*c->moment[2])*delta1;
+ [[__fallthrough__]];
+ case 2:
+ c->moment[2] += delta2;
+ [[__fallthrough__]];
+ case 1:
+ c->moment[1] += delta1;
+ [[__fallthrough__]];
+ case 0:
+ c->moment[0] += delta0;
+ }
+}
+
+/**
+ ** @brief Add value @a val to the statistic @a c.
+ **
+ ** Only the number of samples is collected.
+ **/
+inline
+void stats_collect0(stats c[static 1],
+ double val)
+ [[__reproducible__]] {
+ stats_collect(c, val, 0);
+}
+
+/**
+ ** @brief Add value @a val to the statistic @a c.
+ **
+ ** Only the number of samples and mean is collected.
+ **/
+inline
+void stats_collect1(stats c[static 1],
+ double val)
+ [[__reproducible__]] {
+ stats_collect(c, val, 1);
+}
+
+/**
+ ** @brief Add value @a val to the statistic @a c.
+ **
+ ** The number of samples, mean and standard deviation is collected.
+ **/
+inline
+void stats_collect2(stats c[static 1],
+ double val)
+ [[__reproducible__]] {
+ stats_collect(c, val, 2);
+}
+
+/**
+ ** @brief Add value @a val to the statistic @a c.
+ **
+ ** All statistics up to the skew are collected.
+ **/
+inline
+void stats_collect3(stats c[static 1],
+ double val)
+ [[__reproducible__]] {
+ stats_collect(c, val, 3);
+}
diff --git a/strcpy.c b/strcpy.c
new file mode 100644
index 0000000..a34dd71
--- /dev/null
+++ b/strcpy.c
@@ -0,0 +1,17 @@
+#include <string.h>
+#include <stdio.h>
+int main(int argc, char* argv[argc+1]) {
+ size_t const len = strlen(argv[0]); // Computes the length
+ // Initialized VLA, /*@\C{23}*/
+ // terminates array with 0 character
+ char name[len+1] = { };
+ // Copies the program name
+ memcpy(name, argv[0], len);
+ if (!strcmp(name, argv[0])) {
+ printf("program name \"%s\" successfully copied\n",
+ name);
+ } else {
+ printf("copying %s leads to different string %s\n",
+ argv[0], name);
+ }
+}
diff --git a/strtoul.c b/strtoul.c
new file mode 100644
index 0000000..9306822
--- /dev/null
+++ b/strtoul.c
@@ -0,0 +1,149 @@
+#include "c23-fallback.h"
+#include <errno.h>
+//#include <limits.h>
+#include <stdio.h>
+//#include <stdlib.h>
+//#include <string.h>
+#include <assert.h>
+/* Supposes that lowercase characters are contiguous. */
+static_assert('z'-'a' == 25,
+ "alphabetic characters not contiguous");
+#include <ctype.h>
+/* Converts an alphanumeric digit to an unsigned */
+/* '0' ... '9' => 0 .. 9u */
+/* 'A' ... 'Z' => 10 .. 35u */
+/* 'a' ... 'z' => 10 .. 35u */
+/* Other values => Greater */
+unsigned hexatridecimal(int a) {
+ if (isdigit(a)) {
+ /* This is guaranteed to work: decimal digits
+ are consecutive, and isdigit is not
+ locale dependent. */
+ return a - '0';
+ } else {
+ /* Leaves a unchanged if it is not lowercase */
+ a = toupper(a);
+ /* Returns value >= 36 if not Latin uppercase */
+ return (isupper(a)) ? 10 + (a - 'A') : -1;
+ }
+}
+
+unsigned long Strtoul_inner(char const s[static 1],
+ size_t i,
+ unsigned base) {
+ unsigned long ret = 0;
+ while (s[i]) {
+ unsigned c = hexatridecimal(s[i]);
+ if (c >= base) break;
+ /* Maximal representable value for 64 bit is
+ 3w5e11264sgsf in base 36 */
+ if (ULONG_MAX/base < ret) {
+ ret = ULONG_MAX;
+ errno = ERANGE;
+ break;
+ }
+ ret *= base;
+ ret += c;
+ ++i;
+ }
+ return ret;
+}
+
+static unsigned find_prefix(char const s[static 1], size_t i, char const t[static 1]) {
+ size_t ret = 0;
+ while (s[i+ret] && (tolower(s[i+ret]) == tolower(t[ret]))) {
+ ++ret;
+ }
+ return ret;
+}
+
+unsigned long Strtoul(char const s[static 1], unsigned base) {
+ if (base > 36u) { /* Tests if base */
+ errno = EINVAL; /* Extends the specification */
+ return ULONG_MAX;
+ }
+ size_t i = strspn(s, " \f\n\r\t\v"); /* Skips spaces */
+ bool switchsign = false; /* Looks for a sign */
+ switch (s[i]) {
+ case '-' :
+ switchsign = true;
+ [[fallthrough]];
+ case '+' :
+ ++i;
+ }
+ /* Test if there is anything left in the string. */
+ if (!s[i]) return 0;
+ if (!base || base == 16 || base == 2) { /* use prefix */
+ size_t adj = find_prefix(s, i, "0x");
+ switch (adj) {
+ case 2: // There is a 0x or a 0X prefix
+ if (!base || base == 16) base = 16;
+ // If we are looking for another base, the x is the end
+ else return 0;
+ break;
+ case 1: // There is a 0 prefix
+ adj = find_prefix(s, i, "0b");
+ switch (adj) {
+ case 1: // There is only a 0 prefix
+ if (!base) base = 8;
+ break;
+ default: // There is a 0b or a 0B prefix
+ if (!base || base == 2) base = 2;
+ // If we are looking for another base, the b is the end
+ else return 0;
+ break;
+ }
+ break;
+ default:
+ if (!base) base = 10;
+ break;
+ }
+ i += adj;
+ }
+ /* Test again, maybe the prefix 0 was the only digit. */
+ if (!s[i]) return 0;
+ /* Now, starts the real conversion */
+ unsigned long ret = Strtoul_inner(s, i, base);
+ return (switchsign) ? -ret : ret;
+}
+
+char* char36(unsigned long n, unsigned base, char buf[static 256]) {
+ if (base > 36) {
+ strcpy(buf, "EINVAL");
+ } else {
+ unsigned i = 256;
+ while (n) {
+ --i;
+ buf[i] = n % base;
+ n /= base;
+ if (buf[i] < 10) {
+ buf[i] += '0';
+ } else {
+ buf[i] += 'a' - 10;
+ }
+ }
+ memmove(buf, &buf[i], 256-i);
+ }
+ return buf;
+}
+
+int main(int argc, char* argv[argc+1]) {
+ static unsigned const code[] = { 0, 8, 10, 16, 36, 37, };
+ for (unsigned j = 0; j < sizeof code/ sizeof code[0]; ++j) {
+ printf("%u:", code[j]);
+ for (int i = 1; i < argc; ++i) {
+ unsigned long res = Strtoul(argv[i], code[j]);
+ if (res == ULONG_MAX && errno) {
+ puts("");
+ fprintf(stderr, "invalid conversion with base %u and \"%s\"\n", code[j], argv[i]);
+ perror("error");
+ errno = 0;
+ } else {
+ printf("\t%lu", res);
+ }
+ }
+ puts("");
+ if (code[j]) printf("%u:\tULONG_MAX %s\n", code[j], char36(ULONG_MAX, code[j], (char[256]){ }));
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/swap.c b/swap.c
new file mode 100644
index 0000000..1f006d7
--- /dev/null
+++ b/swap.c
@@ -0,0 +1,144 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+#define static_assert_compatible(A, B, REASON) \
+ static_assert(_Generic((typeof(A)*)nullptr, \
+ typeof(B)*: true, \
+ default: false), \
+ "expected compatible types: " REASON ", have " #A " and " #B "")
+
+#ifndef __GNUC__
+#define SWAP(X, Y) \
+ do { \
+ /* These two variables have the role of function \
+ parameters. They ensure to evaluate the \
+ expression X and Y once, since these could be \
+ complicated lvalue expressions with evaluated \
+ subexpressions that have side effects. */ \
+ auto const swap_p1 = &(X); \
+ auto const swap_p2 = &(Y); \
+ static_assert_compatible(*swap_p1, *swap_p2, \
+ "to exchange values, '" \
+ #X "' and '" #Y \
+ "' must have compatible types"); \
+ auto swap_tmp = *swap_p1; \
+ *swap_p1 = *swap_p2; \
+ *swap_p2 = swap_tmp; \
+ } while (false)
+#else
+#define SWAP(X, Y) \
+ /* This starts the compound expression construct. */ \
+ ({ \
+ /* These two variables have the role of function \
+ parameters. They ensure to evaluate the \
+ expression X and Y once, since these could be \
+ complicated lvalue expressions with evaluated \
+ subexpressions that have side effects. */ \
+ auto const swap_p1 = &(X); \
+ auto const swap_p2 = &(Y); \
+ static_assert_compatible(*swap_p1, *swap_p2, \
+ "to exchange values, '" \
+ #X "' and '" #Y \
+ "' must have compatible types"); \
+ auto swap_tmp = *swap_p1; \
+ *swap_p1 = *swap_p2; \
+ *swap_p2 = swap_tmp; \
+ /* ensure that the type of the expression is void */ \
+ (void)0; \
+ })
+#endif
+
+
+#define isnegative(X) ((X) < 0)
+
+#define MAX_EQSIGN(X, Y) \
+ /* This starts the compound expression construct. */ \
+ ({ \
+ /* These two captures play the role of function \
+ parameters and read X and Y ... */ \
+ auto const max_x = (X); \
+ auto const max_y = (Y); \
+ /* now the body starts */ \
+ /* types need to have the same signedness */ \
+ (max_x < max_y) ? max_y : max_x; \
+ })
+
+#define MAX(X, Y) \
+ /* This starts the compound expression construct. */ \
+ ({ \
+ auto const max_x = (X); \
+ auto const max_y = (Y); \
+ /* now the body starts */ \
+ ((isnegative(max_x) && !isnegative(max_y)) \
+ ? max_y \
+ : ((isnegative(max_y) && !isnegative(max_x)) \
+ ? max_x \
+ : /* both have the same sign */ \
+ ((max_x < max_y) ? max_y : max_x))); \
+ })
+
+
+
+#define mincharacteristic(X, Y) ((issigned(X)<<2)|(issigned(Y)<<1)|1)
+
+#define minunsigned(X, Y) (_Generic(tozero(X)+tozero(Y), typeof(X): tozero(Y), default: tozero(X)))
+
+#define minreturn(X, Y) \
+ _Generic( \
+ (char(*)[mincharacteristic(X, Y)]){ 0 }, \
+ /* both signed, arithmetic conversion */ \
+ char(*)[4|2|1]: tozero(X)+tozero(Y), \
+ /* only one signed, use it */ \
+ char(*)[0|2|1]: tozero(Y), \
+ char(*)[4|0|1]: tozero(X), \
+ /* both unsigned, use narrower */ \
+ char(*)[0|0|1]: minunsigned(X, Y) \
+)
+
+#define MIN(X, Y) \
+ /* This starts the compound expression construct. */ \
+ ({ \
+ /* These two captures play the role of function \
+ parameters and read X and Y ... */ \
+ auto const min_x = (X); \
+ auto const min_y = (Y); \
+ /* now the body starts */ \
+ typedef typeof(minreturn(min_x, min_y)) min_type; \
+ ((isnegative(min_x) && !issigned(min_y)) \
+ ? (min_type)min_x \
+ : ((isnegative(min_y) && !issigned(min_x)) \
+ ? (min_type)min_y \
+ : ( \
+ /* both have the same signedness or are both positive */ \
+ (min_x < min_y) \
+ ? (min_type)min_x \
+ : (min_type)min_y))); \
+ })
+
+
+int main (int argc, char* argv[static argc + 1]) {
+ size_t i = argc;
+ size_t j = argc+1;
+ printf("%s, %s\n", argv[1], argv[2]);
+ // Works even with side effects, but don't do that
+ SWAP(argv[1+(i++)%2], argv[1+(j++)%2]); // don't do that
+ printf("%s, %s\n", argv[1], argv[2]);
+ // Works even with side effects, but don't do that
+ SWAP(argv[1+(i++)%2], argv[1+(j++)%2]); // don't do that
+ printf("%s, %s\n", argv[1], argv[2]);
+ printf("%d\t%d\t→\t%d\n", -1, -2, MIN(-1, -2));
+ printf("%d\t%ldl\t→\t%ldl\n", -2, -1L, MIN(-2, -1L));
+ printf("%d\t%d\t→\t%d\n", -1, 2, MIN(-1, 2));
+ printf("%d\t%ldl\t→\t%ldl\n", -1, 2L, MIN(-1, 2L));
+ printf("%d\t%uu\t→\t%d\n", -1, 2u, MIN(-1, 2u));
+ printf("%d\t%luul\t→\t%d\n", -1, 2UL, MIN(-1, 2UL));
+ printf("%uu\t%uu\t→\t%uu\n", -1u, -2u, MIN(-1u, -2u));
+ printf("%uu\t%luul\t→\t%uu\n", -1u, -2UL, MIN(-1u, -2UL));
+ static_assert_compatible(1, 0, "both int");
+ //static_assert_compatible(1, 0L, "both int");
+ //static_assert_compatible(1L, 0, "both int");
+ static_assert_compatible(1L, 0L, "both int");
+ enum A : unsigned long { A = -1UL, };
+ static_assert_compatible(A, 0UL, "both int");
+ static_assert_compatible(int[1], int[true], "both int");
+}
diff --git a/tendots.c b/tendots.c
new file mode 100644
index 0000000..0733747
--- /dev/null
+++ b/tendots.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+
+/* delay execution with some crude code,
+ should use thrd_sleep, once we have that */
+void delay(double secs) {
+ double const magic = 4E8; // works just on my machine
+ unsigned long long const nano = secs * magic;
+ for (unsigned long volatile count = 0;
+ count < nano;
+ ++count) {
+ /* nothing here */
+ }
+}
+
+int main(int argc, [[maybe_unused]] char* argv[argc+1]) {
+ fputs("waiting 10 seconds for you to stop me", stdout);
+ if (argc < 3) fflush(stdout);
+ for (unsigned i = 0; i < 10; ++i) {
+ fputc('.', stdout);
+ if (argc < 2) fflush(stdout);
+ delay(1.0);
+ }
+ fputs("\n", stdout);
+ fputs("You did ignore me, so bye bye\n", stdout);
+}
diff --git a/termin.c b/termin.c
new file mode 100644
index 0000000..5de89da
--- /dev/null
+++ b/termin.c
@@ -0,0 +1,97 @@
+#include "termin.h"
+#include "c23-fallback.h"
+#include <termios.h>
+#include <stdio.h>
+#include <stdbool.h>
+#ifndef __STDC_NO_THREADS__
+# include <threads.h>
+#endif
+#include <ctype.h>
+#include <string.h>
+
+/* Store the flags as they were set before termin_unbuffered. */
+static tcflag_t termin_echo;
+
+/* The flags that we manipulate. */
+#define TERMIN_FLAGS (ECHO | ECHONL | ICANON)
+
+void termin_reset(void) {
+ struct termios term;
+ tcgetattr(0, &term);
+ term.c_lflag |= termin_echo;
+ tcsetattr(0, TCSAFLUSH, &term);
+}
+
+void termin_unbuffered(void) {
+ // have stdin unbuffered on stdio level
+ setvbuf(stdin, 0, _IONBF, 0);
+ // store the terminal settings with respect to the flags
+ struct termios term;
+ tcgetattr(0, &term);
+ termin_echo = term.c_lflag & TERMIN_FLAGS;
+ // install exit handlers
+ atexit(termin_reset);
+ at_quick_exit(termin_reset);
+ // change the terminal settings to raw mode
+ term.c_lflag &= ~TERMIN_FLAGS;
+ tcsetattr(0, TCSAFLUSH, &term);
+}
+
+static unsigned minc = 0;
+static unsigned maxc = UCHAR_MAX;
+
+static
+void term_init(void) {
+ while (!termin_trans[minc]) ++minc;
+ while (!termin_trans[maxc]) --maxc;
+}
+
+#ifndef __STDC_NO_THREADS__
+static once_flag term_once = ONCE_FLAG_INIT;
+#endif
+
+static
+bool termin_read_csi(size_t len, char command[len]) {
+ for (char *p = command, *stop = command+len-1; p < stop; ++p) {
+ int c = getchar();
+ // catch all: EOF, 0, control characters
+ if (c < ' ') return false;
+ p[0] = c;
+ if ('@' <= c && c <= '~') {
+ p[1] = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+char const* termin_read_esc(size_t len, char command[len]) {
+ command[1] = getchar();
+ switch (command[1]) {
+ case '[':
+ return
+ termin_read_csi(len-2, command+2)
+ ? command
+ : 0;
+ case 'N':
+ case 'O':
+ case 'Z':
+ case '%':
+ command[2] = getchar();
+ command[3] = 0;
+ return command;
+ default:
+ ungetc(command[1], stdin);
+ return nullptr;
+ }
+}
+
+char termin_translate(char const* command) {
+ call_once(&term_once, term_init);
+ for (unsigned c = minc; c <= maxc; ++c) {
+ if (termin_trans[c] && !strcmp(command, termin_trans[c])) {
+ return c;
+ }
+ }
+ return 0;
+}
diff --git a/termin.h b/termin.h
new file mode 100644
index 0000000..c6ae98d
--- /dev/null
+++ b/termin.h
@@ -0,0 +1,54 @@
+#ifndef TERMIN_H
+#define TERMIN_H 1
+
+#include "c23-fallback.h"
+#include "esc.h"
+
+/**
+ ** @brief Set a terminal on stdin to raw mode.
+ **
+ ** This uses termios.h underneath, so it probably only works on POSIX
+ ** systems.
+ **
+ ** The terminal is automatically reset to its original state on @c
+ ** exit or @c quick_exit by means of ::termin_reset.
+ **/
+void termin_unbuffered(void);
+
+/**
+ ** @brief Reset the terminal to its original state.
+ **/
+void termin_reset(void);
+
+/**
+ ** @brief The translation table for escape sequences.
+ **
+ ** An application that wants to use this module has to provide this
+ ** symbol.
+ **
+ ** @see ::termin_translate
+ **/
+extern char const*const termin_trans[UCHAR_MAX+1];
+
+/**
+ ** @brief Translate and escape sequence to a simple character.
+ **
+ ** This supposes that you assign a simple character for each escape
+ ** sequence that you want to translate.
+ **
+ ** If an exact match for an escape sequence is fount in
+ ** ::termin_trans, its position in the table is the translation.
+ **
+ ** @see ::termin_trans
+ **/
+char termin_translate(char const* command);
+
+/**
+ ** @brief Read an escape sequence into @a command.
+ **
+ ** This reads from @c stdin and supposes that the escape character is
+ ** already stored in @c command[0].
+ **/
+char const* termin_read_esc(size_t len, char command[len]);
+
+#endif
diff --git a/terran.c b/terran.c
new file mode 100644
index 0000000..e604cd9
--- /dev/null
+++ b/terran.c
@@ -0,0 +1,122 @@
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <errno.h>
+
+/*
+ terran multiplicative factors between units that are fixed for
+ Lunar, Day, Hour, Minute, Second
+*/
+enum { dl = 28, hd = 24, mh = 60, sm = 60, sh = mh*sm, sd = hd * mh * sm, };
+
+/*
+ There is a long term period of 128 years, that has exactly 31 leap
+ years. This amounts to 46751 days.
+*/
+enum { dy = 365, yp = 128, dp = yp*dy + 31, };
+
+/*
+ Then, periods of four years are grouped together, that have either
+ 0 or 1 leap year.
+*/
+enum { yq = 4, dq0 = yq*dy, dq1 = dq0 + 1, };
+
+time_t isotime(char const t[static 1]) {
+ struct tm date[1] = {
+ [0] = { .tm_year = 1900,.tm_mon = 1, },
+ };
+ int retscan = sscanf(t, "%i-%i-%i %i:%i:%i",
+ &date->tm_year, &date->tm_mon, &date->tm_mday,
+ &date->tm_hour, &date->tm_min, &date->tm_sec);
+ if (retscan <= 0) {
+ if (errno) perror("can't scan");
+ return 0;
+ }
+ date->tm_year -= 1900;
+ date->tm_mon -= 1;
+ return mktime(date);
+ /* gmtime_r(&ret, date); */
+ /* return mktime(date); */
+}
+
+void time2terran(time_t t) {
+ /* Provide us with the terran epoch. This is not as easy as it may
+ appear as a first site, since it is difficult to express a UTC
+ timestamp in a struct tm: we have convert back and forth with
+ mktime and gmtime to obtain the correct timestamp at 1969-12-22 0:0:0.
+ */
+ struct tm terranEpoch[1] = {
+ {
+ .tm_year = 69, // year starts at 1900
+ .tm_mon = 11, // mon starts at 0
+ .tm_mday = 23, // mday starts at 1
+ },
+ };
+ time_t epoch = mktime(terranEpoch);
+ struct tm terranEpoch2[1];
+ gmtime_r(&epoch, terranEpoch2);
+ time_t epoch2 = mktime(terranEpoch2);
+ terranEpoch[0].tm_sec += difftime(epoch2, epoch);
+ epoch = mktime(terranEpoch);
+
+ /* Now that we have the appropriate epoch, look how many seconds
+ have passed since then.
+ */
+ long secs = difftime(t, epoch);
+ long days = secs/sd;
+ secs -= days*sd;
+
+ /*
+ adjust with respect to the 128 year periods
+ */
+ long per = days / dp;
+ if (days < 0) {
+ per -= 1;
+ }
+ long year = per * yp;
+ days -= per*dp;
+
+ // the first four years aren't leap years
+ if (days < dq0) {
+ year += (days / dy);
+ days -= (days / dy) * dy;
+ } else {
+ // correct for the first 4 years
+ year += yq;
+ days -= dq0;
+ /*
+ In the remaining period every fourth year starting with year 0
+ is a leap year. Compute the amount of such 4 year periods and
+ adjust
+ */
+ long const qer = days / dq1;
+ year += qer*yq;
+ days %= dq1;
+ // the first year is a leap year
+ if (days > dy+1) {
+ // adjust for that leap year
+ year += 1;
+ days -= (dy+1);
+ // compute the remaining days
+ year += (days / dy);
+ days %= dy;
+ }
+ }
+ size_t const lunar = days / dl;
+ days %= dl;
+ while (secs < 0) secs += sd;
+ size_t hour = secs / sh;
+ secs -= hour*sh;
+ size_t min = secs / sm;
+ secs %= sm;
+ printf("%ld.%ld.%ld,%ld:%ld:%ld TC\n", year, lunar, days, hour, min, secs);
+}
+
+int main (int argc, char* argv[argc+1]) {
+ time_t now = time(0);
+ time2terran(now);
+ for (int i = 1; i < argc; ++i)
+ time2terran(isotime(argv[i]));
+ return EXIT_SUCCESS;
+}
diff --git a/test-c8.c b/test-c8.c
new file mode 100644
index 0000000..204841b
--- /dev/null
+++ b/test-c8.c
@@ -0,0 +1,54 @@
+#include <uchar.h>
+#include <stdio.h>
+#include <string.h>
+#include <locale.h>
+
+void print_string(char const*s) {
+ char const*str = s;
+ size_t n = strlen(str)+1;
+ char8_t buffer[n+1];
+ printf("mb→utf8, source: '%s', sliced: ", str);
+ mbstate_t mb = { 0 };
+ for (char8_t *p = buffer; ; p++) {
+ size_t res = mbrtoc8(p, str, n, &mb);
+ if (res > -3) {
+ printf("!error:%zu!\n", -res);
+ return;
+ } else if (res == -3) {
+ printf(",%hhx", *p);
+ } else if (res) {
+ printf("|%hhx", *p);
+ str += res;
+ } else {
+ printf("(%hhx)", *p);
+ str++;
+ break;
+ }
+ }
+ printf("; target: %s\n", buffer);
+ memset(buffer, 0, sizeof buffer);
+ char *p = (char*)buffer;
+ printf("utf8→mb, source: '%s', ", s);
+ for (char const *q = s; ; q++) {
+ size_t res = c8rtomb(p, *q, &mb);
+ if (res) {
+ switch (res) {
+ default: printf("∞"); res = -1; break;
+ case 4: printf("<-->"); break;
+ case 3: printf("<->"); break;
+ case 2: printf("<>"); break;
+ case 1: printf("x"); break;
+ }
+ if (!*p || (res == -1)) break;
+ else p += res;
+ }
+ }
+ printf("; target: %s\n", buffer);
+}
+
+int main(int argc, char* argv[static argc+1]) {
+ if (argc > 1)
+ setlocale(LC_ALL, "");
+ print_string(u8"abcdäα⌫");
+ print_string(u8"→😇");
+}
diff --git a/test-features.c b/test-features.c
new file mode 100644
index 0000000..2224f9b
--- /dev/null
+++ b/test-features.c
@@ -0,0 +1,368 @@
+// test for features
+
+// Compiler versions know to work with this
+
+// gcc-??
+// clang-??
+
+#include "c23-fallback.h"
+#include <stdio.h>
+
+#define test(...) printf("Testing " #__VA_ARGS__ ": %lld\n", (long long)(__VA_ARGS__))
+
+int main(int argc, char* argv[argc+1]) {
+ test(iscomplex(0.0));
+ test(isimaginary(0.0));
+ test(isfloating(0.0));
+ test(iscomplex(I));
+ test(isimaginary(I));
+ test(isfloating(I));
+ test(iscomplex(0U));
+ test(isimaginary(0U));
+ test(isfloating(0U));
+ test(isinteger(0.0));
+ test(isinteger(0U));
+ test(isinteger((char)'A'));
+ test(isunsigned((char)'A'));
+ test(issigned((char)'A'));
+ test(isunsigned((unsigned char)'A'));
+ test(issigned((unsigned char)'A'));
+ test(tominusone((char)'A'));
+ test(tominusone((unsigned char)'A'));
+ test(is_const_target((char*)nullptr));
+ test(is_const_target((char const*)nullptr));
+ test(is_const_target((char volatile*)nullptr));
+ test(is_const_target((char const volatile*)nullptr));
+ test(is_volatile_target((char*)nullptr));
+ test(is_volatile_target((char const*)nullptr));
+ test(is_volatile_target((char volatile*)nullptr));
+ test(is_volatile_target((char const volatile*)nullptr));
+ test(is_const((char){ 0 }));
+ test(is_const((char const){ 0 }));
+ test(is_const((char volatile){ 0 }));
+ test(is_const((char const volatile){ 0 }));
+ test(is_volatile((char){ 0 }));
+ test(is_volatile((char const){ 0 }));
+ test(is_volatile((char volatile){ 0 }));
+ test(is_volatile((char const volatile){ 0 }));
+ test(is_null_pointer_constant(0));
+ test(is_null_pointer_constant(nullptr));
+ test(is_null_pointer_constant((void*)0));
+ test(is_null_pointer_constant((void*)1));
+ test(is_null_pointer_constant((void*)0LL));
+ test(is_null_pointer_constant((void*)(long long){ 0 }));
+ test(is_zero_ice(0));
+ test(is_zero_ice(1));
+ test(is_zero_ice((long long){ 0 }));
+ test(is_zero_ice(0.0));
+ test(is_zero_ice(((void)0, 0)));
+ test(isice(0));
+ test(isice(1));
+ test(isice((long long){ 0 }));
+ test(isice(0.0));
+ test(isvla(0));
+ test(isvla((char[]){ 0 }));
+ char fla[1][1];
+ test(isvla(fla));
+ test(isvla(fla[0]));
+ test(isvla(&fla));
+ test(isvla(*(&fla)));
+ char vla[(int){ 1 }][1];
+ test(isvla(vla));
+ test(isvla(vla[0]));
+ test(isvla(&vla));
+ test(isvla(*(&vla)));
+ test(isvla(char[1][1]));
+ test(isvla(char[(int){ 1 }][1]));
+ test(isvla(char(*)[1][1]));
+ test(isvla(char(*)[(int){ 1 }][1]));
+ test(is_pointer_nvla((char const volatile[]){ 0 }));
+ test(is_pointer_nvla((char const volatile*){ 0 }));
+ test(is_pointer_nvla(fla));
+ test(is_pointer_nvla(fla[0]));
+ test(is_pointer_nvla(&fla));
+ test(is_pointer_nvla(*(&fla)));
+ test(is_pointer_nvla(vla));
+ test(is_pointer_nvla(vla[0]));
+ test(is_pointer_nvla(&vla));
+ test(is_pointer_nvla(*(&vla)));
+ test(is_pointer_vla(fla));
+ test(is_pointer_vla(fla[0]));
+ test(is_pointer_vla(&fla));
+ test(is_pointer_vla(*(&fla)));
+ test(is_pointer_vla(vla));
+ test(is_pointer_vla(vla[0]));
+ test(is_pointer_vla(&vla));
+ test(is_pointer_vla(*(&vla)));
+ test(is_pointer((char const volatile[]){ 0 }));
+ test(is_pointer((char const volatile*){ nullptr }));
+ test(is_pointer((char*)nullptr));
+ test(is_pointer((void*)nullptr));
+ test(is_pointer((char const volatile*)nullptr));
+ test(is_pointer(fla));
+ test(is_pointer(fla[0]));
+ test(is_pointer(&fla));
+ test(is_pointer(*(&fla)));
+ test(is_pointer(vla));
+ test(is_pointer(vla[0]));
+ test(is_pointer(&vla));
+ test(is_pointer(*(&vla)));
+ test(is_fla((char const volatile[]){ 0 }));
+ test(is_fla((char const volatile*){ 0 }));
+ test(is_fla(fla));
+ test(is_fla(fla[0]));
+ test(is_fla(&fla));
+ test(is_fla(*(&fla)));
+ test(is_fla(vla));
+ test(is_fla(vla[0]));
+ test(is_fla(&vla));
+ test(is_fla(*(&vla)));
+ test(is_array((char const volatile[]){ 0 }));
+ test(is_array((char const volatile*){ 0 }));
+ test(is_array(fla));
+ test(is_array(fla[0]));
+ test(is_array(&fla));
+ test(is_array(*(&fla)));
+ test(is_array(vla));
+ test(is_array(vla[0]));
+ test(is_array(&vla));
+ test(is_array(*(&vla)));
+ /* test(is_array(char[1][1])); */
+ /* test(is_array(char[(int){ 1 }][1])); */
+ /* test(is_array(char(*)[1][1])); */
+ /* test(is_array(char(*)[(int){ 1 }][1])); */
+#if __has_feature(c_stdbit_h)
+ test(stdc_count_ones_uc(0));
+ test(stdc_count_ones_us(0));
+ test(stdc_count_ones_ui(0));
+ test(stdc_count_ones_ul(0));
+ test(stdc_count_ones_ull(0));
+ test(stdc_count_ones(0U));
+ test(stdc_count_ones_uc(-1));
+ test(stdc_count_ones_us(-1));
+ test(stdc_count_ones_ui(-1));
+ test(stdc_count_ones_ul(-1));
+ test(stdc_count_ones_ul(-1));
+ test(stdc_count_ones_uc(u'a'));
+ test(stdc_count_ones_us(u'a'));
+ test(stdc_count_ones_ui(u'a'));
+ test(stdc_count_ones_ul(u'a'));
+ test(stdc_count_ones_ull(u'a'));
+#endif
+ test(stdc_count_ones(u'a'));
+ test(stdc_count_ones(0b01010101U));
+ test(stdc_count_ones(0b01010101UL));
+ test(stdc_count_ones(0b01010101ULL));
+#if __has_feature(c_stdbit_h)
+ test(stdc_count_zeros_uc(0));
+ test(stdc_count_zeros_us(0));
+ test(stdc_count_zeros_ui(0));
+ test(stdc_count_zeros_ul(0));
+ test(stdc_count_zeros_ull(0));
+ test(stdc_count_zeros_uc(-1));
+ test(stdc_count_zeros_us(-1));
+ test(stdc_count_zeros_ui(-1));
+ test(stdc_count_zeros_ul(-1));
+ test(stdc_count_zeros_ul(-1));
+#endif
+ test(stdc_count_zeros(u'a'));
+ test(stdc_count_zeros(0b01010101U));
+ test(stdc_count_zeros(0b01010101UL));
+ test(stdc_count_zeros(0b01010101ULL));
+#if __has_feature(c_stdbit_h)
+ test(stdc_leading_zeros_uc(0U));
+ test(stdc_leading_zeros_us(0U));
+ test(stdc_leading_zeros_ui(0U));
+ test(stdc_leading_zeros_ul(0U));
+ test(stdc_leading_zeros_ull(0U));
+#endif
+ test(stdc_leading_zeros(0U));
+ test(stdc_leading_zeros(1U));
+ test(stdc_leading_zeros(0b01010101U));
+ test(stdc_leading_zeros(0b01010101UL));
+ test(stdc_leading_zeros(0b01010101ULL));
+ test(stdc_trailing_zeros(0U));
+ test(stdc_trailing_zeros(1U));
+ test(stdc_trailing_zeros(0b0101010100U));
+ test(stdc_trailing_zeros(0b0101010100UL));
+ test(stdc_trailing_zeros(0b0101010100ULL));
+ test(stdc_trailing_zeros(u'\0'));
+ test(stdc_trailing_zeros(0U));
+ test(stdc_trailing_zeros(0UL));
+ test(stdc_trailing_zeros(0ULL));
+#if __has_feature(c_stdbit_h)
+ test(stdc_leading_ones_ui(UINT_MAX));
+#endif
+ test(stdc_leading_ones(UINT_MAX));
+ test(stdc_leading_ones(0U+INT_MIN));
+ test(stdc_leading_ones(1U+INT_MIN));
+ test(stdc_leading_ones(ULLONG_MAX ^ UINT_MAX));
+ test(stdc_trailing_ones(0U));
+ test(stdc_trailing_ones(1U));
+ test(stdc_trailing_ones(0b01010101U));
+ test(stdc_trailing_ones(0b01010101UL));
+ test(stdc_trailing_ones(0b01010101ULL));
+ test(stdc_trailing_ones(u'\0'));
+ test(stdc_trailing_ones(UINT_MAX>>1));
+ test(stdc_trailing_ones(ULONG_MAX>>1));
+ test(stdc_trailing_ones(ULONG_MAX>>1));
+ // argument must be an unsigned type
+ // test(stdc_leading_ones(1));
+ /* test(stdc_leading_zeros(true)); */
+ /* test(stdc_leading_zeros(false)); */
+ /* test(stdc_leading_zeros((bool)1)); */
+ test(stdc_leading_zeros((unsigned char)1));
+#if __has_feature(c_stdbit_h)
+ test(stdc_leading_zeros_uc(u'a'));
+ test(stdc_leading_zeros_us(u'a'));
+ test(stdc_leading_zeros_ui(u'a'));
+ test(stdc_leading_zeros_ul(u'a'));
+ test(stdc_leading_zeros_ull(u'a'));
+#endif
+ test(stdc_leading_zeros(u'a'));
+ test(stdc_has_single_bit(u'a'));
+ test(stdc_has_single_bit(u'\2'));
+ /* test(stdc_has_single_bit(true)); */
+ /* test(stdc_has_single_bit(false)); */
+#if __has_feature(c_stdbit_h)
+ test(stdc_has_single_bit_uc(0U));
+ test(stdc_has_single_bit_uc(1U));
+ test(stdc_has_single_bit_uc(2U));
+ test(stdc_has_single_bit_uc(3U));
+ test(stdc_has_single_bit_uc(4U));
+#endif
+ test(stdc_has_single_bit(0U));
+ test(stdc_has_single_bit(1U));
+ test(stdc_has_single_bit(2U));
+ test(stdc_has_single_bit(3U));
+ test(stdc_has_single_bit(4U));
+ test(stdc_has_single_bit(1UL));
+ test(stdc_has_single_bit(2UL));
+ test(stdc_has_single_bit(3UL));
+ test(stdc_has_single_bit(1ULL));
+ test(stdc_has_single_bit(2ULL));
+ test(stdc_has_single_bit(3ULL));
+#if __has_feature(c_stdbit_h)
+ test(stdc_bit_width_uc(u'a'));
+ test(stdc_bit_width_us(u'a'));
+ test(stdc_bit_width_ui(u'a'));
+ test(stdc_bit_width_ul(u'a'));
+ test(stdc_bit_width_ull(u'a'));
+ test(stdc_bit_width(u'a'));
+#endif
+ /* test(stdc_bit_width(true)); */
+ /* test(stdc_bit_width(false)); */
+ test(stdc_bit_width(1U));
+ test(stdc_bit_width(2U));
+ test(stdc_bit_width(3U));
+ test(stdc_bit_width(1UL));
+ test(stdc_bit_width(2UL));
+ test(stdc_bit_width(3UL));
+ test(stdc_bit_width(1ULL));
+ test(stdc_bit_width(2ULL));
+ test(stdc_bit_width(3ULL));
+ /* test(stdc_bit_floor(true)); */
+ /* test(stdc_bit_floor(false)); */
+ test(stdc_bit_floor(0U));
+ test(stdc_bit_floor(1U));
+ test(stdc_bit_floor(2U));
+ test(stdc_bit_floor(3U));
+ test(stdc_bit_floor(4U));
+ test(stdc_bit_floor(1UL));
+ test(stdc_bit_floor(2UL));
+ test(stdc_bit_floor(3UL));
+ test(stdc_bit_floor(1ULL));
+ test(stdc_bit_floor(2ULL));
+ test(stdc_bit_floor(3ULL));
+ /* test(stdc_bit_ceil(true)); */
+ /* test(stdc_bit_ceil(false)); */
+ test(stdc_bit_ceil(0U));
+ test(stdc_bit_ceil(1U));
+ test(stdc_bit_ceil(2U));
+ test(stdc_bit_ceil(3U));
+ test(stdc_bit_ceil(4U));
+ test(stdc_bit_ceil(1UL));
+ test(stdc_bit_ceil(2UL));
+ test(stdc_bit_ceil(3UL));
+ test(stdc_bit_ceil(1ULL));
+ test(stdc_bit_ceil(2ULL));
+ test(stdc_bit_ceil(3ULL));
+#ifdef UINT128_MAX
+ test(stdc_leading_zeros(UINT128_MAX));
+ test(stdc_leading_zeros(UINT128_MAX>>63));
+ test(stdc_leading_zeros(UINT128_MAX>>64));
+ test(stdc_leading_zeros(UINT128_MAX>>65));
+ test(stdc_leading_zeros(UINT128_MAX ^ INT128_MIN));
+ test(stdc_leading_zeros((UINT128_MAX ^ INT128_MIN)>>3));
+ test(stdc_leading_zeros((UINT128_MAX ^ INT128_MIN)>>63));
+ test(stdc_count_ones(UINT128_MAX ^ 0b01010101U));
+ test(stdc_count_zeros(UINT128_MAX ^ 0b01010101U));
+ test(stdc_trailing_zeros(~UINT128_MAX));
+ test(stdc_trailing_zeros(UINT128_MAX - 1));
+ test(stdc_trailing_zeros(UINT128_MAX<<63));
+ test(stdc_trailing_zeros(UINT128_MAX<<64));
+ test(stdc_trailing_zeros(UINT128_MAX<<65));
+ test(stdc_trailing_zeros(UINT128_MAX & INT128_MIN));
+ test(stdc_trailing_zeros((UINT128_MAX & INT128_MIN)>>3));
+ test(stdc_trailing_zeros((UINT128_MAX & INT128_MIN)>>63));
+ test(stdc_leading_ones(UINT128_MAX));
+ test(stdc_leading_ones(UINT128_MAX ^ INT128_MIN));
+ test(stdc_leading_ones(UINT128_MAX ^ UINT_MAX));
+ test(stdc_leading_ones(UINT128_MAX ^ (UINT_MAX>>3)));
+ test(stdc_trailing_ones(UINT128_MAX));
+ test(stdc_trailing_ones(UINT128_MAX - 1));
+ test(stdc_trailing_ones(UINT128_MAX>>63));
+ test(stdc_trailing_ones(UINT128_MAX>>64));
+ test(stdc_trailing_ones(UINT128_MAX>>65));
+ test(stdc_trailing_ones(UINT128_MAX ^ INT128_MIN));
+ test(stdc_trailing_ones((UINT128_MAX ^ INT128_MIN)>>3));
+ test(stdc_trailing_ones((UINT128_MAX ^ INT128_MIN)>>63));
+ test(stdc_has_single_bit(UINT128_MAX ^ INT128_MIN));
+ test(stdc_has_single_bit(UINT128_MAX & INT128_MIN));
+ test(stdc_bit_width(UINT128_MAX ^ INT128_MIN));
+ test(stdc_bit_width(UINT128_MAX & INT128_MIN));
+ test(stdc_bit_floor(UINT128_MAX & INT128_MIN) == INT128_MIN);
+ test(stdc_bit_ceil(UINT128_MAX>>1) == INT128_MIN);
+ test(stdc_leading_zeros(argc+tozero(UINT128_MAX)));
+#endif
+#if BITINT_MAXWIDTH >= 128
+ unsigned _BitInt(128) const ubitint128_max = -1;
+ signed _BitInt(128) const bitint128_min = ubitint128_max/2;
+ test(stdc_leading_zeros(ubitint128_max));
+ test(stdc_leading_zeros(ubitint128_max>>63));
+ test(stdc_leading_zeros(ubitint128_max>>64));
+ test(stdc_leading_zeros(ubitint128_max>>65));
+ test(stdc_leading_zeros(ubitint128_max ^ bitint128_min));
+ test(stdc_leading_zeros((ubitint128_max ^ bitint128_min)>>3));
+ test(stdc_leading_zeros((ubitint128_max ^ bitint128_min)>>63));
+ test(stdc_count_ones(ubitint128_max ^ 0b01010101U));
+ test(stdc_count_zeros(ubitint128_max ^ 0b01010101U));
+ test(stdc_trailing_zeros(~ubitint128_max));
+ test(stdc_trailing_zeros(ubitint128_max - 1));
+ test(stdc_trailing_zeros(ubitint128_max<<63));
+ test(stdc_trailing_zeros(ubitint128_max<<64));
+ test(stdc_trailing_zeros(ubitint128_max<<65));
+ test(stdc_trailing_zeros(ubitint128_max & bitint128_min));
+ test(stdc_trailing_zeros((ubitint128_max & bitint128_min)>>3));
+ test(stdc_trailing_zeros((ubitint128_max & bitint128_min)>>63));
+ test(stdc_leading_ones(ubitint128_max));
+ test(stdc_leading_ones(ubitint128_max ^ bitint128_min));
+ test(stdc_leading_ones(ubitint128_max ^ UINT_MAX));
+ test(stdc_leading_ones(ubitint128_max ^ (UINT_MAX>>3)));
+ test(stdc_trailing_ones(ubitint128_max));
+ test(stdc_trailing_ones(ubitint128_max - 1));
+ test(stdc_trailing_ones(ubitint128_max>>63));
+ test(stdc_trailing_ones(ubitint128_max>>64));
+ test(stdc_trailing_ones(ubitint128_max>>65));
+ test(stdc_trailing_ones(ubitint128_max ^ bitint128_min));
+ test(stdc_trailing_ones((ubitint128_max ^ bitint128_min)>>3));
+ test(stdc_trailing_ones((ubitint128_max ^ bitint128_min)>>63));
+ test(stdc_has_single_bit(ubitint128_max ^ bitint128_min));
+ test(stdc_has_single_bit(ubitint128_max & bitint128_min));
+ test(stdc_bit_width(ubitint128_max ^ bitint128_min));
+ test(stdc_bit_width(ubitint128_max & bitint128_min));
+ test(stdc_bit_floor(ubitint128_max & bitint128_min) == bitint128_min);
+ test(stdc_bit_ceil(ubitint128_max>>1) == bitint128_min);
+ test(stdc_leading_zeros(argc+tozero(ubitint128_max)));
+#endif
+}
diff --git a/test-lib.c b/test-lib.c
new file mode 100644
index 0000000..9430df0
--- /dev/null
+++ b/test-lib.c
@@ -0,0 +1,46 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+
+typedef char const* key;
+
+
+int comp_key(key const*a, key const*b) {
+ key A = *a;
+ key B = *b;
+ return strcmp(A, B);
+}
+
+int comp(void const*a, void const*b) {
+ key const*A = a;
+ key const*B = b;
+ return comp_key(A, B);
+}
+
+int main() {
+ static key data[] = {
+ "Tina",
+ "Albert",
+ "Xaver",
+ "Albertina",
+ };
+
+ qsort(data, (sizeof data)/(sizeof data[0]), sizeof data[0], comp);
+ for (size_t i = 0; i < (sizeof data)/(sizeof data[0]); ++i)
+ printf("%zu: \"%s\"\n", i, data[i]);
+ key k = "Tina";
+ key* n1 = bsearch(&k, data, (sizeof data)/(sizeof data[0]), sizeof data[0], comp);
+ printf("found: \"%s\"\n", n1 ? *n1 : "unknown");
+ key const* d = data;
+ key const* n2 = bsearch(&k, d, (sizeof data)/(sizeof data[0]), sizeof data[0], comp);
+ printf("found: \"%s\"\n", n2 ? *n2 : "unknown");
+ char buf[25] = { 0 };
+ strfroml(buf, 25, "%.10g", 3744.0083383L);
+ printf("strfroml %.10LG → \"%s\"\n", 3744.0083383L, buf);
+ strfroml(buf, 25, "%.10Lg", 3744.0083383L);
+ printf("invalid format leads to \"%s\"\n", buf);
+ for (char const* p = buf; p < buf+16; ++p)
+ printf("alignment %p: %zu, suitable for int*: %d\n", p, memalignment(p), memalignment(p)>=alignof(int));
+ struct two {int a; int b; };
+ assert((struct two){ .a = 0, .b = 1, }.b);
+ assert((struct two){ .a = 0, .b = 1, }.a);
+}
diff --git a/test-printf.c b/test-printf.c
new file mode 100644
index 0000000..e6a544e
--- /dev/null
+++ b/test-printf.c
@@ -0,0 +1,341 @@
+#include "c23-fallback.h"
+#include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#ifndef PRIb8
+# error "library support of binary number representations for input and output are required for this test"
+// really stop it
+# include "really stop useless compilation right now"
+#endif
+
+#define PS(S, B) \
+do { \
+ errno = 0; \
+ unsigned long long res = strtoull(S, nullptr, B); \
+ if (errno) { \
+ perror("strtoull error " S); \
+ errno = 0; \
+ } else { \
+ printf("base %d,\t\"%s\" →\t%llu\n", B, S, res); \
+ if (errno) perror("printf error"); \
+ errno = 0; \
+ } \
+ res = wcstoull(L ## S, nullptr, B); \
+ if (errno) { \
+ perror("wcstoull error " S); \
+ errno = 0; \
+ } else { \
+ printf("base %d,\tL\"%s\" →\t%llu\n", B, S, res); \
+ if (errno) perror("printf error"); \
+ errno = 0; \
+ } \
+ } while (false)
+
+#define PSCAN(F, S, X) \
+do { \
+ sscanf(S, F, &(X)); \
+ if (errno) { \
+ perror("sscanf error \"" S "\" format \"" F "\""); \
+ errno = 0; \
+ } else { \
+ printf("%s: \"%s\" → " F "\n", F, S, X/*+0ULL*/); \
+ if (errno) perror("printf error " F); \
+ errno = 0; \
+ } \
+ swscanf(L ## S, L ## F, &(X)); \
+ if (errno) { \
+ perror("swscanf error L\"" S "\" format L\"" F "\""); \
+ errno = 0; \
+ } else { \
+ printf("%s: L\"%s\" → " F "\n", F, S, X/*+0ULL*/); \
+ if (errno) perror("printf error " F); \
+ errno = 0; \
+ } \
+ } while(false)
+
+int main(int argc, char* argv[static argc+1]) {
+ printf("%b\n", 35u);
+ printf("%#b\n", 35u);
+ printf("%B\n", 35u);
+ printf("%#B\n", 35u);
+ if (argc > 1) {
+ unsigned long b = strtoul(argv[1], nullptr, 0);
+ printf("%lb\n", b);
+ printf("%#lb\n", b);
+ printf("%lB\n", b);
+ printf("%#lB\n", b);
+ }
+ perror("printf error"); errno = 0;
+ uint8_t u8 = -1;
+ uint16_t u16 = -1;
+ uint32_t u32 = -1;
+ uint64_t u64 = -1;
+ printf("PRIb8:\t%" PRIb8 "\n", u8);
+ printf("PRIb16:\t%" PRIb16 "\n", u16);
+ printf("PRIb32:\t%" PRIb32 "\n", u32);
+ printf("PRIb64:\t%" PRIb64 "\n", u64);
+ printf("PRIB8:\t%" PRIB8 "\n", u8);
+ printf("PRIB16:\t%" PRIB16 "\n", u16);
+ printf("PRIB32:\t%" PRIB32 "\n", u32);
+ printf("PRIB64:\t%" PRIB64 "\n", u64);
+ printf("PRIb8:\t%#" PRIb8 "\n", u8);
+ printf("PRIb16:\t%#" PRIb16 "\n", u16);
+ printf("PRIb32:\t%#" PRIb32 "\n", u32);
+ printf("PRIb64:\t%#" PRIb64 "\n", u64);
+ printf("PRIB8:\t%#" PRIB8 "\n", u8);
+ printf("PRIB16:\t%#" PRIB16 "\n", u16);
+ printf("PRIB32:\t%#" PRIB32 "\n", u32);
+ printf("PRIB64:\t%#" PRIB64 "\n", u64);
+ perror("printf error"); errno = 0;
+#if __STDC_VERSION_INTTYPES_H__ < 201800L
+# ifndef C23_FALLBACK_SILENT
+# warning "no C23 printf support found, skipping tests for %w length modifiers"
+# endif
+#else
+ printf("11:\t%w11x\n", u8);
+ perror("printf error"); errno = 0;
+ printf("8:\t%w8x\n", u8);
+ printf("16:\t%w16x\n", u16);
+ printf("32:\t%w32x\n", u32);
+ printf("64:\t%w64x\n", u64);
+ perror("printf error"); errno = 0;
+ printf("11:\t%w11x\n", u8);
+ perror("printf error"); errno = 0;
+ printf("8:\t%w8u\n", u8);
+ printf("16:\t%w16u\n", u16);
+ printf("32:\t%w32u\n", u32);
+ printf("64:\t%w64u\n", u64);
+ perror("printf error"); errno = 0;
+ printf("11:\t%w11x\n", u8);
+ perror("printf error"); errno = 0;
+ printf("8:\t%w8d\n", u8);
+ printf("16:\t%w16d\n", u16);
+ printf("32:\t%w32d\n", u32);
+ printf("64:\t%w64d\n", u64);
+ perror("printf error"); errno = 0;
+ int8_t i8 = INT8_MAX;
+ int16_t i16 = INT16_MAX;
+ int32_t i32 = INT32_MAX;
+ int64_t i64 = INT64_MAX;
+ printf("11:\t%w11x\n", i8);
+ perror("printf error"); errno = 0;
+ printf("8:\t%w8x\n", i8);
+ printf("16:\t%w16x\n", i16);
+ printf("32:\t%w32x\n", i32);
+ printf("64:\t%w64x\n", i64);
+ perror("printf error"); errno = 0;
+ printf("11:\t%w11x\n", i8);
+ perror("printf error"); errno = 0;
+ printf("8:\t%w8u\n", i8);
+ printf("16:\t%w16u\n", i16);
+ printf("32:\t%w32u\n", i32);
+ printf("64:\t%w64u\n", i64);
+ perror("printf error"); errno = 0;
+ printf("11:\t%w11x\n", i8);
+ perror("printf error"); errno = 0;
+ printf("8:\t%w8d\n", i8);
+ printf("16:\t%w16d\n", i16);
+ printf("32:\t%w32d\n", i32);
+ printf("64:\t%w64d\n", i64);
+ perror("printf error"); errno = 0;
+ uint_fast8_t uf8 = -1;
+ uint_fast16_t uf16 = -1;
+ uint_fast32_t uf32 = -1;
+ uint_fast64_t uf64 = -1;
+ printf("11 fast:\t%wf11x\n", uf8);
+ perror("printf error"); errno = 0;
+ printf("8 fast:\t%wf8x\n", uf8);
+ printf("16 fast:\t%wf16x\n", uf16);
+ printf("32 fast:\t%wf32x\n", uf32);
+ printf("64 fast:\t%wf64x\n", uf64);
+ perror("printf error"); errno = 0;
+ printf("11 fast:\t%wf11x\n", uf8);
+ perror("printf error"); errno = 0;
+ printf("8 fast:\t%wf8u\n", uf8);
+ printf("16 fast:\t%wf16u\n", uf16);
+ printf("32 fast:\t%wf32u\n", uf32);
+ printf("64 fast:\t%wf64u\n", uf64);
+ perror("printf error"); errno = 0;
+ printf("11 fast:\t%wf11x\n", uf8);
+ perror("printf error"); errno = 0;
+ printf("8 fast:\t%wf8d\n", uf8);
+ printf("16 fast:\t%wf16d\n", uf16);
+ printf("32 fast:\t%wf32d\n", uf32);
+ printf("64 fast:\t%wf64d\n", uf64);
+ perror("printf error"); errno = 0;
+ int_fast8_t if8 = INT_FAST8_MAX;
+ int_fast16_t if16 = INT_FAST16_MAX;
+ int_fast32_t if32 = INT_FAST32_MAX;
+ int_fast64_t if64 = INT_FAST64_MAX;
+ printf("11 fast:\t%wf11x\n", if8);
+ perror("printf error"); errno = 0;
+ printf("8 fast:\t%wf8x\n", if8);
+ printf("16 fast:\t%wf16x\n", if16);
+ printf("32 fast:\t%wf32x\n", if32);
+ printf("64 fast:\t%wf64x\n", if64);
+ perror("printf error"); errno = 0;
+ printf("11 fast:\t%wf11x\n", if8);
+ perror("printf error"); errno = 0;
+ printf("8 fast:\t%wf8u\n", if8);
+ printf("16 fast:\t%wf16u\n", if16);
+ printf("32 fast:\t%wf32u\n", if32);
+ printf("64 fast:\t%wf64u\n", if64);
+ perror("printf error"); errno = 0;
+ printf("11 fast:\t%wf11x\n", if8);
+ perror("printf error"); errno = 0;
+ printf("8 fast:\t%wf8d\n", if8);
+ printf("16 fast:\t%wf16d\n", if16);
+ printf("32 fast:\t%wf32d\n", if32);
+ printf("64 fast:\t%wf64d\n", if64);
+ perror("printf error"); errno = 0;
+#if UINT128_WIDTH || __SIZEOF_INT128__
+#if UINT128_WIDTH
+ puts("------------ found standard uint128_t -------------");
+ uint128_t u128 = -1;
+ int128_t i128 = u128/2;
+#elif __SIZEOF_INT128__
+ puts("------------ found non-standard __uint128_t -------------");
+ __uint128_t u128 = -1;
+ __int128_t i128 = u128/2;
+#endif
+ printf("128u:\t%w128u\n", (__uint128_t)0);
+ printf("128u:\t%w128u\n", u128);
+ printf("128d:\t%w128d\n", u128);
+ printf("128d:\t%w128u\n", (__int128_t)0);
+ printf("128x:\t%w128x\n", i128);
+ printf("128d:\t%w128d\n", i128);
+ printf("128b:\t%#w128b\n", i128);
+#endif
+ perror("printf error"); errno = 0;
+#endif
+ puts("-------------- scanf functions ------------");
+ PS("2147483647", 0);
+ PS("2147483647", 2);
+ PS("2147483647", 8);
+ PS("2147483647", 10);
+ PS("2147483647", 16);
+ PS("-1", 0);
+ PS("-1", 2);
+ PS("-1", 8);
+ PS("-1", 10);
+ PS("-1", 16);
+ PS("0b1", 0);
+ PS("0b1", 2);
+ PS("0b1", 8);
+ PS("0b1", 10);
+ PS("0b1", 16);
+ PS("0B1", 0);
+ PS("0B1", 2);
+ PS("0B1", 8);
+ PS("0B1", 10);
+ PS("0B1", 16);
+ PS("0x1", 0);
+ PS("0x1", 2);
+ PS("0x1", 8);
+ PS("0x1", 10);
+ PS("0x1", 16);
+ PS("0b1111111111111111111111111111111111111111111111111111111111111111", 0);
+ PS("0b1111111111111111111111111111111111111111111111111111111111111111", 2);
+ PS("0b1111111111111111111111111111111111111111111111111111111111111111", 8);
+ PS("0b1111111111111111111111111111111111111111111111111111111111111111", 10);
+ PS("0b1111111111111111111111111111111111111111111111111111111111111111", 16);
+ PS("0B1111111111111111111111111111111111111111111111111111111111111111", 0);
+ PS("0B1111111111111111111111111111111111111111111111111111111111111111", 2);
+ PS("0B1111111111111111111111111111111111111111111111111111111111111111", 8);
+ PS("0B1111111111111111111111111111111111111111111111111111111111111111", 10);
+ PS("0B1111111111111111111111111111111111111111111111111111111111111111", 16);
+ PS("0xffffffffffffffff", 0);
+ PS("0xffffffffffffffff", 2);
+ PS("0xffffffffffffffff", 8);
+ PS("0xffffffffffffffff", 10);
+ PS("0xffffffffffffffff", 16);
+ PSCAN("%" SCNb8, "0b1", u8);
+ PSCAN("%" SCNu8, "0b1", u8);
+ PSCAN("%" SCNx8, "0b1", u8);
+ PSCAN("%" SCNb8, "0x1", u8);
+ PSCAN("%" SCNu8, "0x1", u8);
+ PSCAN("%" SCNx8, "0x1", u8);
+ PSCAN("%" SCNb16, "0b1", u16);
+ PSCAN("%" SCNu16, "0b1", u16);
+ PSCAN("%" SCNx16, "0b1", u16);
+ PSCAN("%" SCNb16, "0x1", u16);
+ PSCAN("%" SCNu16, "0x1", u16);
+ PSCAN("%" SCNx16, "0x1", u16);
+ PSCAN("%" SCNb32, "0b1", u32);
+ PSCAN("%" SCNu32, "0b1", u32);
+ PSCAN("%" SCNx32, "0b1", u32);
+ PSCAN("%" SCNb32, "0x1", u32);
+ PSCAN("%" SCNu32, "0x1", u32);
+ PSCAN("%" SCNx32, "0x1", u32);
+ PSCAN("%" SCNb64, "0b1", u64);
+ PSCAN("%" SCNu64, "0b1", u64);
+ PSCAN("%" SCNx64, "0b1", u64);
+ PSCAN("%" SCNb64, "0x1", u64);
+ PSCAN("%" SCNu64, "0x1", u64);
+ PSCAN("%" SCNx64, "0x1", u64);
+#if __STDC_VERSION_INTTYPES_H__ < 201800L
+# ifndef C23_FALLBACK_SILENT
+# warning "no C23 scanf support found, skipping tests for %w length modifiers"
+# endif
+#else
+ PSCAN("%w8b", "0b1", u8);
+ PSCAN("%w8u", "0b1", u8);
+ PSCAN("%w8x", "0b1", u8);
+ PSCAN("%w8b", "0x1", u8);
+ PSCAN("%w8u", "0x1", u8);
+ PSCAN("%w8x", "0x1", u8);
+ PSCAN("%w16b", "0b1", u16);
+ PSCAN("%w16u", "0b1", u16);
+ PSCAN("%w16x", "0b1", u16);
+ PSCAN("%w16b", "0x1", u16);
+ PSCAN("%w16u", "0x1", u16);
+ PSCAN("%w16x", "0x1", u16);
+ PSCAN("%w32b", "0b1", u32);
+ PSCAN("%w32u", "0b1", u32);
+ PSCAN("%w32x", "0b1", u32);
+ PSCAN("%w32b", "0x1", u32);
+ PSCAN("%w32u", "0x1", u32);
+ PSCAN("%w32x", "0x1", u32);
+ PSCAN("%w64b", "0b1", u64);
+ PSCAN("%w64u", "0b1", u64);
+ PSCAN("%w64x", "0b1", u64);
+ PSCAN("%w64b", "0x1", u64);
+ PSCAN("%w64u", "0x1", u64);
+ PSCAN("%w64x", "0x1", u64);
+#if UINT128_WIDTH || __SIZEOF_INT128__
+ PSCAN("%w128b", "0b1", u128);
+ PSCAN("%w128u", "0b1", u128);
+ PSCAN("%w128x", "0b1", u128);
+ PSCAN("%w128b", "0x1", u128);
+ PSCAN("%w128u", "0x1", u128);
+ PSCAN("%w128x", "0x1", u128);
+
+ PSCAN("%w128b", "0b11111111111111111111111111111111111111111111111111111111111111111", u128);
+ PSCAN("%w128u", "0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128);
+ PSCAN("%w128x", "0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128);
+ PSCAN("%w128b", "0x7ffffffffffffffffffffffffffffffe", u128);
+ PSCAN("%w128u", "0x7ffffffffffffffffffffffffffffffe", u128);
+ PSCAN("%w128x", "0x7ffffffffffffffffffffffffffffffe", u128);
+
+ PSCAN("%w128b", "0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128);
+ PSCAN("%w128u", "0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128);
+ PSCAN("%w128x", "0b1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", u128);
+ PSCAN("%w128b", "0x7fffffffffffffffffffffffffffffff", u128);
+ PSCAN("%w128u", "0x7fffffffffffffffffffffffffffffff", u128);
+ PSCAN("%w128x", "0x7fffffffffffffffffffffffffffffff", u128);
+ PSCAN("%w128b", "0xffffffffffffffffffffffffffffffff", u128);
+ PSCAN("%w128u", "0xffffffffffffffffffffffffffffffff", u128);
+ PSCAN("%w128x", "0xffffffffffffffffffffffffffffffff", u128);
+#ifdef SCNb128
+ puts("-------------- SCN macros ------------");
+ PSCAN("%" SCNb128, "0b1", u128);
+ PSCAN("%" SCNu128, "0b1", u128);
+ PSCAN("%" SCNx128, "0b1", u128);
+ PSCAN("%" SCNb128, "0x1", u128);
+ PSCAN("%" SCNu128, "0x1", u128);
+ PSCAN("%" SCNx128, "0x1", u128);
+#endif
+#endif
+#endif
+}
diff --git a/test-reorder.c b/test-reorder.c
new file mode 100644
index 0000000..d9c16e6
--- /dev/null
+++ b/test-reorder.c
@@ -0,0 +1,2 @@
+#include "c23-fallback.h"
+#include "reorder.h"
diff --git a/test-strfrom.c b/test-strfrom.c
new file mode 100644
index 0000000..7c5316b
--- /dev/null
+++ b/test-strfrom.c
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#define testIt(FUNC, F, V) \
+ do { \
+ char buf[256]; \
+ int ret = FUNC(buf, 256, (F), (V)); \
+ printf("%d: %s\n", ret, buf); \
+ } while (0)
+
+int main(void) {
+ testIt(strfromd, "%g", 1.0/7);
+ testIt(strfromd, "%.6766g", 1.0/7);
+ testIt(strfromd, "%.0000000000000000000000000000000000000000000006766g", 1.0/7);
+ testIt(strfroml, "%g", 1.0L/7);
+ testIt(strfroml, "%.6766g", 1.0L/7);
+ testIt(strfroml, "%.0000000000000000000000000000000000000000000006766g", 1.0L/7);
+ testIt(strfroml, "%.547996766g", 1.0L/7);
+ testIt(strfromd, "%a", 1.0/7);
+ testIt(strfromd, "%.6766a", 1.0/7);
+ testIt(strfromd, "%.0000000000000000000000000000000000000000000006766a", 1.0/7);
+ testIt(strfroml, "%a", 1.0L/7);
+ testIt(strfroml, "%.6766a", 1.0L/7);
+ testIt(strfroml, "%.0000000000000000000000000000000000000000000006766a", 1.0L/7);
+ testIt(strfroml, "%.547996766a", 1.0L/7);
+}
diff --git a/test_circular.c b/test_circular.c
new file mode 100644
index 0000000..338e16a
--- /dev/null
+++ b/test_circular.c
@@ -0,0 +1,22 @@
+#include <stdlib.h>
+#include "circular.h"
+
+int main(int argc, char* argv[]) {
+ size_t len = argc > 1 ? strtoull(argv[1], nullptr, 0) : 10;
+ fprintf(stderr, "starting with %zu elements in total\n", len);
+ circular* c = circular_new(len);
+ circular_fput(c, stderr);
+ for (size_t i = 0; i < len-1; ++i) {
+ if (!circular_append(c, i)) break;
+ }
+ circular_fput(c, stderr);
+ for (size_t i = 0; i < len/2; ++i) {
+ if (!circular_append(c, circular_pop(c))) break;
+ }
+ circular_fput(c, stderr);
+ c = circular_resize(c, 2*len);
+ circular_fput(c, stderr);
+ c = circular_resize(c, len);
+ circular_fput(c, stderr);
+ circular_delete(c);
+}
diff --git a/tgstring.h b/tgstring.h
new file mode 100644
index 0000000..e58a6cd
--- /dev/null
+++ b/tgstring.h
@@ -0,0 +1,485 @@
+#ifndef __STDC_TGSTRING__
+# define __STDC_TGSTRING__ 201610L
+
+#include "c23-fallback.h"
+
+/**
+ ** @file
+ ** @brief Type generic string interfaces
+ **
+ ** @copyright 2016 Jens Gustedt
+ **
+ ** @remark This code is distributed under a Creative Commons
+ ** Attribution 4.0 International License.
+ **
+ ** @warning This code is provided as is, use at your own risk.
+ **
+ ** This header file provides type generic replacements for the C
+ ** library string functions that are "const-aware" and that chose
+ ** the right alternative between narrow and wide string
+ ** functions.
+ **
+ ** Generally this interfaces (and replaces) all the C standard
+ ** functions with names starting with `str`. These are
+ **
+ ** - `strcat`
+ ** - `strchr`
+ ** - `strcmp`
+ ** - `strcoll`
+ ** - `strcpy`
+ ** - `strcspn`
+ ** - `strlen`
+ ** - `strncat`
+ ** - `strncmp`
+ ** - `strncpy`
+ ** - `strntok`
+ ** - `strpbrk`
+ ** - `strrchr`
+ ** - `strspn`
+ ** - `strstr`
+ ** - `strtod`
+ ** - `strtof`
+ ** - `strtoimax`
+ ** - `strtol`
+ ** - `strtold`
+ ** - `strtoll`
+ ** - `strtoul`
+ ** - `strtoull`
+ ** - `strtoumax`
+ ** - `strxfrm`
+ **
+ ** The interfaces that are prefixed with `mem` are more difficult
+ ** to match, since the types of parameters of the existing `mem`
+ ** and `wmem` functions are not consistent: the `mem` functions
+ ** work with `void*`, thus on @b bytes, whereas the `wmem`
+ ** functions work with `wchar_t`, thus on wide characters. The
+ ** first can receive any pointer argument whereas the second can
+ ** only receive wide character arrays. Generally the semantics of
+ ** a type generic interface that mixes bytes and wide characters
+ ** would not be clear since also the interpretation of a length
+ ** parameter would change.
+ **
+ ** Out of these functions only the functions `memchr` and
+ ** `wmemchr` have the const-awareness problem, so we provide
+ ** these. Because for `wmemchr` the parameter is fixed to be
+ ** derived from `wchar_t`, this one is not very difficult.
+ **
+ ** `memchr` is a bit tricky, because the original receives a
+ ** `void const*` that might come from any data pointer. We have
+ ** to do some black magic to decide if the target type is
+ ** `const`-qualified.
+ **
+ ** This uses a lot of small stub functions that usually will be
+ ** inlined. If you are a C library provider that wants to
+ ** integrate this, see the discussion of ::_TG_INLINE.
+ **/
+
+# if __STDC_VERSION__ < 201112L
+# error "tgstring.h needs a C11 conforming compiler"
+# endif
+
+# include <inttypes.h>
+# include <stdlib.h>
+# include <stdio.h>
+# include <string.h>
+# include <wchar.h>
+
+/* We define a bunch of inline functions that have the only
+ purpose of providing conversions to the their arguments and
+ then to pass them on to the corresponding standard function. We
+ use the same names as the standard function and append "_const"
+ to them. This use is covered by the standard since all of the
+ "str" and "wcs" names are reserved, anyhow. */
+
+/**
+ ** @def _TG_INLINE
+ **
+ ** We would like to have all the stubs as `inline` functions. The
+ ** problem is, that if we do so, and the compiler decides not to
+ ** inline the function (because of command line arguments,
+ ** whatever), the symbol would be missing and the program would
+ ** not link.
+ **
+ ** The alternatives would be
+ **
+ ** - `static inline`, but then the functions could not be
+ ** included in other `inline` functions that are themselves not
+ ** `static`.
+ **
+ ** - `inline` and provide all stubs in the C library.
+ **
+ ** - compiler magic similar to the gcc attribute as shown below
+ **
+ ** If you have such specific magic, I'd be happy to integrate
+ ** your two lines, here. If you don't, you'd have to compile
+ ** this file with something equivalent to
+ ** @code
+ ** cc -D_TG_INLINE= -x c -o tgstring.o tgstring.h
+ ** @endcode
+ ** to produce an object file `tgstring.o` that you add to your C
+ ** library.
+ **/
+#ifndef _TG_INLINE
+# if __GNUC__
+# define _TG_INLINE __attribute__((always_inline)) inline
+/* Fall back to inline, if none of that worked */
+# else
+# define _TG_INLINE inline
+# endif
+#endif
+
+// void *memchr(void const *s, int c, size_t n);
+
+_TG_INLINE
+void const*memchr_const(void const* _s,
+ int _c, size_t _n) {
+ return memchr(_s, _c, _n);
+}
+
+# undef memchr
+/**
+ ** Because `wchar_t` is only a `typedef`, we can't map to the
+ ** wide character version. Since the original C library interface
+ ** has a `void const*` parameter, all types must be valid.
+ **
+ ** This uses black magic to decide if the pointer target of the
+ ** first argument is `const`-qualified. Namely a conditional
+ ** expression is used that aggregates type information from @a _0
+ ** and a `void*`. The resulting type then is a pointer to `void`
+ ** with the collection of the qualifications of the two. The
+ ** alternative part of the conditional expression must not be
+ ** `(void*)0`, because that would be a null pointer constant and
+ ** so the resulting type would be the original type of `_0`.
+ **/
+# define memchr(_0, _1, _2) \
+_Generic(1 ? (_0) : (void*)1, \
+ void*: memchr, \
+ void const*: memchr_const) \
+((_0), (_1), (_2))
+
+
+/**
+ ** @brief An internal macro to select a const conserving string
+ ** function.
+ **
+ ** This macro simply selects the function that corresponds to the
+ ** type of argument @a X. It is not meant to be used directly but
+ ** to be integrated in specific macros that overload C library
+ ** functions.
+ **
+ ** The argument @a X must correspond to an array or pointer,
+ ** otherwise an error is triggered in the controlling expression.
+ **
+ ** If any of the 4 cases makes no sense for the type generic
+ ** interface in question, that case can be switched off by
+ ** passing `nullptr` instead of a function. A use of that case in user
+ ** code then results in a compile time error.
+ **
+ ** @see strchr on how to integrate this into a type generic user
+ ** interface
+ **/
+# define _TG_TO_CONST(SN, SC, WN, WC, X, ...) \
+_Generic(&(X[0]), \
+ char*: SN, \
+ char const*: SC, \
+ wchar_t*: WN, \
+ wchar_t const*: WC \
+ )((X), __VA_ARGS__)
+
+_TG_INLINE
+wchar_t const*wmemchr_const(wchar_t const _s[static 1],
+ wchar_t _c, size_t _n) {
+ return wmemchr(_s, _c, _n);
+}
+
+
+# undef wmemchr
+/**
+ ** This function only makes sense if it receives a `wchar_t`
+ ** argument.
+ **/
+# define wmemchr(...) \
+ _TG_TO_CONST(nullptr, /* char*: error */ \
+ nullptr, /* const char*:error */ \
+ wmemchr, /* wchar_t* */ \
+ wmemchr_const, /* const wchar_t* */ \
+ __VA_ARGS__)
+
+// char *strchr(char const *s, int c);
+
+_TG_INLINE
+char const*strchr_const(char const *_s, int _c) {
+ return strchr(_s, _c);
+}
+
+_TG_INLINE
+wchar_t const*wcschr_const(wchar_t const *_s, wchar_t _c) {
+ return wcschr(_s, _c);
+}
+
+# undef strchr
+# define strchr(...) \
+ _TG_TO_CONST(strchr, strchr_const, wcschr, wcschr_const, \
+ __VA_ARGS__)
+
+// char *strpbrk(char const *_s, char const *_t);
+
+_TG_INLINE
+char const*strpbrk_const(char const *_s, char const *_t) {
+ return strpbrk(_s, _t);
+}
+
+_TG_INLINE
+wchar_t const*wcspbrk_const(wchar_t const *_s, wchar_t const *_t) {
+ return wcspbrk(_s, _t);
+}
+
+# undef strpbrk
+# define strpbrk(...) \
+ _TG_TO_CONST(strpbrk, strpbrk_const, wcspbrk, wcspbrk_const, \
+ __VA_ARGS__)
+
+// char *strrchr(char const *s, int c);
+
+_TG_INLINE
+char const* strrchr_const(char const *_s, int _c) {
+ return strrchr(_s, _c);
+}
+
+_TG_INLINE
+wchar_t const* wcsrchr_const(wchar_t const *_s, wchar_t _c) {
+ return wcsrchr(_s, _c);
+}
+
+# undef strrchr
+# define strrchr(...) \
+ _TG_TO_CONST(strrchr, strrchr_const, wcsrchr, wcsrchr_const, \
+ __VA_ARGS__)
+
+
+// char *strstr(char const *_s, const char *_t);
+
+_TG_INLINE
+char const*strstr_const(char const *_s, char const *_t) {
+ return strstr(_s, _t);
+}
+
+_TG_INLINE
+wchar_t const*wcsstr_const(wchar_t const *_s, wchar_t const *_t) {
+ return wcsstr(_s, _t);
+}
+
+# undef strstr
+# define strstr(...) \
+ _TG_TO_CONST(strstr, /* char* */ \
+ strstr_const, /* const char* */ \
+ wcsstr, /* wchar_t* */ \
+ wcsstr_const, /* const wchar_t* */ \
+ __VA_ARGS__)
+
+/**
+ ** @brief An internal macro to select a string function according
+ ** to the base type `char` or `wchar_t`.
+ **
+ ** This macro simply adds the "wcs" prefix to the name of the
+ ** function, if a `wchar_t` function is to be called. Traditional
+ ** `char` strings are mapped to `char`.
+ **
+ ** There is no default case, because these functions need a
+ ** string and not some arbitrary bytes.
+ **
+ ** The argument @a X must correspond to an array or pointer,
+ ** otherwise an error is triggered in the controlling expression.
+ **
+ ** No precaution is made concerning `const`. If a `const`
+ ** qualified pointer type is passed in and the function that is
+ ** selected does not accept such an argument, an error is
+ ** diagnosed as if the corresponding function would have been
+ ** used directly.
+ **
+ ** @see strcpy on how to integrate this into a type generic user
+ ** interface
+ **/
+# define _TG_STR(NAME, X, ...) \
+_Generic(&(X[0]), \
+ char*: str ## NAME, \
+ char const*: str ## NAME, \
+ wchar_t*: wcs ## NAME, \
+ wchar_t const*: wcs ## NAME \
+ )((X), __VA_ARGS__)
+
+# undef strcpy
+# define strcpy(...) _TG_STR(cpy, __VA_ARGS__)
+# undef strncpy
+# define strncpy(...) _TG_STR(ncpy, __VA_ARGS__)
+# undef strcat
+# define strcat(...) _TG_STR(cat, __VA_ARGS__)
+# undef strncat
+# define strncat(...) _TG_STR(ncat, __VA_ARGS__)
+# undef strcmp
+# define strcmp(...) _TG_STR(cmp, __VA_ARGS__)
+# undef strcoll
+# define strcoll(...) _TG_STR(coll, __VA_ARGS__)
+# undef strncmp
+# define strncmp(...) _TG_STR(ncmp, __VA_ARGS__)
+# undef strxfrm
+# define strxfrm(...) _TG_STR(xfrm, __VA_ARGS__)
+# undef strcspn
+# define strcspn(...) _TG_STR(cspn, __VA_ARGS__)
+# undef strspn
+# define strspn(...) _TG_STR(spn, __VA_ARGS__)
+# undef strntok
+# define strntok(...) _TG_STR(ntok, __VA_ARGS__)
+# undef strlen
+# define strlen(...) _TG_STR(len, __VA_ARGS__)
+
+
+/**
+ ** @brief An internal Xmacro to generate const conserving string
+ ** to floating point conversion function.
+ **
+ ** In the case of the string to number functions the original C
+ ** library functions cannot be used directly, because their second
+ ** argument is an unqualified pointer to pointer. The return type of
+ ** these functions is deduced from the name of the function by using
+ ** `typeof`.
+ **
+ ** @param C is the base type of the string, so `char` or `wchar_t`
+ ** @param NAME is the base name for the function
+ **/
+#define _TG_STR2F_FUNC_(C, NAME) \
+_TG_INLINE \
+typeof(NAME((C[1]){ 0 }, nullptr)) _TG_ ## NAME ## _c(C const* _p, C const** _e) { \
+ return NAME(_p, (C**)_e); \
+} \
+_TG_INLINE \
+typeof(NAME((C[1]){ 0 }, nullptr)) _TG_ ## NAME ## _nc(C* _p, C** _e) { \
+ return NAME(_p, _e); \
+} \
+typedef typeof(NAME((C[1]){ 0 }, nullptr)) _TG_ ## NAME ## _c_ftype(C const*, C const**); \
+typedef typeof(NAME((C[1]){ 0 }, nullptr)) _TG_ ## NAME ## _nc_ftype(C*, C**)
+
+/**
+ ** @brief An internal Xmacro to generate const conserving string
+ ** to floating point conversion function.
+ **
+ ** This macro just assembles _TG_STR2F_FUNC_() for the two cases
+ ** of @a C being `char` or `wchar_t`.
+ **
+ ** @param T is the target type of the conversion
+ ** @param NAME is the base name for the function
+ **/
+#define _TG_STR2F_FUNC(NAME) \
+ _TG_STR2F_FUNC_(char, str ## NAME); \
+ _TG_STR2F_FUNC_(wchar_t, wcs ## NAME)
+
+
+/* generate the three groups of floating point conversion functions */
+_TG_STR2F_FUNC(tof);
+_TG_STR2F_FUNC(tod);
+_TG_STR2F_FUNC(told);
+
+
+
+/**
+ ** @brief An internal Xmacro to generate const conserving string
+ ** to integer conversion function.
+ **/
+#define _TG_STR2I_FUNC_(C, NAME) \
+_TG_INLINE \
+typeof(NAME((C[1]){ 0 }, nullptr, 0)) _TG_ ## NAME ## _c(C const* _p, C const** _e, int _b) { \
+ return NAME(_p, (C**)_e, _b); \
+} \
+_TG_INLINE \
+typeof(NAME((C[1]){ 0 }, nullptr, 0)) _TG_ ## NAME ## _nc(C* _p, C** _e, int _b) { \
+ return NAME(_p, _e, _b); \
+} \
+typedef typeof(NAME((C[1]){ 0 }, nullptr, 0)) _TG_ ## NAME ## _c_ftype(C const*, C const**, int); \
+typedef typeof(NAME((C[1]){ 0 }, nullptr, 0)) _TG_ ## NAME ## _nc_ftype(C*, C**, int)
+
+
+/**
+ ** @brief An internal Xmacro to generate const conserving string
+ ** to integer conversion function.
+ **
+ ** This macro just assembles _TG_STR2I_FUNC_() for the two case of
+ ** `being` `char` or `wchar_t`. The return type of these functions is
+ ** deduced from the name of the function by using `typeof`.
+ **
+ ** @param NAME is the base name for the function
+ **/
+#define _TG_STR2I_FUNC(NAME) \
+ _TG_STR2I_FUNC_(char, str ## NAME); \
+ _TG_STR2I_FUNC_(wchar_t, wcs ## NAME)
+
+
+/* generate the six groups of integer conversion functions */
+_TG_STR2I_FUNC(tol);
+_TG_STR2I_FUNC(toll);
+_TG_STR2I_FUNC(toul);
+_TG_STR2I_FUNC(toull);
+_TG_STR2I_FUNC(toimax);
+_TG_STR2I_FUNC(toumax);
+
+/**
+ ** @brief An internal macro to select a const conserving string
+ ** to integer conversion function.
+ **
+ ** This macro simply selects the function that corresponds to
+ ** the type of argument @a X. It is not meant to be used
+ ** directly but to be integrated in specific macros that
+ ** overload C library functions.
+ **
+ ** The argument @a X must correspond to an array or pointer,
+ ** otherwise an error is triggered in the controlling
+ ** expression.
+ **
+ ** There is no default case, because these functions need a
+ ** string and not some arbitrary bytes.
+ **
+ ** @see strtol on how to integrate this into a type generic user
+ ** interface
+ **/
+# define _TG_STR_FUNC3(NAME, X, P, B, ...) \
+_Generic(&((*P)[0]), \
+ char*: _TG_str ## NAME ## _nc, \
+ char const*: _TG_str ## NAME ## _c, \
+ wchar_t*: _TG_wcs ## NAME ## _nc, \
+ wchar_t const*: _TG_wcs ## NAME ## _c \
+ )((X), (P), (B))
+
+/**
+ ** @brief An internal macro to select a const conserving string
+ ** to floating point conversion function.
+ **
+ ** @see _TG_STR_FUNC3 for a complete description of the strategy
+ **/
+# define _TG_STR_FUNC2(NAME, X, P, ...) \
+_Generic(&((*P)[0]), \
+ char*: _TG_str ## NAME ## _nc, \
+ char const*: _TG_str ## NAME ## _c, \
+ wchar_t*: _TG_wcs ## NAME ## _nc, \
+ wchar_t const*: _TG_wcs ## NAME ## _c \
+ )((X), (P))
+
+# undef strtod
+# define strtod(...) _TG_STR_FUNC2(tod, __VA_ARGS__, 0, 0, 0)
+# undef strtof
+# define strtof(...) _TG_STR_FUNC2(tof, __VA_ARGS__, 0, 0, 0)
+# undef strtold
+# define strtold(...) _TG_STR_FUNC2(told, __VA_ARGS__, 0, 0, 0)
+# undef strtol
+# define strtol(...) _TG_STR_FUNC3(tol, __VA_ARGS__, 0, 0, 0)
+# undef strtoll
+# define strtoll(...) _TG_STR_FUNC3(toll, __VA_ARGS__, 0, 0, 0)
+# undef strtoul
+# define strtoul(...) _TG_STR_FUNC3(toul, __VA_ARGS__, 0, 0, 0)
+# undef strtoull
+# define strtoull(...) _TG_STR_FUNC3(toull, __VA_ARGS__, 0, 0, 0)
+# undef strtoimax
+# define strtoimax(...) _TG_STR_FUNC3(toimax, __VA_ARGS__, 0, 0, 0)
+# undef strtoumax
+# define strtoumax(...) _TG_STR_FUNC3(toumax, __VA_ARGS__, 0, 0, 0)
+
+
+#endif
diff --git a/tgstring_test.c b/tgstring_test.c
new file mode 100644
index 0000000..18d8929
--- /dev/null
+++ b/tgstring_test.c
@@ -0,0 +1,53 @@
+#include "tgstring.h"
+#include <stdio.h>
+
+#define putws(S) do { printf("%ls\n", S); } while(0)
+
+int main(void) {
+ char const carray[] = "some characters";
+ // this should produce a diagnostic
+ char * capnc = strchr(carray, 'c');
+ capnc = memchr(carray, 'c', sizeof carray);
+ // this shouldn't produce a diagnostic
+ char const* capc = strchr(carray, 't');
+ capc = memchr(carray, 't', sizeof carray);
+ printf("%s | %s\n", capnc, capc);
+ // this shouldn't produce a diagnostic
+ char cannc[] = "90 first text";
+ char* cnextnc = memchr(cannc, 't', sizeof cannc);
+ printf("reading value %lu, followed by", strtoul(cannc, &cnextnc)); puts(cnextnc);
+ printf("reading value %g, followed by", strtod(cannc, &cnextnc)); puts(cnextnc);
+ // this shouldn't produce a diagnostic
+ char const canc[] = "11 second text";
+ char const* cnextc = memchr(canc, 't', sizeof canc);
+ printf("reading value %jd, followed by", strtoimax(canc, &cnextc)); puts(cnextc);
+ printf("reading value %Lg, followed by", strtold(canc, &cnextc)); puts(cnextc);
+ printf("reading value %lu, followed by", strtoul(cannc, &cnextc)); puts(cnextc);
+ printf("reading value %g, followed by", strtod(cannc, &cnextc)); puts(cnextc);
+ // these two should produce diagnostics
+ printf("reading value %ju, followed by", strtoumax(canc, &cnextnc)); puts(cnextnc);
+ printf("reading value %Lg, followed by", strtold(canc, &cnextnc)); puts(cnextnc);
+ wchar_t const warray[] = L"some wide characters";
+ // this should produce a diagnostic
+ wchar_t * wapnc = strchr(warray, L'c');
+ wapnc = wmemchr(warray, L'c', sizeof warray/sizeof warray[0]);
+ // this shouldn't produce a diagnostic
+ wchar_t const* wapc = strchr(warray, L't');
+ wapc = wmemchr(warray, L't', sizeof warray/sizeof warray[0]);
+ printf("%ls | %ls\n", wapnc, wapc);
+ // this shouldn't produce a diagnostic
+ wchar_t wcnnc[] = L"5 first text";
+ wchar_t* wnextnc = 0;
+ printf("reading value %lu, followed by", wcstoul(wcnnc, &wnextnc, 0)); putws(wnextnc);
+ printf("reading value %g, followed by", wcstod(wcnnc, &wnextnc)); putws(wnextnc);
+ // this shouldn't produce a diagnostic
+ wchar_t const wcnc[] = L"37 second text";
+ wchar_t const* wnextc = nullptr;
+ printf("reading value %jd, followed by", strtoimax(wcnc, &wnextc)); putws(wnextc);
+ printf("reading value %Lg, followed by", strtold(wcnc, &wnextc)); putws(wnextc);
+ printf("reading value %lu, followed by", strtoul(wcnnc, &wnextc)); putws(wnextc);
+ printf("reading value %g, followed by", strtod(wcnnc, &wnextc)); putws(wnextc);
+ // these two should produce diagnostics
+ printf("reading value %ju, followed by", strtoumax(wcnc, &wnextnc)); putws(wnextnc);
+ printf("reading value %Lg, followed by", strtold(wcnc, &wnextnc)); putws(wnextnc);
+}
diff --git a/timespec.c b/timespec.c
new file mode 100644
index 0000000..597e932
--- /dev/null
+++ b/timespec.c
@@ -0,0 +1,118 @@
+#include "c23-fallback.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <time.h>
+#include <tgmath.h>
+
+#include "stats.h"
+
+/**
+ ** @brief compute a time difference
+ **
+ ** This uses a @c double to compute the time. If we want to
+ ** be able to track times without further loss of precision
+ ** and have @c double with 52 bit mantissa, this
+ ** corresponds to a maximal time difference of about 4.5E6
+ ** seconds, or 52 days.
+ **
+ **/
+double timespec_diff(struct timespec const* later,
+ struct timespec const* sooner){
+ /* Be careful: tv_sec could be an unsigned type */
+ if (later->tv_sec < sooner->tv_sec)
+ return -timespec_diff(sooner, later);
+ else
+ return
+ (later->tv_sec - sooner->tv_sec)
+ /* tv_nsec is known to be a signed type. */
+ + (later->tv_nsec - sooner->tv_nsec) * 1E-9;
+}
+
+uint64_t volatile s;
+
+int main(int argc, char* argv[argc+1]) {
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s NNN, NNN iterations\n",
+ argv[0]);
+ return EXIT_FAILURE;
+ }
+ enum { probes = 10, loops = 7, };
+ uint64_t iterations = strtoull(argv[1], nullptr, 0);
+ uint64_t upper = iterations*iterations;
+
+ stats statistic[loops] = { };
+
+ struct timespec tdummy;
+ stats sdummy[4] = { };
+
+ for (unsigned probe = 0; probe < probes; ++probe) {
+ uint64_t accu0 = 0;
+ uint64_t accu1 = 0;
+ struct timespec t[loops+1] = { };
+ timespec_get(&t[0], TIME_UTC);
+ /* Volatile for i ensures that the loop is effected */
+ for (uint64_t volatile i = 0; i < iterations; ++i) {
+ /* do nothing */
+ }
+ timespec_get(&t[1], TIME_UTC);
+ /* s must be volatile to ensure that the loop is effected */
+ for (uint64_t i = 0; i < iterations; ++i) {
+ s = i;
+ }
+ timespec_get(&t[2], TIME_UTC);
+ /* Opaque computation ensures that the loop is effected */
+ for (uint64_t i = 1; accu0 < upper; i += 2) {
+ accu0 += i;
+ }
+ timespec_get(&t[3], TIME_UTC);
+ /* A function call can usually not be optimized out. */
+ for (uint64_t i = 0; i < iterations; ++i) {/*@\label{ln:accumulate}*/
+ timespec_get(&tdummy, TIME_UTC);
+ accu1 += tdummy.tv_nsec;
+ }
+ timespec_get(&t[4], TIME_UTC);
+ /* A function call can usually not be optimized out, but
+ an inline function can. */
+ for (uint64_t i = 0; i < iterations; ++i) {/*@\label{ln:collect1}*/
+ timespec_get(&tdummy, TIME_UTC);
+ stats_collect1(&sdummy[1], tdummy.tv_nsec);
+ }
+ timespec_get(&t[5], TIME_UTC);
+ for (uint64_t i = 0; i < iterations; ++i) {/*@\label{ln:collect2}*/
+ timespec_get(&tdummy, TIME_UTC);
+ stats_collect2(&sdummy[2], tdummy.tv_nsec);
+ }
+ timespec_get(&t[6], TIME_UTC);
+ for (uint64_t i = 0; i < iterations; ++i) {/*@\label{ln:collect3}*/
+ timespec_get(&tdummy, TIME_UTC);
+ stats_collect3(&sdummy[3], tdummy.tv_nsec);
+ }
+ timespec_get(&t[7], TIME_UTC);
+ /* make sure to use values of volatiles after the loop */
+ printf("s, %zu iterations, accu0 %zx, accu1 %zx\n", s, accu0, accu1);
+ for (size_t moment = 1; moment < 4; ++moment) {
+ double mean = stats_mean(&sdummy[moment]);
+ double sdev = stats_rsdev_unbiased(&sdummy[moment]);
+ double skew = stats_skew(&sdummy[moment]);
+ printf("%zu moments: average value of nsec\t%5.4g\t± %g%% (%g skew)\n",
+ moment, mean, 100.0*sdev, skew);
+ }
+
+ for (unsigned i = 0; i < loops; i++) {
+ double diff = timespec_diff(&t[i+1], &t[i]);
+ stats_collect2(&statistic[i], diff);
+ }
+
+ }
+
+ for (unsigned i = 0; i < loops; i++) {
+ double mean = stats_mean(&statistic[i]);
+ double rsdev = stats_rsdev_unbiased(&statistic[i]);
+ printf("loop %u: E(t) (sec):\t%5.2e ± %4.02f%%,\tloop body %5.2e\n",
+ i, mean, 100.0*rsdev, mean/iterations);
+ }
+
+
+ return EXIT_SUCCESS;
+}
diff --git a/va_arg.c b/va_arg.c
new file mode 100644
index 0000000..1b2c0a8
--- /dev/null
+++ b/va_arg.c
@@ -0,0 +1,52 @@
+#include "c23-fallback.h"
+#include <stdarg.h>
+#include <stdio.h>
+//#include <stdlib.h>
+
+/**
+ ** @brief A small, useless function to show how variadic
+ ** functions work
+ **/
+double sumIt(size_t n, ...) {
+ double ret = 0.0;
+ va_list va;
+ va_start(va);
+ for (size_t i = 0; i < n; ++i)
+ ret += va_arg(va, double);
+ va_end(va);
+ return ret;
+}
+
+/**
+ ** @brief A simple debug stream
+ **
+ ** Per convention no output is produced when this is null.
+ **
+ ** This can be set local to the current thread, such that threads may
+ ** print their debug messages to different files or streams.
+ **/
+thread_local FILE* iodebug = nullptr;
+
+/**
+ ** @brief Prints to the debug stream @c iodebug
+ **/
+[[gnu::format(printf, 1, 2)]]
+int printf_debug(const char *format, ...) {
+ int ret = 0;
+ if (iodebug) {
+ va_list va;
+ va_start(va);
+ ret = vfprintf(iodebug, format, va);
+ va_end(va);
+ }
+ return ret;
+}
+
+
+
+int main(int argc, char* argv[argc+1]) {
+ if (argc < 4) return EXIT_FAILURE;
+ iodebug = stderr;
+ printf_debug("%g\n", sumIt(3, strtod(argv[1], nullptr), strtod(argv[2], nullptr), strtod(argv[3], nullptr)));
+ return EXIT_SUCCESS;
+}
diff --git a/volatile_atomic.c b/volatile_atomic.c
new file mode 100644
index 0000000..ecff3c5
--- /dev/null
+++ b/volatile_atomic.c
@@ -0,0 +1,162 @@
+#include <stdatomic.h>
+
+/**
+ ** @brief A simple accumulation in a loop
+ **
+ ** Any decent optimizer should be able to figure out that the return
+ ** value is @c 100 and that the only side effect is to store the
+ ** value @c 10 into @c *count.
+ **/
+unsigned accu(unsigned*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ (*count) = 0;
+ while (tmp < 9) {
+ tmp = (*count)++;
+ ret += 2*tmp +1;
+ }
+ return ret;
+}
+
+/**
+ ** @brief @c volatile qualification
+ **
+ ** @c volatile forces all stores to be effected. So this has side
+ ** effects of storing @f$0, \ldots, 10@f$ into @c *count.
+ **/
+unsigned accuv(unsigned volatile*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ (*count) = 0;
+ while (tmp < 9) {
+ tmp = (*count)++;
+ ret += 2*tmp +1;
+ }
+ return ret;
+}
+
+/**
+ ** @brief @c _Atomic specification
+ **
+ ** Here, because it is not @c volatile, none of the side effects
+ ** would have to be made visible before returning. Only there is an
+ ** ambiguous phrase in the C standard:
+ **
+ ** <quote>
+ ** Atomic read-modify-write operations shall always read the last
+ ** value (in the modification order) stored before the write
+ ** associated with the read-modify-write operation.
+ ** </quote>
+ **
+ ** This can be read that for read-modify-write operations the read
+ ** and write operations *must* be effected.
+ **/
+unsigned accua(_Atomic(unsigned)*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ (*count) = 0;
+ while (tmp < 9) {
+ tmp = (*count)++;
+ ret += 2*tmp +1;
+ }
+ return ret;
+}
+
+unsigned accuav(_Atomic(unsigned) volatile*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ (*count) = 0;
+ while (tmp < 9) {
+ tmp = (*count)++;
+ ret += 2*tmp +1;
+ }
+ return ret;
+}
+
+unsigned accuar(_Atomic(unsigned)*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ atomic_store_explicit(count, 0, memory_order_relaxed);
+ while (tmp < 9) {
+ tmp = atomic_fetch_add_explicit(count, 1u, memory_order_relaxed);
+ ret += 2*tmp +1;
+ }
+ return ret;
+}
+
+unsigned accuavr(_Atomic(unsigned) volatile*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ atomic_store_explicit(count, 0, memory_order_relaxed);
+ while (tmp < 9) {
+ tmp = atomic_fetch_add_explicit(count, 1u, memory_order_relaxed);
+ ret += 2*tmp +1;
+ }
+ return ret;
+}
+
+unsigned accut(unsigned*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ (*count) = 0;
+ while (tmp < 10) {
+ ret += 2*tmp +1;
+ (*count) = ++tmp;
+ }
+ return ret;
+}
+
+unsigned accutv(unsigned volatile*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ (*count) = 0;
+ while (tmp < 10) {
+ ret += 2*tmp +1;
+ (*count) = ++tmp;
+ }
+ return ret;
+}
+
+unsigned accuta(_Atomic(unsigned)*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ (*count) = 0;
+ while (tmp < 10) {
+ ret += 2*tmp +1;
+ (*count) = ++tmp;
+ }
+ return ret;
+}
+
+unsigned accutav(_Atomic(unsigned) volatile*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ (*count) = 0;
+ while (tmp < 10) {
+ ret += 2*tmp +1;
+ (*count) = ++tmp;
+ }
+ return ret;
+}
+
+unsigned accutar(_Atomic(unsigned)*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ atomic_store_explicit(count, 0, memory_order_relaxed);
+ while (tmp < 10) {
+ ret += 2*tmp +1;
+ atomic_store_explicit(count, ++tmp, memory_order_relaxed);
+ }
+ return ret;
+}
+
+unsigned accutavr(_Atomic(unsigned) volatile*restrict count) {
+ unsigned ret = 0;
+ unsigned tmp = 0;
+ atomic_store_explicit(count, 0, memory_order_relaxed);
+ while (tmp < 10) {
+ ret += 2*tmp +1;
+ atomic_store_explicit(count, ++tmp, memory_order_relaxed);
+ }
+ return ret;
+}
diff --git a/yday.c b/yday.c
new file mode 100644
index 0000000..d8b169f
--- /dev/null
+++ b/yday.c
@@ -0,0 +1,41 @@
+#include <time.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+bool leapyear(unsigned year) {
+ /* All years that are divisible by 4 are leap years,
+ unless they start a new century, provided they
+ are not divisible by 400. */
+ return !(year % 4) && ((year % 100) || !(year % 400));
+}
+
+#define DAYS_BEFORE \
+(int const[12]){ \
+ [0] = 0, [1] = 31, [2] = 59, [3] = 90, \
+ [4] = 120, [5] = 151, [6] = 181, [7] = 212, \
+ [8] = 243, [9] = 273, [10] = 304, [11] = 334, \
+}
+
+struct tm time_set_yday(struct tm t) {
+ // tm_mdays starts at 1.
+ t.tm_yday += DAYS_BEFORE[t.tm_mon] + t.tm_mday - 1;
+ // Takes care of leap years
+ if ((t.tm_mon > 1) && leapyear(t.tm_year+1900))
+ ++t.tm_yday;
+ return t;
+}
+
+int main(void) {
+ struct tm today = {
+ .tm_year = 2019-1900,
+ .tm_mon = 4-1,
+ .tm_mday = 3,
+ .tm_hour = 10,
+ .tm_min = 0,
+ .tm_sec = 47,
+ };
+ printf("this year is %d, next year will be %d\n",
+ today.tm_year+1900, today.tm_year+1900+1);
+ today = time_set_yday(today);
+ printf("day of the year is %d\n", today.tm_yday);
+}