blob: 47dd279c9e09ed8e4a6ada0f01e6051b9600d8e6 [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;
Daisuke Miyakawacfdd4a72009-07-20 18:28:54 -0700204 } else {
205 return -1;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900206 }
207 break;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800208 default:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900209 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800210 }
211 }
212
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900213 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800214}
215
216/**
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900217 * Return true if the prefix of "ch" is "ignorable". Here, "ignorable" means
218 * that "ch" has only one digit and separater characters. The one digit is
219 * assumed to be trunk prefix.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800220 */
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900221static bool checkPrefixIsIgnorable(const char* ch, int i) {
222 bool trunk_prefix_was_read = false;
223 while (i >= 0) {
224 if (tryGetISODigit(ch[i]) >= 0) {
225 if (trunk_prefix_was_read) {
226 // More than one digit appeared, meaning that "a" and "b"
227 // is different.
228 return false;
229 } else {
230 // Ignore just one digit, assuming it is trunk prefix.
231 trunk_prefix_was_read = true;
232 }
233 } else if (isNonSeparator(ch[i])) {
234 // Trunk prefix is a digit, not "*", "#"...
235 return false;
236 }
237 i--;
238 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800239
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900240 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800241}
242
243/**
244 * Compare phone numbers a and b, return true if they're identical
245 * enough for caller ID purposes.
246 *
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900247 * Assume NULL as 0-length string.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800248 *
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900249 * Detailed information:
250 * Currently (as of 2009-06-12), we cannot depend on the locale given from the
251 * OS. For example, current Android does not accept "en_JP", meaning
252 * "the display language is English but the phone should be in Japan", but
253 * en_US, es_US, etc. So we cannot identify which digit is valid trunk prefix
254 * in the country where the phone is used. More specifically, "880-1234-1234"
255 * is not valid phone number in Japan since the trunk prefix in Japan is not 8
256 * but 0 (correct number should be "080-1234-1234"), while Russian trunk prefix
257 * is 8. Also, we cannot know whether the country where users live has trunk
258 * prefix itself. So, we cannot determine whether "+81-80-1234-1234" is NOT
259 * same as "880-1234-1234" (while "+81-80-1234-1234" is same as "080-1234-1234"
260 * and we can determine "880-1234-1234" is different from "080-1234-1234").
261 *
262 * In the future, we should handle trunk prefix more correctly, but as of now,
263 * we just ignore it...
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800264 */
265bool phone_number_compare(const char* a, const char* b)
266{
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900267 size_t len_a = 0;
268 size_t len_b = 0;
269 if (a == NULL) {
270 a = "";
271 } else {
272 len_a = strlen(a);
273 }
274 if (b == NULL) {
275 b = "";
276 } else {
277 len_b = strlen(b);
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800278 }
279
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900280 const char* tmp_a = NULL;
281 const char* tmp_b = NULL;
282 size_t tmp_len_a = len_a;
283 size_t tmp_len_b = len_b;
284
285 int ccc_a = tryGetCountryCallingCode(a, len_a, &tmp_a, &tmp_len_a);
286 int ccc_b = tryGetCountryCallingCode(b, len_b, &tmp_b, &tmp_len_b);
287 bool ok_to_ignore_prefix = true;
288 if (ccc_a >= 0 && ccc_b >= 0) {
289 if (ccc_a != ccc_b) {
290 // Different Country Calling Code. Must be different phone number.
291 return false;
292 }
293 // When both have ccc, do not ignore trunk prefix. Without this,
294 // "+81123123" becomes same as "+810123123" (+81 == Japan)
295 ok_to_ignore_prefix = false;
296 } else if (ccc_a < 0 && ccc_b < 0) {
297 // When both do not have ccc, do not ignore trunk prefix. Without this,
298 // "123123" becomes same as "0123123"
299 ok_to_ignore_prefix = false;
300 } else {
301 if (ccc_a < 0) {
302 tryGetTrunkPrefixOmittedStr(a, len_a, &tmp_a, &tmp_len_a);
303 }
304 if (ccc_b < 0) {
305 tryGetTrunkPrefixOmittedStr(b, len_b, &tmp_b, &tmp_len_b);
306 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800307 }
308
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900309 if (tmp_a != NULL) {
310 a = tmp_a;
311 len_a = tmp_len_a;
312 }
313 if (tmp_b != NULL) {
314 b = tmp_b;
315 len_b = tmp_len_b;
316 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800317
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900318 int i_a = len_a - 1;
319 int i_b = len_b - 1;
320 while (i_a >= 0 && i_b >= 0) {
321 bool skip_compare = false;
322 char ch_a = a[i_a];
323 char ch_b = b[i_b];
324 if (!isNonSeparator(ch_a)) {
325 i_a--;
326 skip_compare = true;
327 }
328 if (!isNonSeparator(ch_b)) {
329 i_b--;
330 skip_compare = true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800331 }
332
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900333 if (!skip_compare) {
334 if (ch_a != ch_b) {
335 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800336 }
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900337 i_a--;
338 i_b--;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800339 }
340 }
341
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900342 if (ok_to_ignore_prefix) {
343 if (!checkPrefixIsIgnorable(a, i_a)) {
344 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800345 }
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900346 if (!checkPrefixIsIgnorable(b, i_b)) {
347 return false;
348 }
349 } else {
350 // In the US, 1-650-555-1234 must be equal to 650-555-1234,
351 // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan.
352 // This request exists just in US (with 1 trunk (NDD) prefix).
353 //
354 // At least, in this "rough" comparison, we should ignore the prefix
355 // '1', so if the remaining non-separator number is 0, we ignore it
356 // just once.
357 bool may_be_namp = true;
358 while (i_a >= 0) {
359 const char ch_a = a[i_a];
360 if (isNonSeparator(ch_a)) {
361 if (may_be_namp && tryGetISODigit(ch_a) == 1) {
362 may_be_namp = false;
363 } else {
364 return false;
365 }
366 }
367 i_a--;
368 }
369 while (i_b >= 0) {
370 const char ch_b = b[i_b];
371 if (isNonSeparator(ch_b)) {
372 if (may_be_namp && tryGetISODigit(ch_b) == 1) {
373 may_be_namp = false;
374 } else {
375 return false;
376 }
377 }
378 i_b--;
379 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800380 }
381
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900382 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800383}
384
385} // namespace android