blob: cb8552e2648dea90569db0c1cf987414ae96bf55 [file] [log] [blame]
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +09001/*
2 * Copyright 2009, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
The Android Open Source Project7790ef52009-03-03 19:30:40 -080016
17#include <string.h>
18
19namespace android {
20
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090021/* Generated by the following Python script. Values of country calling codes
22 are from http://en.wikipedia.org/wiki/List_of_country_calling_codes
The Android Open Source Project7790ef52009-03-03 19:30:40 -080023
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090024#!/usr/bin/python
25import sys
26ccc_set_2digits = set([0, 1, 7,
27 20, 27, 28, 30, 31, 32, 33, 34, 36, 39, 40, 43, 44, 45,
28 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 58, 60, 61,
29 62, 63, 64, 65, 66, 81, 82, 83, 84, 86, 89, 90, 91, 92,
30 93, 94, 95, 98])
The Android Open Source Project7790ef52009-03-03 19:30:40 -080031
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090032ONE_LINE_NUM = 10
33
34for i in xrange(100):
35 if i % ONE_LINE_NUM == 0:
36 sys.stdout.write(' ')
37 if i in ccc_set_2digits:
38 included = 'true'
39 else:
40 included = 'false'
41 sys.stdout.write(included + ',')
42 if ((i + 1) % ONE_LINE_NUM) == 0:
43 sys.stdout.write('\n')
44 else:
45 sys.stdout.write(' ')
46*/
47static bool two_length_country_code_map[100] = {
48 true, true, false, false, false, false, false, true, false, false,
49 false, false, false, false, false, false, false, false, false, false,
50 true, false, false, false, false, false, false, true, true, false,
51 true, true, true, true, true, false, true, false, false, true,
52 true, false, false, true, true, true, true, true, true, true,
53 false, true, true, true, true, true, true, true, true, false,
54 true, true, true, true, true, true, true, false, false, false,
55 false, false, false, false, false, false, false, false, false, false,
56 false, true, true, true, true, false, true, false, false, true,
57 true, true, true, true, true, true, false, false, true, false,
58};
59
60#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
61
62/**
63 * Returns true if "ccc_candidate" expresses (part of ) some country calling
64 * code.
65 * Returns false otherwise.
66 */
67static bool isCountryCallingCode(int ccc_candidate) {
68 return ccc_candidate > 0 &&
69 ccc_candidate < (int)ARRAY_SIZE(two_length_country_code_map) &&
70 two_length_country_code_map[ccc_candidate];
Daisuke Miyakawade430942009-06-26 11:17:34 +090071}
The Android Open Source Project7790ef52009-03-03 19:30:40 -080072
Daisuke Miyakawade430942009-06-26 11:17:34 +090073/**
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090074 * Returns interger corresponding to the input if input "ch" is
75 * ISO-LATIN characters 0-9.
76 * Returns -1 otherwise
Daisuke Miyakawade430942009-06-26 11:17:34 +090077 */
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090078static int tryGetISODigit (char ch)
Daisuke Miyakawade430942009-06-26 11:17:34 +090079{
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090080 if ('0' <= ch && ch <= '9') {
81 return ch - '0';
82 } else {
83 return -1;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090084 }
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090085}
86
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090087/** True if c is ISO-LATIN characters 0-9, *, # , + */
88static bool isNonSeparator(char ch)
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090089{
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090090 return ('0' <= ch && ch <= '9') || ch == '*' || ch == '#' || ch == '+';
91}
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090092
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090093/**
94 * Try to store the pointer to "new_ptr" which does not have trunk prefix.
95 *
96 * Currently this function simply ignore the first digit assuming it is
97 * trunk prefix. Actually trunk prefix is different in each country.
98 *
99 * e.g.
100 * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
101 * "+33123456789" equals "0123456789" (French trunk digit is 0)
102 *
103 */
104static bool tryGetTrunkPrefixOmittedStr(const char *str, size_t len,
105 const char **new_ptr, size_t *new_len)
106{
107 for (size_t i = 0 ; i < len ; i++) {
108 char ch = str[i];
109 if (tryGetISODigit(ch) >= 0) {
110 if (new_ptr != NULL) {
111 *new_ptr = str + i + 1;
112 }
113 if (new_len != NULL) {
114 *new_len = len - (i + 1);
115 }
116 return true;
117 } else if (isNonSeparator(ch)) {
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900118 return false;
119 }
120 }
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900121
122 return false;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900123}
124
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900125/*
126 * Note that this function does not strictly care the country calling code with
127 * 3 length (like Morocco: +212), assuming it is enough to use the first two
128 * digit to compare two phone numbers.
129 */
130static int tryGetCountryCallingCode(const char *str, size_t len,
131 const char **new_ptr, size_t *new_len)
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900132{
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900133 // Rough regexp:
134 // ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
135 // 0 1 2 3 45 6 7 89
136 //
137 // In all the states, this function ignores separator characters.
138 // "166" is the special case for the call from Thailand to the US. Ugu!
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900139
140 int state = 0;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900141 int ccc = 0;
142 for (size_t i = 0 ; i < len ; i++ ) {
143 char ch = str[i];
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900144 switch (state) {
145 case 0:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900146 if (ch == '+') state = 1;
147 else if (ch == '0') state = 2;
148 else if (ch == '1') state = 8;
149 else if (isNonSeparator(ch)) return -1;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900150 break;
151
152 case 2:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900153 if (ch == '0') state = 3;
154 else if (ch == '1') state = 4;
155 else if (isNonSeparator(ch)) return -1;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900156 break;
157
158 case 4:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900159 if (ch == '1') state = 5;
160 else if (isNonSeparator(ch)) return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800161 break;
162
163 case 1:
164 case 3:
165 case 5:
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800166 case 6:
167 case 7:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900168 {
169 int ret = tryGetISODigit(ch);
170 if (ret > 0) {
171 ccc = ccc * 10 + ret;
172 if (ccc >= 100 || isCountryCallingCode(ccc)) {
173 if (new_ptr != NULL) {
174 *new_ptr = str + i + 1;
175 }
176 if (new_len != NULL) {
177 *new_len = len - (i + 1);
178 }
179 return ccc;
180 }
181 if (state == 1 || state == 3 || state == 5) {
182 state = 6;
183 } else {
184 state++;
185 }
186 } else if (isNonSeparator(ch)) {
187 return -1;
188 }
189 }
190 break;
191 case 8:
192 if (ch == '6') state = 9;
193 else if (isNonSeparator(ch)) return -1;
194 break;
195 case 9:
196 if (ch == '6') {
197 if (new_ptr != NULL) {
198 *new_ptr = str + i + 1;
199 }
200 if (new_len != NULL) {
201 *new_len = len - (i + 1);
202 }
203 return 66;
204 }
205 break;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800206 default:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900207 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800208 }
209 }
210
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900211 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800212}
213
214/**
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900215 * Return true if the prefix of "ch" is "ignorable". Here, "ignorable" means
216 * that "ch" has only one digit and separater characters. The one digit is
217 * assumed to be trunk prefix.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800218 */
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900219static bool checkPrefixIsIgnorable(const char* ch, int i) {
220 bool trunk_prefix_was_read = false;
221 while (i >= 0) {
222 if (tryGetISODigit(ch[i]) >= 0) {
223 if (trunk_prefix_was_read) {
224 // More than one digit appeared, meaning that "a" and "b"
225 // is different.
226 return false;
227 } else {
228 // Ignore just one digit, assuming it is trunk prefix.
229 trunk_prefix_was_read = true;
230 }
231 } else if (isNonSeparator(ch[i])) {
232 // Trunk prefix is a digit, not "*", "#"...
233 return false;
234 }
235 i--;
236 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800237
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900238 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800239}
240
241/**
242 * Compare phone numbers a and b, return true if they're identical
243 * enough for caller ID purposes.
244 *
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900245 * Assume NULL as 0-length string.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800246 *
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900247 * Detailed information:
248 * Currently (as of 2009-06-12), we cannot depend on the locale given from the
249 * OS. For example, current Android does not accept "en_JP", meaning
250 * "the display language is English but the phone should be in Japan", but
251 * en_US, es_US, etc. So we cannot identify which digit is valid trunk prefix
252 * in the country where the phone is used. More specifically, "880-1234-1234"
253 * is not valid phone number in Japan since the trunk prefix in Japan is not 8
254 * but 0 (correct number should be "080-1234-1234"), while Russian trunk prefix
255 * is 8. Also, we cannot know whether the country where users live has trunk
256 * prefix itself. So, we cannot determine whether "+81-80-1234-1234" is NOT
257 * same as "880-1234-1234" (while "+81-80-1234-1234" is same as "080-1234-1234"
258 * and we can determine "880-1234-1234" is different from "080-1234-1234").
259 *
260 * In the future, we should handle trunk prefix more correctly, but as of now,
261 * we just ignore it...
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800262 */
263bool phone_number_compare(const char* a, const char* b)
264{
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900265 size_t len_a = 0;
266 size_t len_b = 0;
267 if (a == NULL) {
268 a = "";
269 } else {
270 len_a = strlen(a);
271 }
272 if (b == NULL) {
273 b = "";
274 } else {
275 len_b = strlen(b);
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800276 }
277
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900278 const char* tmp_a = NULL;
279 const char* tmp_b = NULL;
280 size_t tmp_len_a = len_a;
281 size_t tmp_len_b = len_b;
282
283 int ccc_a = tryGetCountryCallingCode(a, len_a, &tmp_a, &tmp_len_a);
284 int ccc_b = tryGetCountryCallingCode(b, len_b, &tmp_b, &tmp_len_b);
285 bool ok_to_ignore_prefix = true;
286 if (ccc_a >= 0 && ccc_b >= 0) {
287 if (ccc_a != ccc_b) {
288 // Different Country Calling Code. Must be different phone number.
289 return false;
290 }
291 // When both have ccc, do not ignore trunk prefix. Without this,
292 // "+81123123" becomes same as "+810123123" (+81 == Japan)
293 ok_to_ignore_prefix = false;
294 } else if (ccc_a < 0 && ccc_b < 0) {
295 // When both do not have ccc, do not ignore trunk prefix. Without this,
296 // "123123" becomes same as "0123123"
297 ok_to_ignore_prefix = false;
298 } else {
299 if (ccc_a < 0) {
300 tryGetTrunkPrefixOmittedStr(a, len_a, &tmp_a, &tmp_len_a);
301 }
302 if (ccc_b < 0) {
303 tryGetTrunkPrefixOmittedStr(b, len_b, &tmp_b, &tmp_len_b);
304 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800305 }
306
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900307 if (tmp_a != NULL) {
308 a = tmp_a;
309 len_a = tmp_len_a;
310 }
311 if (tmp_b != NULL) {
312 b = tmp_b;
313 len_b = tmp_len_b;
314 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800315
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900316 int i_a = len_a - 1;
317 int i_b = len_b - 1;
318 while (i_a >= 0 && i_b >= 0) {
319 bool skip_compare = false;
320 char ch_a = a[i_a];
321 char ch_b = b[i_b];
322 if (!isNonSeparator(ch_a)) {
323 i_a--;
324 skip_compare = true;
325 }
326 if (!isNonSeparator(ch_b)) {
327 i_b--;
328 skip_compare = true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800329 }
330
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900331 if (!skip_compare) {
332 if (ch_a != ch_b) {
333 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800334 }
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900335 i_a--;
336 i_b--;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800337 }
338 }
339
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900340 if (ok_to_ignore_prefix) {
341 if (!checkPrefixIsIgnorable(a, i_a)) {
342 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800343 }
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900344 if (!checkPrefixIsIgnorable(b, i_b)) {
345 return false;
346 }
347 } else {
348 // In the US, 1-650-555-1234 must be equal to 650-555-1234,
349 // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan.
350 // This request exists just in US (with 1 trunk (NDD) prefix).
351 //
352 // At least, in this "rough" comparison, we should ignore the prefix
353 // '1', so if the remaining non-separator number is 0, we ignore it
354 // just once.
355 bool may_be_namp = true;
356 while (i_a >= 0) {
357 const char ch_a = a[i_a];
358 if (isNonSeparator(ch_a)) {
359 if (may_be_namp && tryGetISODigit(ch_a) == 1) {
360 may_be_namp = false;
361 } else {
362 return false;
363 }
364 }
365 i_a--;
366 }
367 while (i_b >= 0) {
368 const char ch_b = b[i_b];
369 if (isNonSeparator(ch_b)) {
370 if (may_be_namp && tryGetISODigit(ch_b) == 1) {
371 may_be_namp = false;
372 } else {
373 return false;
374 }
375 }
376 i_b--;
377 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800378 }
379
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900380 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800381}
382
383} // namespace android