aboutsummaryrefslogblamecommitdiffstats
path: root/libcontacts_same_number.c
blob: 25e19e0aa44ffa16c1115952bdfcc1ed7185b587 (plain) (tree)




















                                                                                                                          
                                                                       




                                        

                          
                                              





                                               

                                                         


                 
                                             





                                               

                                                         


                 
                                          


                            












                                                                

                         






                                                                 
         
 
                                             












                                                        

                  
                               









                                                                             
 

                                           
 





                                                                                                   

                                                        





                                     





                                                         

                          
                                                         



                             













                                                           



                     
/* 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;
}