• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
16 
17 #include <ctype.h>
18 #include <string.h>
19 
20 namespace android {
21 
22 /* Generated by the following Python script. Values of country calling codes
23    are from http://en.wikipedia.org/wiki/List_of_country_calling_codes
24 
25 #!/usr/bin/python
26 import sys
27 ccc_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])
32 
33 ONE_LINE_NUM = 10
34 
35 for 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 */
48 static 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  */
isCountryCallingCode(int ccc_candidate)68 static 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];
72 }
73 
74 /**
75  * Returns interger corresponding to the input if input "ch" is
76  * ISO-LATIN characters 0-9.
77  * Returns -1 otherwise
78  */
tryGetISODigit(char ch)79 static int tryGetISODigit (char ch)
80 {
81     if ('0' <= ch && ch <= '9') {
82         return ch - '0';
83     } else {
84         return -1;
85     }
86 }
87 
88 /**
89  * True if ch is ISO-LATIN characters 0-9, *, # , +
90  * Note this method current does not account for the WILD char 'N'
91  */
isDialable(char ch)92 static bool isDialable(char ch)
93 {
94     return ('0' <= ch && ch <= '9') || ch == '*' || ch == '#' || ch == '+';
95 }
96 
97 /** Returns true if ch is not dialable or alpha char */
isSeparator(char ch)98 static bool isSeparator(char ch)
99 {
100     return !isDialable(ch) && (isalpha(ch) == 0);
101 }
102 
103 /**
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  */
tryGetTrunkPrefixOmittedStr(const char * str,size_t len,const char ** new_ptr,size_t * new_len)114 static 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;
127         } else if (isDialable(ch)) {
128             return false;
129         }
130     }
131 
132     return false;
133 }
134 
135 /*
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  */
tryGetCountryCallingCode(const char * str,size_t len,const char ** new_ptr,size_t * new_len)140 static int tryGetCountryCallingCode(const char *str, size_t len,
141                                     const char **new_ptr, size_t *new_len)
142 {
143     // Rough regexp:
144     //  ^[^0-9*#+]*((\+|0(0|11)\d\d?|166) [^0-9*#+] $
145     //         0        1 2 3 45  6 7  89
146     //
147     // In all the states, this function ignores separator characters.
148     // "166" is the special case for the call from Thailand to the US. Ugu!
149 
150     int state = 0;
151     int ccc = 0;
152     for (size_t i = 0 ; i < len ; i++ ) {
153         char ch = str[i];
154         switch (state) {
155             case 0:
156                 if      (ch == '+') state = 1;
157                 else if (ch == '0') state = 2;
158                 else if (ch == '1') state = 8;
159                 else if (isDialable(ch)) return -1;
160             break;
161 
162             case 2:
163                 if      (ch == '0') state = 3;
164                 else if (ch == '1') state = 4;
165                 else if (isDialable(ch)) return -1;
166             break;
167 
168             case 4:
169                 if      (ch == '1') state = 5;
170                 else if (isDialable(ch)) return -1;
171             break;
172 
173             case 1:
174             case 3:
175             case 5:
176             case 6:
177             case 7:
178                 {
179                     int ret = tryGetISODigit(ch);
180                     if (ret > 0) {
181                         ccc = ccc * 10 + ret;
182                         if (ccc >= 100 || isCountryCallingCode(ccc)) {
183                             if (new_ptr != NULL) {
184                                 *new_ptr = str + i + 1;
185                             }
186                             if (new_len != NULL) {
187                                 *new_len = len - (i + 1);
188                             }
189                             return ccc;
190                         }
191                         if (state == 1 || state == 3 || state == 5) {
192                             state = 6;
193                         } else {
194                             state++;
195                         }
196                     } else if (isDialable(ch)) {
197                         return -1;
198                     }
199                 }
200                 break;
201             case 8:
202                 if (ch == '6') state = 9;
203                 else if (isDialable(ch)) return -1;
204                 break;
205             case 9:
206                 if (ch == '6') {
207                     if (new_ptr != NULL) {
208                         *new_ptr = str + i + 1;
209                     }
210                     if (new_len != NULL) {
211                         *new_len = len - (i + 1);
212                     }
213                     return 66;
214                 } else {
215                     return -1;
216                 }
217                 break;
218             default:
219                 return -1;
220         }
221     }
222 
223     return -1;
224 }
225 
226 /**
227  * Return true if the prefix of "ch" is "ignorable". Here, "ignorable" means
228  * that "ch" has only one digit and separater characters. The one digit is
229  * assumed to be trunk prefix.
230  */
checkPrefixIsIgnorable(const char * ch,int i)231 static bool checkPrefixIsIgnorable(const char* ch, int i) {
232     bool trunk_prefix_was_read = false;
233     while (i >= 0) {
234         if (tryGetISODigit(ch[i]) >= 0) {
235             if (trunk_prefix_was_read) {
236                 // More than one digit appeared, meaning that "a" and "b"
237                 // is different.
238                 return false;
239             } else {
240                 // Ignore just one digit, assuming it is trunk prefix.
241                 trunk_prefix_was_read = true;
242             }
243         } else if (isDialable(ch[i])) {
244             // Trunk prefix is a digit, not "*", "#"...
245             return false;
246         }
247         i--;
248     }
249 
250     return true;
251 }
252 
253 /**
254  * Compare phone numbers a and b, return true if they're identical
255  * enough for caller ID purposes.
256  *
257  * Assume NULL as 0-length string.
258  *
259  * Detailed information:
260  * Currently (as of 2009-06-12), we cannot depend on the locale given from the
261  * OS. For example, current Android does not accept "en_JP", meaning
262  * "the display language is English but the phone should be in Japan", but
263  * en_US, es_US, etc. So we cannot identify which digit is valid trunk prefix
264  * in the country where the phone is used. More specifically, "880-1234-1234"
265  * is not valid phone number in Japan since the trunk prefix in Japan is not 8
266  * but 0 (correct number should be "080-1234-1234"), while Russian trunk prefix
267  * is 8. Also, we cannot know whether the country where users live has trunk
268  * prefix itself. So, we cannot determine whether "+81-80-1234-1234" is NOT
269  * same as "880-1234-1234" (while "+81-80-1234-1234" is same as "080-1234-1234"
270  * and we can determine "880-1234-1234" is different from "080-1234-1234").
271  *
272  * In the future, we should handle trunk prefix more correctly, but as of now,
273  * we just ignore it...
274  */
phone_number_compare(const char * a,const char * b)275 bool phone_number_compare(const char* a, const char* b)
276 {
277     size_t len_a = 0;
278     size_t len_b = 0;
279     if (a == NULL) {
280         a = "";
281     } else {
282         len_a = strlen(a);
283     }
284     if (b == NULL) {
285         b = "";
286     } else {
287         len_b = strlen(b);
288     }
289 
290     const char* tmp_a = NULL;
291     const char* tmp_b = NULL;
292     size_t tmp_len_a = len_a;
293     size_t tmp_len_b = len_b;
294 
295     int ccc_a = tryGetCountryCallingCode(a, len_a, &tmp_a, &tmp_len_a);
296     int ccc_b = tryGetCountryCallingCode(b, len_b, &tmp_b, &tmp_len_b);
297     bool ok_to_ignore_prefix = true;
298     if (ccc_a >= 0 && ccc_b >= 0) {
299         if (ccc_a != ccc_b) {
300             // Different Country Calling Code. Must be different phone number.
301             return false;
302         }
303         // When both have ccc, do not ignore trunk prefix. Without this,
304         // "+81123123" becomes same as "+810123123" (+81 == Japan)
305         ok_to_ignore_prefix = false;
306     } else if (ccc_a < 0 && ccc_b < 0) {
307         // When both do not have ccc, do not ignore trunk prefix. Without this,
308         // "123123" becomes same as "0123123"
309         ok_to_ignore_prefix = false;
310     } else {
311         if (ccc_a < 0) {
312             tryGetTrunkPrefixOmittedStr(a, len_a, &tmp_a, &tmp_len_a);
313         }
314         if (ccc_b < 0) {
315             tryGetTrunkPrefixOmittedStr(b, len_b, &tmp_b, &tmp_len_b);
316         }
317     }
318 
319     if (tmp_a != NULL) {
320         a = tmp_a;
321         len_a = tmp_len_a;
322     }
323     if (tmp_b != NULL) {
324         b = tmp_b;
325         len_b = tmp_len_b;
326     }
327 
328     int i_a = len_a - 1;
329     int i_b = len_b - 1;
330     while (i_a >= 0 && i_b >= 0) {
331         bool skip_compare = false;
332         char ch_a = a[i_a];
333         char ch_b = b[i_b];
334         if (isSeparator(ch_a)) {
335             i_a--;
336             skip_compare = true;
337         }
338         if (isSeparator(ch_b)) {
339             i_b--;
340             skip_compare = true;
341         }
342 
343         if (!skip_compare) {
344             if (ch_a != ch_b) {
345                 return false;
346             }
347             i_a--;
348             i_b--;
349         }
350     }
351 
352     if (ok_to_ignore_prefix) {
353         if (!checkPrefixIsIgnorable(a, i_a)) {
354             return false;
355         }
356         if (!checkPrefixIsIgnorable(b, i_b)) {
357             return false;
358         }
359     } else {
360         // In the US, 1-650-555-1234 must be equal to 650-555-1234,
361         // while 090-1234-1234 must not be equalt to 90-1234-1234 in Japan.
362         // This request exists just in US (with 1 trunk (NDD) prefix).
363         //
364         // At least, in this "rough" comparison, we should ignore the prefix
365         // '1', so if the remaining non-separator number is 0, we ignore it
366         // just once.
367         bool may_be_namp = true;
368         while (i_a >= 0) {
369             const char ch_a = a[i_a];
370             if (isDialable(ch_a)) {
371                 if (may_be_namp && tryGetISODigit(ch_a) == 1) {
372                     may_be_namp = false;
373                 } else {
374                     return false;
375                 }
376             }
377             i_a--;
378         }
379         while (i_b >= 0) {
380             const char ch_b = b[i_b];
381             if (isDialable(ch_b)) {
382                 if (may_be_namp && tryGetISODigit(ch_b) == 1) {
383                     may_be_namp = false;
384                 } else {
385                     return false;
386                 }
387             }
388             i_b--;
389         }
390     }
391 
392     return true;
393 }
394 
395 } // namespace android
396