aboutsummaryrefslogtreecommitdiff
path: root/src/pms_util.c
blob: 635fa3c5dc93ba91b358af579b4c71ca9e0300ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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;
}