aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2026-01-25 15:39:50 +0100
committerMattias Andrée <m@maandree.se>2026-01-25 15:39:50 +0100
commit8a72c20e94a6c74b3e2d4580adacbdf75f952146 (patch)
tree7c3b8f8c541d77ad54c6a4c3f3303befe75589a6
parentAdd overlaid (diff)
downloadcharconv-8a72c20e94a6c74b3e2d4580adacbdf75f952146.tar.gz
charconv-8a72c20e94a6c74b3e2d4580adacbdf75f952146.tar.bz2
charconv-8a72c20e94a6c74b3e2d4580adacbdf75f952146.tar.xz
Add joined
Signed-off-by: Mattias Andrée <m@maandree.se>
-rw-r--r--Makefile6
-rw-r--r--convert-to-joined.c4
-rw-r--r--libcharconv.h5
-rw-r--r--libcharconv_joined.c68
-rw-r--r--libcharconv_latin.c6
5 files changed, 87 insertions, 2 deletions
diff --git a/Makefile b/Makefile
index f09b1a3..a7c7621 100644
--- a/Makefile
+++ b/Makefile
@@ -62,7 +62,8 @@ BIN =\
convert-to-yijing-hexagrams\
convert-to-vulgar-fractions\
convert-to-flipped\
- convert-to-overlaid
+ convert-to-overlaid\
+ convert-to-joined
LIBOBJ =\
libcharconv_decode_utf8_.o\
@@ -113,7 +114,8 @@ LIBOBJ =\
libcharconv_yijing_hexagrams.o\
libcharconv_vulgar_fractions.o\
libcharconv_flipped.o\
- libcharconv_overlaid.o
+ libcharconv_overlaid.o\
+ libcharconv_joined.o
LOBJ = $(LIBOBJ:.o=.lo)
diff --git a/convert-to-joined.c b/convert-to-joined.c
new file mode 100644
index 0000000..71295bf
--- /dev/null
+++ b/convert-to-joined.c
@@ -0,0 +1,4 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+SIMPLE(libcharconv_joined)
diff --git a/libcharconv.h b/libcharconv.h
index 289f9e8..731c7ce 100644
--- a/libcharconv.h
+++ b/libcharconv.h
@@ -338,6 +338,11 @@ LIBCHARCONV_FUNC_(libcharconv_flipped);
*/
LIBCHARCONV_FUNC_(libcharconv_overlaid);
+/**
+ * Join characters horizontally
+ */
+LIBCHARCONV_FUNC_(libcharconv_joined);
+
#undef LIBCHARCONV_FUNC_
#endif
diff --git a/libcharconv_joined.c b/libcharconv_joined.c
new file mode 100644
index 0000000..72377a2
--- /dev/null
+++ b/libcharconv_joined.c
@@ -0,0 +1,68 @@
+/* See LICENSE file for copyright and license details. */
+#include "lib-common.h"
+
+
+static struct {
+ uint_least32_t a;
+ uint_least32_t b;
+ uint_least32_t to;
+} pairs[] = {
+ {(uint_least32_t)'?', (uint_least32_t)'!', 0x2048},
+ {(uint_least32_t)'?', (uint_least32_t)'?', 0x2047},
+ {(uint_least32_t)'!', (uint_least32_t)'!', 0x203C},
+ {(uint_least32_t)'!', (uint_least32_t)'?', 0x2049}
+};
+
+
+enum libcharconv_result
+libcharconv_joined(const char *s, size_t slen, size_t *n, uint_least32_t *cp, size_t *ncp)
+{
+ uint_least32_t a, b;
+ size_t i, alen, blen;
+ *n = 0;
+ while (slen) {
+ alen = libcharconv_decode_utf8_(s, slen, &a);
+ if (alen > slen) {
+ if (*n)
+ goto no_conv;
+ return LIBCHARCONV_INDETERMINATE;
+ }
+ if (!alen) {
+ *n += 1u;
+ slen -= 1u;
+ s = &s[1];
+ continue;
+ }
+
+ for (i = 0u; i < sizeof(pairs) / sizeof(*pairs); i++) {
+ if (a != pairs[i].a)
+ continue;
+ if (*n)
+ goto no_conv;
+ if (slen == alen)
+ return LIBCHARCONV_INDETERMINATE;
+ blen = libcharconv_decode_utf8_(&s[alen], slen - alen, &b);
+ if (blen > slen)
+ return LIBCHARCONV_INDETERMINATE;
+ if (!blen)
+ goto no_conv;
+ if (b == pairs[i].b)
+ goto conv;
+ }
+
+ *n += alen;
+ s = &s[alen];
+ slen -= alen;
+ }
+no_conv:
+ return LIBCHARCONV_NO_CONVERT;
+
+conv:
+ if (*n)
+ goto no_conv;
+ if (*ncp)
+ *cp = pairs[i].to;
+ *n += alen + blen;
+ *ncp = 1u;
+ return LIBCHARCONV_CONVERTED;
+}
diff --git a/libcharconv_latin.c b/libcharconv_latin.c
index ee975e1..a4b9059 100644
--- a/libcharconv_latin.c
+++ b/libcharconv_latin.c
@@ -630,6 +630,12 @@ libcharconv_latin(const char *s, size_t slen, size_t *n, uint_least32_t *cp, siz
case UINT32_C(0x203D): c1 = '!'; c2 = '?'; goto conv2;
case UINT32_C(0x2E18): cp1 = UINT32_C(0xBF); cp2 = UINT32_C(0xA1); goto conv2cp;
+ /* joined */
+ case UINT32_C(0x2048): c1 = '?'; c2 = '!'; goto conv2;
+ case UINT32_C(0x2047): c1 = '?'; c2 = '?'; goto conv2;
+ case UINT32_C(0x203C): c1 = '!'; c2 = '!'; goto conv2;
+ case UINT32_C(0x2049): c1 = '!'; c2 = '?'; goto conv2;
+
default:
no_match:
*n += clen;