blob: 321b0ea5d89601d2b5b7a233d628cad5f35d4c96 [file] [log] [blame]
Daisuke Miyakawade430942009-06-26 11:17: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 Miyakawade430942009-06-26 11:17: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 Miyakawade430942009-06-26 11:17: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 Miyakawade430942009-06-26 11:17: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/** True the character(s) expresses some country calling code. False otherwise.
61 */
62static bool isCountryCallingCode(int ccc_candidate) {
63 return ccc_candidate > 0 &&
64 ccc_candidate < (int)sizeof(two_length_country_code_map) &&
65 two_length_country_code_map[ccc_candidate];
The Android Open Source Project7790ef52009-03-03 19:30:40 -080066}
67
68/**
Daisuke Miyakawade430942009-06-26 11:17:34 +090069 * Returns interger corresponding to the input if input "ch" is
70 * ISO-LATIN characters 0-9.
71 * Returns -1 otherwise
The Android Open Source Project7790ef52009-03-03 19:30:40 -080072 */
Daisuke Miyakawade430942009-06-26 11:17:34 +090073static int tryGetISODigit (char ch)
The Android Open Source Project7790ef52009-03-03 19:30:40 -080074{
Daisuke Miyakawade430942009-06-26 11:17:34 +090075 if ('0' <= ch && ch <= '9') {
76 return ch - '0';
77 } else {
78 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -080079 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -080080}
81
Daisuke Miyakawade430942009-06-26 11:17:34 +090082/** True if c is ISO-LATIN characters 0-9, *, # , + */
83static bool isNonSeparator(char ch)
The Android Open Source Project7790ef52009-03-03 19:30:40 -080084{
Daisuke Miyakawade430942009-06-26 11:17:34 +090085 return ('0' <= ch && ch <= '9') || ch == '*' || ch == '#' || ch == '+';
86}
The Android Open Source Project7790ef52009-03-03 19:30:40 -080087
Daisuke Miyakawade430942009-06-26 11:17:34 +090088/**
89 * Try to store the pointer to "new_ptr" which does not have trunk prefix.
90 *
91 * Currently this function simply ignore the first digit assuming it is
92 * trunk prefix. Actually trunk prefix is different in each country.
93 *
94 * e.g.
95 * "+79161234567" equals "89161234567" (Russian trunk digit is 8)
96 * "+33123456789" equals "0123456789" (French trunk digit is 0)
97 *
98 */
99static bool tryGetTrunkPrefixOmittedStr(const char *str, size_t len,
100 const char **new_ptr, size_t *new_len)
101{
102 for (size_t i = 0 ; i < len ; i++) {
103 char ch = str[i];
104 if (tryGetISODigit(ch) >= 0) {
105 if (new_ptr != NULL) {
106 *new_ptr = str + i + 1;
107 }
108 if (new_len != NULL) {
109 *new_len = len - (i + 1);
110 }
111 return true;
112 } else if (isNonSeparator(ch)) {
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800113 return false;
114 }
115 }
Daisuke Miyakawade430942009-06-26 11:17:34 +0900116
117 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800118}
119
Daisuke Miyakawade430942009-06-26 11:17:34 +0900120static int tryGetCountryCallingCode(const char *str, size_t len,
121 const char **new_ptr, size_t *new_len)
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800122{
Daisuke Miyakawade430942009-06-26 11:17:34 +0900123 // Rough regexp:
124 // ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
125 // 0 1 2 3 45 6 7 89
126 //
127 // In all the states, this function ignores separator characters.
128 // "166" is the special case for the call from Thailand to the US. Ugu!
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800129
130 int state = 0;
Daisuke Miyakawade430942009-06-26 11:17:34 +0900131 int ccc = 0;
132 for (size_t i = 0 ; i < len ; i++ ) {
133 char ch = str[i];
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800134 switch (state) {
135 case 0:
Daisuke Miyakawade430942009-06-26 11:17:34 +0900136 if (ch == '+') state = 1;
137 else if (ch == '0') state = 2;
138 else if (ch == '1') state = 8;
139 else if (isNonSeparator(ch)) return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800140 break;
141
142 case 2:
Daisuke Miyakawade430942009-06-26 11:17:34 +0900143 if (ch == '0') state = 3;
144 else if (ch == '1') state = 4;
145 else if (isNonSeparator(ch)) return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800146 break;
147
148 case 4:
Daisuke Miyakawade430942009-06-26 11:17:34 +0900149 if (ch == '1') state = 5;
150 else if (isNonSeparator(ch)) return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800151 break;
152
153 case 1:
154 case 3:
155 case 5:
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800156 case 6:
157 case 7:
Daisuke Miyakawade430942009-06-26 11:17:34 +0900158 {
159 int ret = tryGetISODigit(ch);
160 if (ret > 0) {
161 ccc = ccc * 10 + ret;
162 if (ccc >= 100 || isCountryCallingCode(ccc)) {
163 if (new_ptr != NULL) {
164 *new_ptr = str + i + 1;
165 }
166 if (new_len != NULL) {
167 *new_len = len - (i + 1);
168 }
169 return ccc;
170 }
171 if (state == 1 || state == 3 || state == 5) {
172 state = 6;
173 } else {
174 state++;
175 }
176 } else if (isNonSeparator(ch)) {
177 return -1;
178 }
179 }
180 break;
181 case 8:
182 if (ch == '6') state = 9;
183 else if (isNonSeparator(ch)) return -1;
184 break;
185 case 9:
186 if (ch == '6') {
187 if (new_ptr != NULL) {
188 *new_ptr = str + i + 1;
189 }
190 if (new_len != NULL) {
191 *new_len = len - (i + 1);
192 }
193 return 66;
194 }
195 break;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800196 default:
Daisuke Miyakawade430942009-06-26 11:17:34 +0900197 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800198 }
199 }
200
Daisuke Miyakawade430942009-06-26 11:17:34 +0900201 return -1;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800202}
203
204/**
Daisuke Miyakawade430942009-06-26 11:17:34 +0900205 * Return true if the prefix of "ch" is "ignorable". Here, "ignorable" means
206 * that "ch" has only one digit and separater characters. The one digit is
207 * assumed to be trunk prefix.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800208 */
Daisuke Miyakawade430942009-06-26 11:17:34 +0900209static bool checkPrefixIsIgnorable(const char* ch, int i) {
210 bool trunk_prefix_was_read = false;
211 while (i >= 0) {
212 if (tryGetISODigit(ch[i]) >= 0) {
213 if (trunk_prefix_was_read) {
214 // More than one digit appeared, meaning that "a" and "b"
215 // is different.
216 return false;
217 } else {
218 // Ignore just one digit, assuming it is trunk prefix.
219 trunk_prefix_was_read = true;
220 }
221 } else if (isNonSeparator(ch[i])) {
222 // Trunk prefix is a digit, not "*", "#"...
223 return false;
224 }
225 i--;
226 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800227
Daisuke Miyakawade430942009-06-26 11:17:34 +0900228 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800229}
230
231/**
232 * Compare phone numbers a and b, return true if they're identical
233 * enough for caller ID purposes.
234 *
Daisuke Miyakawade430942009-06-26 11:17:34 +0900235 * Assume NULL as 0-length string.
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800236 *
Daisuke Miyakawade430942009-06-26 11:17:34 +0900237 * Detailed information:
238 * Currently (as of 2009-06-12), we cannot depend on the locale given from the
239 * OS. For example, current Android does not accept "en_JP", meaning
240 * "the display language is English but the phone should be in Japan", but
241 * en_US, es_US, etc. So we cannot identify which digit is valid trunk prefix
242 * in the country where the phone is used. More specifically, "880-1234-1234"
243 * is not valid phone number in Japan since the trunk prefix in Japan is not 8
244 * but 0 (correct number should be "080-1234-1234"), while Russian trunk prefix
245 * is 8. Also, we cannot know whether the country where users live has trunk
246 * prefix itself. So, we cannot determine whether "+81-80-1234-1234" is NOT
247 * same as "880-1234-1234" (while "+81-80-1234-1234" is same as "080-1234-1234"
248 * and we can determine "880-1234-1234" is different from "080-1234-1234").
249 *
250 * In the future, we should handle trunk prefix more correctly, but as of now,
251 * we just ignore it...
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800252 */
253bool phone_number_compare(const char* a, const char* b)
254{
Daisuke Miyakawade430942009-06-26 11:17:34 +0900255 size_t len_a = 0;
256 size_t len_b = 0;
257 if (a == NULL) {
258 a = "";
259 } else {
260 len_a = strlen(a);
261 }
262 if (b == NULL) {
263 b = "";
264 } else {
265 len_b = strlen(b);
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800266 }
267
Daisuke Miyakawade430942009-06-26 11:17:34 +0900268 const char* tmp_a = NULL;
269 const char* tmp_b = NULL;
270 size_t tmp_len_a = len_a;
271 size_t tmp_len_b = len_b;
272
273 int ccc_a = tryGetCountryCallingCode(a, len_a, &tmp_a, &tmp_len_a);
274 int ccc_b = tryGetCountryCallingCode(b, len_b, &tmp_b, &tmp_len_b);
275 bool ok_to_ignore_prefix = true;
276 if (ccc_a >= 0 && ccc_b >= 0) {
277 if (ccc_a != ccc_b) {
278 // Different Country Calling Code. Must be different phone number.
279 return false;
280 }
281 // When both have ccc, do not ignore trunk prefix. Without this,
282 // "+81123123" becomes same as "+810123123" (+81 == Japan)
283 ok_to_ignore_prefix = false;
284 } else if (ccc_a < 0 && ccc_b < 0) {
285 // When both do not have ccc, do not ignore trunk prefix. Without this,
286 // "123123" becomes same as "0123123"
287 ok_to_ignore_prefix = false;
288 } else {
289 if (ccc_a < 0) {
290 tryGetTrunkPrefixOmittedStr(a, len_a, &tmp_a, &tmp_len_a);
291 }
292 if (ccc_b < 0) {
293 tryGetTrunkPrefixOmittedStr(b, len_b, &tmp_b, &tmp_len_b);
294 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800295 }
296
Daisuke Miyakawade430942009-06-26 11:17:34 +0900297 if (tmp_a != NULL) {
298 a = tmp_a;
299 len_a = tmp_len_a;
300 }
301 if (tmp_b != NULL) {
302 b = tmp_b;
303 len_b = tmp_len_b;
304 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800305
Daisuke Miyakawade430942009-06-26 11:17:34 +0900306 int i_a = len_a - 1;
307 int i_b = len_b - 1;
308 while (i_a >= 0 && i_b >= 0) {
309 bool skip_compare = false;
310 char ch_a = a[i_a];
311 char ch_b = b[i_b];
312 if (!isNonSeparator(ch_a)) {
313 i_a--;
314 skip_compare = true;
315 }
316 if (!isNonSeparator(ch_b)) {
317 i_b--;
318 skip_compare = true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800319 }
320
Daisuke Miyakawade430942009-06-26 11:17:34 +0900321 if (!skip_compare) {
322 if (ch_a != ch_b) {
323 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800324 }
Daisuke Miyakawade430942009-06-26 11:17:34 +0900325 i_a--;
326 i_b--;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800327 }
328 }
329
Daisuke Miyakawade430942009-06-26 11:17:34 +0900330 if (ok_to_ignore_prefix) {
331 if (!checkPrefixIsIgnorable(a, i_a)) {
332 return false;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800333 }
Daisuke Miyakawade430942009-06-26 11:17:34 +0900334 if (!checkPrefixIsIgnorable(b, i_b)) {
335 return false;
336 }
337 } else {
338 while (i_a >= 0) {
339 if (isNonSeparator(a[i_a])) {
340 return false;
341 }
342 i_a--;
343 }
344 while (i_b >= 0) {
345 if (isNonSeparator(b[i_b])) {
346 return false;
347 }
348 i_b--;
349 }
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800350 }
351
Daisuke Miyakawade430942009-06-26 11:17:34 +0900352 return true;
The Android Open Source Project7790ef52009-03-03 19:30:40 -0800353}
354
355} // namespace android