diff options
| author | Miguel Dalberto Pedro <miguel.pedro@precisioninno.com> | 2026-10-29 17:55:36 -0300 |
|---|---|---|
| committer | Miguel Dalberto Pedro <miguel.pedro@precisioninno.com> | 2026-10-29 17:55:36 -0300 |
| commit | 915478aef931cb57fe56fe568ab4982454a29812 (patch) | |
| tree | 33057f4773852d99e8cd7ad92d568e79f34847ee /src/pms_util.c | |
| download | pms-915478aef931cb57fe56fe568ab4982454a29812.tar.gz pms-915478aef931cb57fe56fe568ab4982454a29812.zip | |
Diffstat (limited to 'src/pms_util.c')
| -rw-r--r-- | src/pms_util.c | 89 |
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; +} |
