/* See LICENSE file for copyright and license details. */
#include "common.h"
static const char *digits[255] = {
['#'] = "#", ['*'] = "*", ['+'] = "00",
['0'] = "0", ['1'] = "1", ['2'] = "2", ['3'] = "3", ['4'] = "4",
['5'] = "5", ['6'] = "6", ['7'] = "7", ['8'] = "8", ['9'] = "9",
['A'] = "2", ['B'] = "2", ['C'] = "2",
['D'] = "3", ['E'] = "3", ['F'] = "3",
['G'] = "4", ['H'] = "4", ['I'] = "4",
['J'] = "5", ['K'] = "5", ['L'] = "5",
['M'] = "6", ['N'] = "6", ['O'] = "6",
['P'] = "7", ['Q'] = "7", ['R'] = "7", ['S'] = "7",
['T'] = "8", ['U'] = "8", ['V'] = "8",
['W'] = "8", ['X'] = "9", ['Y'] = "9", ['Z'] = "9",
['('] = "(", [')'] = ")" /* For skipping ranges, e.g. the 0 in +46(0)7… shall be skipped when +46 is included */
/* Lower case letters are intentionally left out */
};
static char *
canonicalise(const char *number, const char *country, char **post_cccp)
{
size_t nlen = 0, clen = 0, skip;
const char *digit, *p;
char *ret, *r;
*post_cccp = NULL;
for (p = country, skip = 0; *p; p++) {
digit = digits[*p & 255];
if (digit) {
if (*digit == '(')
skip += 1;
else if (*digit == ')')
skip -= !!skip;
else if (!skip)
clen += digit[1] ? 2 : 1;
}
}
for (p = number, skip = 0; *p; p++) {
digit = digits[*p & 255];
if (digit) {
if (*digit == '(')
skip += 1;
else if (*digit == ')')
skip -= !!skip;
else if (!skip)
nlen += digit[1] ? 2 : 1;
}
}
r = ret = malloc(clen + nlen + 3);
if (!ret)
return NULL;
if (clen) {
for (p = country, skip = 0; *p; p++) {
digit = digits[*p & 255];
if (digit) {
if (*digit == '(') {
skip += 1;
} else if (*digit == ')') {
skip -= !!skip;
} else if (!skip) {
*r++ = digit[0];
if (digit[1])
*r++ = digit[1];
}
}
}
if (clen < 2 || ret[0] != '0' || ret[1] != '0') {
memmove(&ret[2], ret, clen);
ret[0] = ret[1] = '0';
clen += 2;
r += 2;
}
*post_cccp = r;
}
for (p = number, skip = 0; *p; p++) {
digit = digits[*p & 255];
if (digit) {
if (*digit == '(') {
skip += 1;
} else if (*digit == ')') {
skip -= !!skip;
} else if (!skip) {
*r++ = digit[0];
if (digit[1])
*r++ = digit[1];
}
}
}
*r = '\0';
if (ret[clen] != '0') {
*post_cccp = NULL;
memmove(ret, &ret[clen], nlen + 1);
} else {
memmove(&ret[clen], &ret[clen + 1], nlen--);
if (nlen >= 2 && ret[clen + 1] == '0') {
if (nlen < clen || memcmp(&ret[0], &ret[clen], clen))
*post_cccp = NULL;
memmove(ret, &ret[clen], nlen + 1);
}
}
if (ret[0] == '0' && ret[1] != '0')
*post_cccp = &ret[1];
return ret;
}
int
libcontacts_same_number(const char *a, const char *a_country, const char *b, const char *b_country)
{
char *a_full, *b_full, *a_post_ccc, *b_post_ccc;
int ret, a_has_ccc, b_has_ccc;
if (!a || !*a || !b || !*b) {
errno = EINVAL;
return -1;
}
if (!a_country)
a_country = "";
if (!b_country)
b_country = "";
a_full = canonicalise(a, a_country, &a_post_ccc);
if (!a_full)
return -1;
b_full = canonicalise(b, b_country, &b_post_ccc);
if (!b_full) {
free(a_full);
return -1;
}
a_has_ccc = (a_full[0] == '0' && a_full[1] == '0');
b_has_ccc = (b_full[0] == '0' && b_full[1] == '0');
a = a_full;
b = b_full;
if (a_has_ccc != b_has_ccc) {
if (a_post_ccc && b_post_ccc) {
a = a_post_ccc;
b = b_post_ccc;
}
}
ret = !strcmp(a, b);
free(a_full);
free(b_full);
return ret;
}