#include "pms_util.h" #include #include 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; }