blob: a753f426725bfdd4affe588e92b041e6934af0fb [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
Wei Huang0d04c4c2009-08-31 19:18:23 -070017#include <ctype.h>
The Android Open Source Project7790ef52009-03-03 19:30:40 -080018#include <string.h>
19
20namespace android {
21
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090022/* Generated by the following Python script. Values of country calling codes
23 are from http://en.wikipedia.org/wiki/List_of_country_calling_codes
The Android Open Source Project7790ef52009-03-03 19:30:40 -080024
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090025#!/usr/bin/python
26import sys
27ccc_set_2digits = set([0, 1, 7,
28 20, 27, 28, 30, 31, 32, 33, 34, 36, 39, 40, 43, 44, 45,
29 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 57, 58, 60, 61,
30 62, 63, 64, 65, 66, 81, 82, 83, 84, 86, 89, 90, 91, 92,
31 93, 94, 95, 98])
The Android Open Source Project7790ef52009-03-03 19:30:40 -080032
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090033ONE_LINE_NUM = 10
34
35for i in xrange(100):
36 if i % ONE_LINE_NUM == 0:
37 sys.stdout.write(' ')
38 if i in ccc_set_2digits:
39 included = 'true'
40 else:
41 included = 'false'
42 sys.stdout.write(included + ',')
43 if ((i + 1) % ONE_LINE_NUM) == 0:
44 sys.stdout.write('\n')
45 else:
46 sys.stdout.write(' ')
47*/
48static bool two_length_country_code_map[100] = {
49 true, true, false, false, false, false, false, true, false, false,
50 false, false, false, false, false, false, false, false, false, false,
51 true, false, false, false, false, false, false, true, true, false,
52 true, true, true, true, true, false, true, false, false, true,
53 true, false, false, true, true, true, true, true, true, true,
54 false, true, true, true, true, true, true, true, true, false,
55 true, true, true, true, true, true, true, false, false, false,
56 false, false, false, false, false, false, false, false, false, false,
57 false, true, true, true, true, false, true, false, false, true,
58 true, true, true, true, true, true, false, false, true, false,
59};
60
61#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
62
63/**
64 * Returns true if "ccc_candidate" expresses (part of ) some country calling
65 * code.
66 * Returns false otherwise.
67 */
68static bool isCountryCallingCode(int ccc_candidate) {
69 return ccc_candidate > 0 &&
70 ccc_candidate < (int)ARRAY_SIZE(two_length_country_code_map) &&
71 two_length_country_code_map[ccc_candidate];
Daisuke Miyakawade430942009-06-26 11:17:34 +090072}
The Android Open Source Project7790ef52009-03-03 19:30:40 -080073
Daisuke Miyakawade430942009-06-26 11:17:34 +090074/**
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090075 * Returns interger corresponding to the input if input "ch" is
76 * ISO-LATIN characters 0-9.
77 * Returns -1 otherwise
Daisuke Miyakawade430942009-06-26 11:17:34 +090078 */
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090079static int tryGetISODigit (char ch)
Daisuke Miyakawade430942009-06-26 11:17:34 +090080{
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090081 if ('0' <= ch && ch <= '9') {
82 return ch - '0';
83 } else {
84 return -1;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090085 }
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090086}
87
Wei Huang0d04c4c2009-08-31 19:18:23 -070088/**
89 * True if ch is ISO-LATIN characters 0-9, *, # , +
90 * Note this method current does not account for the WILD char 'N'
91 */
92static bool isDialable(char ch)
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090093{
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +090094 return ('0' <= ch && ch <= '9') || ch == '*' || ch == '#' || ch == '+';
95}
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +090096
Wei Huang0d04c4c2009-08-31 19:18:23 -070097/** Returns true if ch is not dialable or alpha char */
98static bool isSeparator(char ch)
99{
100 return !isDialable(ch) && (isalpha(ch) == 0);
101}
102
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900103/**
104 * Try to store the pointer to "new_ptr" which does not have trunk prefix.
105 *
106 * Currently this function simply ignore the first digit assuming it is
107 * trunk prefix. Actually trunk prefix is different in each country.
108 *
109 * e.g.
110 * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
111 * "+33123456789" equals "0123456789" (French trunk digit is 0)
112 *
113 */
114static bool tryGetTrunkPrefixOmittedStr(const char *str, size_t len,
115 const char **new_ptr, size_t *new_len)
116{
117 for (size_t i = 0 ; i < len ; i++) {
118 char ch = str[i];
119 if (tryGetISODigit(ch) >= 0) {
120 if (new_ptr != NULL) {
121 *new_ptr = str + i + 1;
122 }
123 if (new_len != NULL) {
124 *new_len = len - (i + 1);
125 }
126 return true;
Wei Huang0d04c4c2009-08-31 19:18:23 -0700127 } else if (isDialable(ch)) {
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900128 return false;
129 }
130 }
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900131
132 return false;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900133}
134
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900135/*
136 * Note that this function does not strictly care the country calling code with
137 * 3 length (like Morocco: +212), assuming it is enough to use the first two
138 * digit to compare two phone numbers.
139 */
140static int tryGetCountryCallingCode(const char *str, size_t len,
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900141 const char **new_ptr, size_t *new_len,
142 bool accept_thailand_case)
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900143{
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900144 // Rough regexp:
145 // ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
146 // 0 1 2 3 45 6 7 89
147 //
148 // In all the states, this function ignores separator characters.
149 // "166" is the special case for the call from Thailand to the US. Ugu!
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900150
151 int state = 0;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900152 int ccc = 0;
153 for (size_t i = 0 ; i < len ; i++ ) {
154 char ch = str[i];
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900155 switch (state) {
156 case 0:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900157 if (ch == '+') state = 1;
158 else if (ch == '0') state = 2;
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900159 else if (ch == '1') {
160 if (accept_thailand_case) {
161 state = 8;
162 } else {
163 return -1;
164 }
165 } else if (isDialable(ch)) return -1;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900166 break;
167
168 case 2:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900169 if (ch == '0') state = 3;
170 else if (ch == '1') state = 4;
Wei Huang0d04c4c2009-08-31 19:18:23 -0700171 else if (isDialable(ch)) return -1;
Daisuke Miyakawaab3ee7d2009-07-07 09:37:55 +0900172 break;
173
174 case 4:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900175 if (ch == '1') state = 5;
Wei Huang0d04c4c2009-08-31 19:18:23 -0700176 else if (isDialable(ch)) return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800177 break;
178
179 case 1:
180 case 3:
181 case 5:
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800182 case 6:
183 case 7:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900184 {
185 int ret = tryGetISODigit(ch);
186 if (ret > 0) {
187 ccc = ccc * 10 + ret;
188 if (ccc >= 100 || isCountryCallingCode(ccc)) {
189 if (new_ptr != NULL) {
190 *new_ptr = str + i + 1;
191 }
192 if (new_len != NULL) {
193 *new_len = len - (i + 1);
194 }
195 return ccc;
196 }
197 if (state == 1 || state == 3 || state == 5) {
198 state = 6;
199 } else {
200 state++;
201 }
Wei Huang0d04c4c2009-08-31 19:18:23 -0700202 } else if (isDialable(ch)) {
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900203 return -1;
204 }
205 }
206 break;
207 case 8:
208 if (ch == '6') state = 9;
Wei Huang0d04c4c2009-08-31 19:18:23 -0700209 else if (isDialable(ch)) return -1;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900210 break;
211 case 9:
212 if (ch == '6') {
213 if (new_ptr != NULL) {
214 *new_ptr = str + i + 1;
215 }
216 if (new_len != NULL) {
217 *new_len = len - (i + 1);
218 }
219 return 66;
Daisuke Miyakawacfdd4a72009-07-20 18:28:54 -0700220 } else {
221 return -1;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900222 }
223 break;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800224 default:
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900225 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800226 }
227 }
228
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900229 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800230}
231
232/**
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900233 * Return true if the prefix of "ch" is "ignorable". Here, "ignorable" means
234 * that "ch" has only one digit and separater characters. The one digit is
235 * assumed to be trunk prefix.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800236 */
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900237static bool checkPrefixIsIgnorable(const char* ch, int i) {
238 bool trunk_prefix_was_read = false;
239 while (i >= 0) {
240 if (tryGetISODigit(ch[i]) >= 0) {
241 if (trunk_prefix_was_read) {
242 // More than one digit appeared, meaning that "a" and "b"
243 // is different.
244 return false;
245 } else {
246 // Ignore just one digit, assuming it is trunk prefix.
247 trunk_prefix_was_read = true;
248 }
Wei Huang0d04c4c2009-08-31 19:18:23 -0700249 } else if (isDialable(ch[i])) {
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900250 // Trunk prefix is a digit, not "*", "#"...
251 return false;
252 }
253 i--;
254 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800255
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900256 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800257}
258
259/**
260 * Compare phone numbers a and b, return true if they're identical
261 * enough for caller ID purposes.
262 *
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900263 * Assume NULL as 0-length string.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800264 *
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900265 * Detailed information:
266 * Currently (as of 2009-06-12), we cannot depend on the locale given from the
267 * OS. For example, current Android does not accept "en_JP", meaning
268 * "the display language is English but the phone should be in Japan", but
269 * en_US, es_US, etc. So we cannot identify which digit is valid trunk prefix
270 * in the country where the phone is used. More specifically, "880-1234-1234"
271 * is not valid phone number in Japan since the trunk prefix in Japan is not 8
272 * but 0 (correct number should be "080-1234-1234"), while Russian trunk prefix
273 * is 8. Also, we cannot know whether the country where users live has trunk
274 * prefix itself. So, we cannot determine whether "+81-80-1234-1234" is NOT
275 * same as "880-1234-1234" (while "+81-80-1234-1234" is same as "080-1234-1234"
276 * and we can determine "880-1234-1234" is different from "080-1234-1234").
277 *
278 * In the future, we should handle trunk prefix more correctly, but as of now,
279 * we just ignore it...
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800280 */
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900281static bool phone_number_compare_inter(const char* const org_a, const char* const org_b,
282 bool accept_thailand_case)
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800283{
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900284 const char* a = org_a;
285 const char* b = org_b;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900286 size_t len_a = 0;
287 size_t len_b = 0;
288 if (a == NULL) {
289 a = "";
290 } else {
291 len_a = strlen(a);
292 }
293 if (b == NULL) {
294 b = "";
295 } else {
296 len_b = strlen(b);
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800297 }
298
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900299 const char* tmp_a = NULL;
300 const char* tmp_b = NULL;
301 size_t tmp_len_a = len_a;
302 size_t tmp_len_b = len_b;
303
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900304 int ccc_a = tryGetCountryCallingCode(a, len_a, &tmp_a, &tmp_len_a, accept_thailand_case);
305 int ccc_b = tryGetCountryCallingCode(b, len_b, &tmp_b, &tmp_len_b, accept_thailand_case);
306 bool both_have_ccc = false;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900307 bool ok_to_ignore_prefix = true;
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900308 bool trunk_prefix_is_omitted_a = false;
309 bool trunk_prefix_is_omitted_b = false;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900310 if (ccc_a >= 0 && ccc_b >= 0) {
311 if (ccc_a != ccc_b) {
312 // Different Country Calling Code. Must be different phone number.
313 return false;
314 }
315 // When both have ccc, do not ignore trunk prefix. Without this,
316 // "+81123123" becomes same as "+810123123" (+81 == Japan)
317 ok_to_ignore_prefix = false;
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900318 both_have_ccc = true;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900319 } else if (ccc_a < 0 && ccc_b < 0) {
320 // When both do not have ccc, do not ignore trunk prefix. Without this,
321 // "123123" becomes same as "0123123"
322 ok_to_ignore_prefix = false;
323 } else {
324 if (ccc_a < 0) {
325 tryGetTrunkPrefixOmittedStr(a, len_a, &tmp_a, &tmp_len_a);
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900326 trunk_prefix_is_omitted_a = true;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900327 }
328 if (ccc_b < 0) {
329 tryGetTrunkPrefixOmittedStr(b, len_b, &tmp_b, &tmp_len_b);
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900330 trunk_prefix_is_omitted_b = true;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900331 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800332 }
333
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900334 if (tmp_a != NULL) {
335 a = tmp_a;
336 len_a = tmp_len_a;
337 }
338 if (tmp_b != NULL) {
339 b = tmp_b;
340 len_b = tmp_len_b;
341 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800342
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900343 int i_a = len_a - 1;
344 int i_b = len_b - 1;
345 while (i_a >= 0 && i_b >= 0) {
346 bool skip_compare = false;
347 char ch_a = a[i_a];
348 char ch_b = b[i_b];
Wei Huang0d04c4c2009-08-31 19:18:23 -0700349 if (isSeparator(ch_a)) {
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900350 i_a--;
351 skip_compare = true;
352 }
Wei Huang0d04c4c2009-08-31 19:18:23 -0700353 if (isSeparator(ch_b)) {
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900354 i_b--;
355 skip_compare = true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800356 }
357
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900358 if (!skip_compare) {
359 if (ch_a != ch_b) {
360 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800361 }
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900362 i_a--;
363 i_b--;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800364 }
365 }
366
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900367 if (ok_to_ignore_prefix) {
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900368 if ((trunk_prefix_is_omitted_a && i_a >= 0) ||
369 !checkPrefixIsIgnorable(a, i_a)) {
370 if (accept_thailand_case) {
371 // Maybe the code handling the special case for Thailand makes the
372 // result garbled, so disable the code and try again.
373 // e.g. "16610001234" must equal to "6610001234", but with
374 // Thailand-case handling code, they become equal to each other.
375 //
376 // Note: we select simplicity rather than adding some complicated
377 // logic here for performance(like "checking whether remaining
378 // numbers are just 66 or not"), assuming inputs are small
379 // enough.
380 return phone_number_compare_inter(org_a, org_b, false);
381 } else {
382 return false;
383 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800384 }
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900385 if ((trunk_prefix_is_omitted_b && i_b >= 0) ||
386 !checkPrefixIsIgnorable(b, i_b)) {
387 if (accept_thailand_case) {
388 return phone_number_compare_inter(org_a, org_b, false);
389 } else {
390 return false;
391 }
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900392 }
393 } else {
394 // In the US, 1-650-555-1234 must be equal to 650-555-1234,
395 // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan.
396 // This request exists just in US (with 1 trunk (NDD) prefix).
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900397 // In addition, "011 11 7005554141" must not equal to "+17005554141",
398 // while "011 1 7005554141" must equal to "+17005554141"
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900399 //
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900400 // In this comparison, we ignore the prefix '1' just once, when
401 // - at least either does not have CCC, or
402 // - the remaining non-separator number is 1
403 bool may_be_namp = !both_have_ccc;
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900404 while (i_a >= 0) {
405 const char ch_a = a[i_a];
Wei Huang0d04c4c2009-08-31 19:18:23 -0700406 if (isDialable(ch_a)) {
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900407 if (may_be_namp && tryGetISODigit(ch_a) == 1) {
408 may_be_namp = false;
409 } else {
410 return false;
411 }
412 }
413 i_a--;
414 }
415 while (i_b >= 0) {
416 const char ch_b = b[i_b];
Wei Huang0d04c4c2009-08-31 19:18:23 -0700417 if (isDialable(ch_b)) {
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900418 if (may_be_namp && tryGetISODigit(ch_b) == 1) {
419 may_be_namp = false;
420 } else {
421 return false;
422 }
423 }
424 i_b--;
425 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800426 }
427
Daisuke Miyakawaf06f5fa2009-07-07 11:11:34 +0900428 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800429}
430
Daisuke Miyakawa948a1192009-09-19 19:19:53 -0700431bool phone_number_compare_strict(const char* a, const char* b)
Daisuke Miyakawab3b8a9d2009-09-01 22:07:18 +0900432{
433 return phone_number_compare_inter(a, b, true);
434}
435
Dmitri Plotnikov37214cd2011-01-06 09:01:51 -0800436/**
437 * Imitates the Java method PhoneNumberUtils.getStrippedReversed.
438 * Used for API compatibility with Android 1.6 and earlier.
439 */
440bool phone_number_stripped_reversed_inter(const char* in, char* out, const int len, int *outlen) {
441 int in_len = strlen(in);
442 int out_len = 0;
443 bool have_seen_plus = false;
444 for (int i = in_len; --i >= 0;) {
445 char c = in[i];
446 if ((c >= '0' && c <= '9') || c == '*' || c == '#' || c == 'N') {
447 if (out_len < len) {
448 out[out_len++] = c;
449 }
450 } else {
451 switch (c) {
452 case '+':
453 if (!have_seen_plus) {
454 if (out_len < len) {
455 out[out_len++] = c;
456 }
457 have_seen_plus = true;
458 }
459 break;
460 case ',':
461 case ';':
462 out_len = 0;
463 break;
464 }
465 }
466 }
467
468 *outlen = out_len;
469 return true;
470}
471
Daisuke Miyakawa948a1192009-09-19 19:19:53 -0700472} // namespace android