You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
486 lines
16 KiB
486 lines
16 KiB
/*
|
|
* Copyright 2009, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
namespace android {
|
|
|
|
/* Generated by the following Python script. Values of country calling codes
|
|
are from http://en.wikipedia.org/wiki/List_of_country_calling_codes
|
|
|
|
#!/usr/bin/python
|
|
import sys
|
|
ccc_set_2digits = set([0, 1, 7,
|
|
20, 27, 28, 30, 31, 32, 33, 34, 36, 39, 40, 43, 44, 45,
|
|
46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 58, 60, 61,
|
|
62, 63, 64, 65, 66, 81, 82, 83, 84, 86, 89, 90, 91, 92,
|
|
93, 94, 95, 98])
|
|
|
|
ONE_LINE_NUM = 10
|
|
|
|
for i in xrange(100):
|
|
if i % ONE_LINE_NUM == 0:
|
|
sys.stdout.write(' ')
|
|
if i in ccc_set_2digits:
|
|
included = 'true'
|
|
else:
|
|
included = 'false'
|
|
sys.stdout.write(included + ',')
|
|
if ((i + 1) % ONE_LINE_NUM) == 0:
|
|
sys.stdout.write('\n')
|
|
else:
|
|
sys.stdout.write(' ')
|
|
*/
|
|
static bool two_length_country_code_map[100] = {
|
|
true, true, false, false, false, false, false, true, false, false,
|
|
false, false, false, false, false, false, false, false, false, false,
|
|
true, false, false, false, false, false, false, true, true, false,
|
|
true, true, true, true, true, false, true, false, false, true,
|
|
true, false, false, true, true, true, true, true, true, true,
|
|
false, true, true, true, true, true, true, true, true, false,
|
|
true, true, true, true, true, true, true, false, false, false,
|
|
false, false, false, false, false, false, false, false, false, false,
|
|
false, true, true, true, true, false, true, false, false, true,
|
|
true, true, true, true, true, true, false, false, true, false,
|
|
};
|
|
|
|
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
|
|
|
|
/**
|
|
* Returns true if "ccc_candidate" expresses (part of ) some country calling
|
|
* code.
|
|
* Returns false otherwise.
|
|
*/
|
|
static bool isCountryCallingCode(int ccc_candidate) {
|
|
return ccc_candidate > 0 &&
|
|
ccc_candidate < (int)ARRAY_SIZE(two_length_country_code_map) &&
|
|
two_length_country_code_map[ccc_candidate];
|
|
}
|
|
|
|
/**
|
|
* Returns interger corresponding to the input if input "ch" is
|
|
* ISO-LATIN characters 0-9.
|
|
* Returns -1 otherwise
|
|
*/
|
|
static int tryGetISODigit (char ch)
|
|
{
|
|
if ('0' <= ch && ch <= '9') {
|
|
return ch - '0';
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* True if ch is ISO-LATIN characters 0-9, *, # , +
|
|
* Note this method current does not account for the WILD char 'N'
|
|
*/
|
|
static bool isDialable(char ch)
|
|
{
|
|
return ('0' <= ch && ch <= '9') || ch == '*' || ch == '#' || ch == '+';
|
|
}
|
|
|
|
/** Returns true if ch is not dialable or alpha char */
|
|
static bool isSeparator(char ch)
|
|
{
|
|
return !isDialable(ch) && (isalpha(ch) == 0);
|
|
}
|
|
|
|
/**
|
|
* Try to store the pointer to "new_ptr" which does not have trunk prefix.
|
|
*
|
|
* Currently this function simply ignore the first digit assuming it is
|
|
* trunk prefix. Actually trunk prefix is different in each country.
|
|
*
|
|
* e.g.
|
|
* "+79161234567" equals "89161234567" (Russian trunk digit is 8)
|
|
* "+33123456789" equals "0123456789" (French trunk digit is 0)
|
|
*
|
|
*/
|
|
static bool tryGetTrunkPrefixOmittedStr(const char *str, size_t len,
|
|
const char **new_ptr, size_t *new_len)
|
|
{
|
|
for (size_t i = 0 ; i < len ; i++) {
|
|
char ch = str[i];
|
|
if (tryGetISODigit(ch) >= 0) {
|
|
if (new_ptr != NULL) {
|
|
*new_ptr = str + i + 1;
|
|
}
|
|
if (new_len != NULL) {
|
|
*new_len = len - (i + 1);
|
|
}
|
|
return true;
|
|
} else if (isDialable(ch)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Note that this function does not strictly care the country calling code with
|
|
* 3 length (like Morocco: +212), assuming it is enough to use the first two
|
|
* digit to compare two phone numbers.
|
|
*/
|
|
static int tryGetCountryCallingCode(const char *str, size_t len,
|
|
const char **new_ptr, size_t *new_len,
|
|
bool accept_thailand_case)
|
|
{
|
|
// Rough regexp:
|
|
// ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
|
|
// 0 1 2 3 45 6 7 89
|
|
//
|
|
// In all the states, this function ignores separator characters.
|
|
// "166" is the special case for the call from Thailand to the US. Ugu!
|
|
|
|
int state = 0;
|
|
int ccc = 0;
|
|
for (size_t i = 0 ; i < len ; i++ ) {
|
|
char ch = str[i];
|
|
switch (state) {
|
|
case 0:
|
|
if (ch == '+') state = 1;
|
|
else if (ch == '0') state = 2;
|
|
else if (ch == '1') {
|
|
if (accept_thailand_case) {
|
|
state = 8;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else if (isDialable(ch)) return -1;
|
|
break;
|
|
|
|
case 2:
|
|
if (ch == '0') state = 3;
|
|
else if (ch == '1') state = 4;
|
|
else if (isDialable(ch)) return -1;
|
|
break;
|
|
|
|
case 4:
|
|
if (ch == '1') state = 5;
|
|
else if (isDialable(ch)) return -1;
|
|
break;
|
|
|
|
case 1:
|
|
case 3:
|
|
case 5:
|
|
case 6:
|
|
case 7:
|
|
{
|
|
int ret = tryGetISODigit(ch);
|
|
if (ret > 0) {
|
|
ccc = ccc * 10 + ret;
|
|
if (ccc >= 100 || isCountryCallingCode(ccc)) {
|
|
if (new_ptr != NULL) {
|
|
*new_ptr = str + i + 1;
|
|
}
|
|
if (new_len != NULL) {
|
|
*new_len = len - (i + 1);
|
|
}
|
|
return ccc;
|
|
}
|
|
if (state == 1 || state == 3 || state == 5) {
|
|
state = 6;
|
|
} else {
|
|
state++;
|
|
}
|
|
} else if (isDialable(ch)) {
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
case 8:
|
|
if (ch == '6') state = 9;
|
|
else if (isDialable(ch)) return -1;
|
|
break;
|
|
case 9:
|
|
if (ch == '6') {
|
|
if (new_ptr != NULL) {
|
|
*new_ptr = str + i + 1;
|
|
}
|
|
if (new_len != NULL) {
|
|
*new_len = len - (i + 1);
|
|
}
|
|
return 66;
|
|
} else {
|
|
return -1;
|
|
}
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Return true if the prefix of "ch" is "ignorable". Here, "ignorable" means
|
|
* that "ch" has only one digit and separator characters. The one digit is
|
|
* assumed to be the trunk prefix.
|
|
*/
|
|
static bool checkPrefixIsIgnorable(const char* ch, int i) {
|
|
bool trunk_prefix_was_read = false;
|
|
while (i >= 0) {
|
|
if (tryGetISODigit(ch[i]) >= 0) {
|
|
if (trunk_prefix_was_read) {
|
|
// More than one digit appeared, meaning that "a" and "b"
|
|
// is different.
|
|
return false;
|
|
} else {
|
|
// Ignore just one digit, assuming it is trunk prefix.
|
|
trunk_prefix_was_read = true;
|
|
}
|
|
} else if (isDialable(ch[i])) {
|
|
// Trunk prefix is a digit, not "*", "#"...
|
|
return false;
|
|
}
|
|
i--;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Compare phone numbers a and b, return true if they're identical
|
|
* enough for caller ID purposes.
|
|
*
|
|
* Assume NULL as 0-length string.
|
|
*
|
|
* Detailed information:
|
|
* Currently (as of 2009-06-12), we cannot depend on the locale given from the
|
|
* OS. For example, current Android does not accept "en_JP", meaning
|
|
* "the display language is English but the phone should be in Japan", but
|
|
* en_US, es_US, etc. So we cannot identify which digit is valid trunk prefix
|
|
* in the country where the phone is used. More specifically, "880-1234-1234"
|
|
* is not valid phone number in Japan since the trunk prefix in Japan is not 8
|
|
* but 0 (correct number should be "080-1234-1234"), while Russian trunk prefix
|
|
* is 8. Also, we cannot know whether the country where users live has trunk
|
|
* prefix itself. So, we cannot determine whether "+81-80-1234-1234" is NOT
|
|
* same as "880-1234-1234" (while "+81-80-1234-1234" is same as "080-1234-1234"
|
|
* and we can determine "880-1234-1234" is different from "080-1234-1234").
|
|
*
|
|
* In the future, we should handle trunk prefix more correctly, but as of now,
|
|
* we just ignore it...
|
|
*/
|
|
static bool phone_number_compare_inter(const char* const org_a, const char* const org_b,
|
|
bool accept_thailand_case)
|
|
{
|
|
const char* a = org_a;
|
|
const char* b = org_b;
|
|
size_t len_a = 0;
|
|
size_t len_b = 0;
|
|
if (a == NULL) {
|
|
a = "";
|
|
} else {
|
|
len_a = strlen(a);
|
|
}
|
|
if (b == NULL) {
|
|
b = "";
|
|
} else {
|
|
len_b = strlen(b);
|
|
}
|
|
|
|
const char* tmp_a = NULL;
|
|
const char* tmp_b = NULL;
|
|
size_t tmp_len_a = len_a;
|
|
size_t tmp_len_b = len_b;
|
|
|
|
int ccc_a = tryGetCountryCallingCode(a, len_a, &tmp_a, &tmp_len_a, accept_thailand_case);
|
|
int ccc_b = tryGetCountryCallingCode(b, len_b, &tmp_b, &tmp_len_b, accept_thailand_case);
|
|
bool both_have_ccc = false;
|
|
bool may_ignore_prefix = true;
|
|
bool trunk_prefix_is_omitted_a = false;
|
|
bool trunk_prefix_is_omitted_b = false;
|
|
if (ccc_a >= 0 && ccc_b >= 0) {
|
|
if (ccc_a != ccc_b) {
|
|
// Different Country Calling Code. Must be different phone number.
|
|
return false;
|
|
}
|
|
// When both have ccc, do not ignore trunk prefix. Without this,
|
|
// "+81123123" becomes same as "+810123123" (+81 == Japan)
|
|
may_ignore_prefix = false;
|
|
both_have_ccc = true;
|
|
} else if (ccc_a < 0 && ccc_b < 0) {
|
|
// When both do not have ccc, do not ignore trunk prefix. Without this,
|
|
// "123123" becomes same as "0123123"
|
|
may_ignore_prefix = false;
|
|
} else {
|
|
if (ccc_a < 0) {
|
|
tryGetTrunkPrefixOmittedStr(a, len_a, &tmp_a, &tmp_len_a);
|
|
trunk_prefix_is_omitted_a = true;
|
|
}
|
|
if (ccc_b < 0) {
|
|
tryGetTrunkPrefixOmittedStr(b, len_b, &tmp_b, &tmp_len_b);
|
|
trunk_prefix_is_omitted_b = true;
|
|
}
|
|
}
|
|
|
|
if (tmp_a != NULL) {
|
|
a = tmp_a;
|
|
len_a = tmp_len_a;
|
|
}
|
|
if (tmp_b != NULL) {
|
|
b = tmp_b;
|
|
len_b = tmp_len_b;
|
|
}
|
|
|
|
int i_a = len_a - 1;
|
|
int i_b = len_b - 1;
|
|
while (i_a >= 0 && i_b >= 0) {
|
|
bool skip_compare = false;
|
|
char ch_a = a[i_a];
|
|
char ch_b = b[i_b];
|
|
if (isSeparator(ch_a)) {
|
|
i_a--;
|
|
skip_compare = true;
|
|
}
|
|
if (isSeparator(ch_b)) {
|
|
i_b--;
|
|
skip_compare = true;
|
|
}
|
|
|
|
if (!skip_compare) {
|
|
if (ch_a != ch_b) {
|
|
return false;
|
|
}
|
|
i_a--;
|
|
i_b--;
|
|
}
|
|
}
|
|
|
|
if (may_ignore_prefix) {
|
|
bool trunk_prefix_ignorable_a = checkPrefixIsIgnorable(a, i_a);
|
|
if ((trunk_prefix_is_omitted_a && i_a >= 0) || !trunk_prefix_ignorable_a) {
|
|
if (accept_thailand_case) {
|
|
// Maybe the code handling the special case for Thailand makes the
|
|
// result garbled, so disable the code and try again.
|
|
// e.g. "16610001234" must equal to "6610001234", but with
|
|
// Thailand-case handling code, they become equal to each other.
|
|
//
|
|
// Note: we select simplicity rather than adding some complicated
|
|
// logic here for performance(like "checking whether remaining
|
|
// numbers are just 66 or not"), assuming inputs are small
|
|
// enough.
|
|
return phone_number_compare_inter(org_a, org_b, false);
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (trunk_prefix_ignorable_a && trunk_prefix_is_omitted_b) {
|
|
bool cmp_prefixes = i_a == 0 && isDialable(a[i_a]);
|
|
if (cmp_prefixes && org_b[i_a] != a[i_a]) {
|
|
// Unmatched trunk prefix
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool trunk_prefix_ignorable_b = checkPrefixIsIgnorable(b, i_b);
|
|
if ((trunk_prefix_is_omitted_b && i_b >= 0) || !trunk_prefix_ignorable_b) {
|
|
if (accept_thailand_case) {
|
|
return phone_number_compare_inter(org_a, org_b, false);
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if (trunk_prefix_ignorable_b && trunk_prefix_is_omitted_a) {
|
|
bool cmp_prefixes = i_b == 0 && isDialable(b[i_b]);
|
|
if (cmp_prefixes && org_a[i_b] != b[i_b]) {
|
|
// Unmatched trunk prefix
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
// In the US, 1-650-555-1234 must be equal to 650-555-1234,
|
|
// while 090-1234-1234 must not be equal to 90-1234-1234 in Japan.
|
|
// This request exists just in US (with 1 trunk (NDD) prefix).
|
|
// In addition, "011 11 7005554141" must not equal to "+17005554141",
|
|
// while "011 1 7005554141" must equal to "+17005554141"
|
|
//
|
|
// In this comparison, we ignore the prefix '1' just once, when
|
|
// - at least either does not have CCC, or
|
|
// - the remaining non-separator number is 1
|
|
bool may_be_namp = !both_have_ccc;
|
|
while (i_a >= 0) {
|
|
const char ch_a = a[i_a];
|
|
if (isDialable(ch_a)) {
|
|
if (may_be_namp && tryGetISODigit(ch_a) == 1) {
|
|
may_be_namp = false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
i_a--;
|
|
}
|
|
while (i_b >= 0) {
|
|
const char ch_b = b[i_b];
|
|
if (isDialable(ch_b)) {
|
|
if (may_be_namp && tryGetISODigit(ch_b) == 1) {
|
|
may_be_namp = false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
i_b--;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool phone_number_compare_strict(const char* a, const char* b)
|
|
{
|
|
return phone_number_compare_inter(a, b, true);
|
|
}
|
|
|
|
/**
|
|
* Imitates the Java method PhoneNumberUtils.getStrippedReversed.
|
|
* Used for API compatibility with Android 1.6 and earlier.
|
|
*/
|
|
bool phone_number_stripped_reversed_inter(const char* in, char* out, const int len, int *outlen) {
|
|
int in_len = strlen(in);
|
|
int out_len = 0;
|
|
bool have_seen_plus = false;
|
|
for (int i = in_len; --i >= 0;) {
|
|
char c = in[i];
|
|
if ((c >= '0' && c <= '9') || c == '*' || c == '#' || c == 'N') {
|
|
if (out_len < len) {
|
|
out[out_len++] = c;
|
|
}
|
|
} else {
|
|
switch (c) {
|
|
case '+':
|
|
if (!have_seen_plus) {
|
|
if (out_len < len) {
|
|
out[out_len++] = c;
|
|
}
|
|
have_seen_plus = true;
|
|
}
|
|
break;
|
|
case ',':
|
|
case ';':
|
|
out_len = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*outlen = out_len;
|
|
return true;
|
|
}
|
|
|
|
} // namespace android
|