aboutsummaryrefslogtreecommitdiff
path: root/src/pms_util.c
diff options
context:
space:
mode:
authorMiguel Dalberto Pedro <miguel.pedro@precisioninno.com>2026-10-29 17:55:36 -0300
committerMiguel Dalberto Pedro <miguel.pedro@precisioninno.com>2026-10-29 17:55:36 -0300
commit915478aef931cb57fe56fe568ab4982454a29812 (patch)
tree33057f4773852d99e8cd7ad92d568e79f34847ee /src/pms_util.c
downloadpms-master.tar.gz
pms-master.zip
Initial commit: Padre Matos Soares (1932) Bible corpus and pms CLIHEADmaster
Diffstat (limited to 'src/pms_util.c')
-rw-r--r--src/pms_util.c89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/pms_util.c b/src/pms_util.c
new file mode 100644
index 0000000..635fa3c
--- /dev/null
+++ b/src/pms_util.c
@@ -0,0 +1,89 @@
+#include "pms_util.h"
+#include <ctype.h>
+#include <string.h>
+
+static char fold_latin1_c3(unsigned char b2) {
+ if ((b2 >= 0x80 && b2 <= 0x85) || (b2 >= 0xA0 && b2 <= 0xA5)) return 'a';
+ if (b2 == 0x87 || b2 == 0xA7) return 'c';
+ if ((b2 >= 0x88 && b2 <= 0x8B) || (b2 >= 0xA8 && b2 <= 0xAB)) return 'e';
+ if ((b2 >= 0x8C && b2 <= 0x8F) || (b2 >= 0xAC && b2 <= 0xAF)) return 'i';
+ if (b2 == 0x91 || b2 == 0xB1) return 'n';
+ if ((b2 >= 0x92 && b2 <= 0x96) || (b2 >= 0xB2 && b2 <= 0xB6)) return 'o';
+ if ((b2 >= 0x99 && b2 <= 0x9C) || (b2 >= 0xB9 && b2 <= 0xBC)) return 'u';
+ if (b2 == 0x9D || b2 == 0xBD || b2 == 0xBF) return 'y';
+ return 0;
+}
+
+size_t pms_fold_utf8(const char *src, char *dst, size_t dst_size, int *byte_map) {
+ if (!src || !dst || dst_size == 0) {
+ return 0;
+ }
+ size_t i = 0;
+ size_t out = 0;
+ while (src[i] != '\0' && out + 1 < dst_size) {
+ unsigned char c = (unsigned char)src[i];
+ if (c < 0x80) {
+ if (byte_map) byte_map[out] = (int)i;
+ dst[out++] = (char)tolower(c);
+ i += 1;
+ } else if (c == 0xC3 && src[i + 1] != '\0') {
+ unsigned char b2 = (unsigned char)src[i + 1];
+ char folded = fold_latin1_c3(b2);
+ if (byte_map) byte_map[out] = (int)i;
+ dst[out++] = folded ? folded : '?';
+ i += 2;
+ } else if ((c & 0xE0) == 0xC0 && src[i + 1] != '\0') {
+ if (byte_map) byte_map[out] = (int)i;
+ dst[out++] = ' ';
+ i += 2;
+ } else if ((c & 0xF0) == 0xE0 && src[i + 1] != '\0' && src[i + 2] != '\0') {
+ /* Handle curly apostrophes U+2018 / U+2019 (E2 80 98 / E2 80 99) and dashes */
+ unsigned char b2 = (unsigned char)src[i + 1];
+ unsigned char b3 = (unsigned char)src[i + 2];
+ char repl = ' ';
+ if (c == 0xE2 && b2 == 0x80 && (b3 == 0x98 || b3 == 0x99)) {
+ repl = '\'';
+ } else if (c == 0xE2 && b2 == 0x80 && (b3 == 0x93 || b3 == 0x94)) {
+ repl = '-';
+ }
+ if (byte_map) byte_map[out] = (int)i;
+ dst[out++] = repl;
+ i += 3;
+ } else if ((c & 0xF8) == 0xF0 && src[i + 1] != '\0' && src[i + 2] != '\0' && src[i + 3] != '\0') {
+ if (byte_map) byte_map[out] = (int)i;
+ dst[out++] = ' ';
+ i += 4;
+ } else {
+ i += 1;
+ }
+ }
+ if (byte_map) {
+ byte_map[out] = (int)strlen(src);
+ }
+ dst[out] = '\0';
+ return out;
+}
+
+size_t pms_normalize_book_key(const char *src, char *dst, size_t dst_size) {
+ char folded[256];
+ pms_fold_utf8(src, folded, sizeof(folded), NULL);
+ size_t out = 0;
+ for (size_t i = 0; folded[i] != '\0' && out + 1 < dst_size; i++) {
+ unsigned char c = (unsigned char)folded[i];
+ if (isalnum(c)) {
+ dst[out++] = (char)c;
+ }
+ }
+ dst[out] = '\0';
+ return out;
+}
+
+size_t pms_utf8_char_len(const char *s, size_t byte_len) {
+ size_t count = 0;
+ for (size_t i = 0; i < byte_len && s[i] != '\0'; i++) {
+ if (((unsigned char)s[i] & 0xC0) != 0x80) {
+ count++;
+ }
+ }
+ return count;
+}