← Back to davo.co
summaryrefslogtreecommitdiffstats
path: root/mbstrings.c
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 /mbstrings.c
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
Diffstat (limited to 'mbstrings.c')
-rw-r--r--mbstrings.c346
1 files changed, 346 insertions, 0 deletions
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;
+}