xref: /aosp_15_r20/external/sqlite/android/OldPhoneNumberUtils.cpp (revision a3141fd39888aecc864dfb08485df64ff6c387f9)
1*a3141fd3SAndroid Build Coastguard Worker /*
2*a3141fd3SAndroid Build Coastguard Worker  *
3*a3141fd3SAndroid Build Coastguard Worker  * Copyright 2006, The Android Open Source Project
4*a3141fd3SAndroid Build Coastguard Worker  *
5*a3141fd3SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
6*a3141fd3SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
7*a3141fd3SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
8*a3141fd3SAndroid Build Coastguard Worker  *
9*a3141fd3SAndroid Build Coastguard Worker  *     http://www.apache.org/licenses/LICENSE-2.0
10*a3141fd3SAndroid Build Coastguard Worker  *
11*a3141fd3SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
12*a3141fd3SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
13*a3141fd3SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*a3141fd3SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
15*a3141fd3SAndroid Build Coastguard Worker  * limitations under the License.
16*a3141fd3SAndroid Build Coastguard Worker  */
17*a3141fd3SAndroid Build Coastguard Worker 
18*a3141fd3SAndroid Build Coastguard Worker // Old implementation for phone_number_compare(), which has used in cupcake, but once replaced with
19*a3141fd3SAndroid Build Coastguard Worker // the new, more strict version, and reverted again.
20*a3141fd3SAndroid Build Coastguard Worker 
21*a3141fd3SAndroid Build Coastguard Worker #include <string.h>
22*a3141fd3SAndroid Build Coastguard Worker 
23*a3141fd3SAndroid Build Coastguard Worker namespace android {
24*a3141fd3SAndroid Build Coastguard Worker 
25*a3141fd3SAndroid Build Coastguard Worker static int MIN_MATCH = 7;
26*a3141fd3SAndroid Build Coastguard Worker 
27*a3141fd3SAndroid Build Coastguard Worker /** True if c is ISO-LATIN characters 0-9 */
isISODigit(char c)28*a3141fd3SAndroid Build Coastguard Worker static bool isISODigit (char c)
29*a3141fd3SAndroid Build Coastguard Worker {
30*a3141fd3SAndroid Build Coastguard Worker     return c >= '0' && c <= '9';
31*a3141fd3SAndroid Build Coastguard Worker }
32*a3141fd3SAndroid Build Coastguard Worker 
33*a3141fd3SAndroid Build Coastguard Worker /** True if c is ISO-LATIN characters 0-9, *, # , +  */
isNonSeparator(char c)34*a3141fd3SAndroid Build Coastguard Worker static bool isNonSeparator(char c)
35*a3141fd3SAndroid Build Coastguard Worker {
36*a3141fd3SAndroid Build Coastguard Worker     return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+';
37*a3141fd3SAndroid Build Coastguard Worker }
38*a3141fd3SAndroid Build Coastguard Worker 
39*a3141fd3SAndroid Build Coastguard Worker /**
40*a3141fd3SAndroid Build Coastguard Worker  * Phone numbers are stored in "lookup" form in the database
41*a3141fd3SAndroid Build Coastguard Worker  * as reversed strings to allow for caller ID lookup
42*a3141fd3SAndroid Build Coastguard Worker  *
43*a3141fd3SAndroid Build Coastguard Worker  * This method takes a phone number and makes a valid SQL "LIKE"
44*a3141fd3SAndroid Build Coastguard Worker  * string that will match the lookup form
45*a3141fd3SAndroid Build Coastguard Worker  *
46*a3141fd3SAndroid Build Coastguard Worker  */
47*a3141fd3SAndroid Build Coastguard Worker /** all of a up to len must be an international prefix or
48*a3141fd3SAndroid Build Coastguard Worker  *  separators/non-dialing digits
49*a3141fd3SAndroid Build Coastguard Worker  */
matchIntlPrefix(const char * a,int len)50*a3141fd3SAndroid Build Coastguard Worker static bool matchIntlPrefix(const char* a, int len)
51*a3141fd3SAndroid Build Coastguard Worker {
52*a3141fd3SAndroid Build Coastguard Worker     /* '([^0-9*#+]\+[^0-9*#+] | [^0-9*#+]0(0|11)[^0-9*#+] )$' */
53*a3141fd3SAndroid Build Coastguard Worker     /*        0    1                     2 3 45               */
54*a3141fd3SAndroid Build Coastguard Worker 
55*a3141fd3SAndroid Build Coastguard Worker     int state = 0;
56*a3141fd3SAndroid Build Coastguard Worker     for (int i = 0 ; i < len ; i++) {
57*a3141fd3SAndroid Build Coastguard Worker         char c = a[i];
58*a3141fd3SAndroid Build Coastguard Worker 
59*a3141fd3SAndroid Build Coastguard Worker         switch (state) {
60*a3141fd3SAndroid Build Coastguard Worker             case 0:
61*a3141fd3SAndroid Build Coastguard Worker                 if      (c == '+') state = 1;
62*a3141fd3SAndroid Build Coastguard Worker                 else if (c == '0') state = 2;
63*a3141fd3SAndroid Build Coastguard Worker                 else if (isNonSeparator(c)) return false;
64*a3141fd3SAndroid Build Coastguard Worker             break;
65*a3141fd3SAndroid Build Coastguard Worker 
66*a3141fd3SAndroid Build Coastguard Worker             case 2:
67*a3141fd3SAndroid Build Coastguard Worker                 if      (c == '0') state = 3;
68*a3141fd3SAndroid Build Coastguard Worker                 else if (c == '1') state = 4;
69*a3141fd3SAndroid Build Coastguard Worker                 else if (isNonSeparator(c)) return false;
70*a3141fd3SAndroid Build Coastguard Worker             break;
71*a3141fd3SAndroid Build Coastguard Worker 
72*a3141fd3SAndroid Build Coastguard Worker             case 4:
73*a3141fd3SAndroid Build Coastguard Worker                 if      (c == '1') state = 5;
74*a3141fd3SAndroid Build Coastguard Worker                 else if (isNonSeparator(c)) return false;
75*a3141fd3SAndroid Build Coastguard Worker             break;
76*a3141fd3SAndroid Build Coastguard Worker 
77*a3141fd3SAndroid Build Coastguard Worker             default:
78*a3141fd3SAndroid Build Coastguard Worker                 if (isNonSeparator(c)) return false;
79*a3141fd3SAndroid Build Coastguard Worker             break;
80*a3141fd3SAndroid Build Coastguard Worker 
81*a3141fd3SAndroid Build Coastguard Worker         }
82*a3141fd3SAndroid Build Coastguard Worker     }
83*a3141fd3SAndroid Build Coastguard Worker 
84*a3141fd3SAndroid Build Coastguard Worker     return state == 1 || state == 3 || state == 5;
85*a3141fd3SAndroid Build Coastguard Worker }
86*a3141fd3SAndroid Build Coastguard Worker 
87*a3141fd3SAndroid Build Coastguard Worker /** all of 'a' up to len must match non-US trunk prefix ('0') */
matchTrunkPrefix(const char * a,int len)88*a3141fd3SAndroid Build Coastguard Worker static bool matchTrunkPrefix(const char* a, int len)
89*a3141fd3SAndroid Build Coastguard Worker {
90*a3141fd3SAndroid Build Coastguard Worker     bool found;
91*a3141fd3SAndroid Build Coastguard Worker 
92*a3141fd3SAndroid Build Coastguard Worker     found = false;
93*a3141fd3SAndroid Build Coastguard Worker 
94*a3141fd3SAndroid Build Coastguard Worker     for (int i = 0 ; i < len ; i++) {
95*a3141fd3SAndroid Build Coastguard Worker         char c = a[i];
96*a3141fd3SAndroid Build Coastguard Worker 
97*a3141fd3SAndroid Build Coastguard Worker         if (c == '0' && !found) {
98*a3141fd3SAndroid Build Coastguard Worker             found = true;
99*a3141fd3SAndroid Build Coastguard Worker         } else if (isNonSeparator(c)) {
100*a3141fd3SAndroid Build Coastguard Worker             return false;
101*a3141fd3SAndroid Build Coastguard Worker         }
102*a3141fd3SAndroid Build Coastguard Worker     }
103*a3141fd3SAndroid Build Coastguard Worker 
104*a3141fd3SAndroid Build Coastguard Worker     return found;
105*a3141fd3SAndroid Build Coastguard Worker }
106*a3141fd3SAndroid Build Coastguard Worker 
107*a3141fd3SAndroid Build Coastguard Worker /** all of 'a' up to len must be a (+|00|011)country code)
108*a3141fd3SAndroid Build Coastguard Worker  *  We're fast and loose with the country code. Any \d{1,3} matches */
matchIntlPrefixAndCC(const char * a,int len)109*a3141fd3SAndroid Build Coastguard Worker static bool matchIntlPrefixAndCC(const char* a, int len)
110*a3141fd3SAndroid Build Coastguard Worker {
111*a3141fd3SAndroid Build Coastguard Worker     /*  [^0-9*#+]*(\+|0(0|11)\d\d?\d? [^0-9*#+] $ */
112*a3141fd3SAndroid Build Coastguard Worker     /*      0       1 2 3 45  6 7  8              */
113*a3141fd3SAndroid Build Coastguard Worker 
114*a3141fd3SAndroid Build Coastguard Worker     int state = 0;
115*a3141fd3SAndroid Build Coastguard Worker     for (int i = 0 ; i < len ; i++ ) {
116*a3141fd3SAndroid Build Coastguard Worker         char c = a[i];
117*a3141fd3SAndroid Build Coastguard Worker 
118*a3141fd3SAndroid Build Coastguard Worker         switch (state) {
119*a3141fd3SAndroid Build Coastguard Worker             case 0:
120*a3141fd3SAndroid Build Coastguard Worker                 if      (c == '+') state = 1;
121*a3141fd3SAndroid Build Coastguard Worker                 else if (c == '0') state = 2;
122*a3141fd3SAndroid Build Coastguard Worker                 else if (isNonSeparator(c)) return false;
123*a3141fd3SAndroid Build Coastguard Worker             break;
124*a3141fd3SAndroid Build Coastguard Worker 
125*a3141fd3SAndroid Build Coastguard Worker             case 2:
126*a3141fd3SAndroid Build Coastguard Worker                 if      (c == '0') state = 3;
127*a3141fd3SAndroid Build Coastguard Worker                 else if (c == '1') state = 4;
128*a3141fd3SAndroid Build Coastguard Worker                 else if (isNonSeparator(c)) return false;
129*a3141fd3SAndroid Build Coastguard Worker             break;
130*a3141fd3SAndroid Build Coastguard Worker 
131*a3141fd3SAndroid Build Coastguard Worker             case 4:
132*a3141fd3SAndroid Build Coastguard Worker                 if      (c == '1') state = 5;
133*a3141fd3SAndroid Build Coastguard Worker                 else if (isNonSeparator(c)) return false;
134*a3141fd3SAndroid Build Coastguard Worker             break;
135*a3141fd3SAndroid Build Coastguard Worker 
136*a3141fd3SAndroid Build Coastguard Worker             case 1:
137*a3141fd3SAndroid Build Coastguard Worker             case 3:
138*a3141fd3SAndroid Build Coastguard Worker             case 5:
139*a3141fd3SAndroid Build Coastguard Worker                 if      (isISODigit(c)) state = 6;
140*a3141fd3SAndroid Build Coastguard Worker                 else if (isNonSeparator(c)) return false;
141*a3141fd3SAndroid Build Coastguard Worker             break;
142*a3141fd3SAndroid Build Coastguard Worker 
143*a3141fd3SAndroid Build Coastguard Worker             case 6:
144*a3141fd3SAndroid Build Coastguard Worker             case 7:
145*a3141fd3SAndroid Build Coastguard Worker                 if      (isISODigit(c)) state++;
146*a3141fd3SAndroid Build Coastguard Worker                 else if (isNonSeparator(c)) return false;
147*a3141fd3SAndroid Build Coastguard Worker             break;
148*a3141fd3SAndroid Build Coastguard Worker 
149*a3141fd3SAndroid Build Coastguard Worker             default:
150*a3141fd3SAndroid Build Coastguard Worker                 if (isNonSeparator(c)) return false;
151*a3141fd3SAndroid Build Coastguard Worker         }
152*a3141fd3SAndroid Build Coastguard Worker     }
153*a3141fd3SAndroid Build Coastguard Worker 
154*a3141fd3SAndroid Build Coastguard Worker     return state == 6 || state == 7 || state == 8;
155*a3141fd3SAndroid Build Coastguard Worker }
156*a3141fd3SAndroid Build Coastguard Worker 
157*a3141fd3SAndroid Build Coastguard Worker /**
158*a3141fd3SAndroid Build Coastguard Worker  * Compare phone numbers a and b, return true if they're identical
159*a3141fd3SAndroid Build Coastguard Worker  * enough for caller ID purposes.
160*a3141fd3SAndroid Build Coastguard Worker  *
161*a3141fd3SAndroid Build Coastguard Worker  * - Compares from right to left
162*a3141fd3SAndroid Build Coastguard Worker  * - requires minimum characters to match
163*a3141fd3SAndroid Build Coastguard Worker  * - handles common trunk prefixes and international prefixes
164*a3141fd3SAndroid Build Coastguard Worker  *   (basically, everything except the Russian trunk prefix)
165*a3141fd3SAndroid Build Coastguard Worker  *
166*a3141fd3SAndroid Build Coastguard Worker  * Tolerates nulls
167*a3141fd3SAndroid Build Coastguard Worker  */
phone_number_compare_loose_with_minmatch(const char * a,const char * b,int min_match)168*a3141fd3SAndroid Build Coastguard Worker bool phone_number_compare_loose_with_minmatch(const char* a, const char* b, int min_match)
169*a3141fd3SAndroid Build Coastguard Worker {
170*a3141fd3SAndroid Build Coastguard Worker     int ia, ib;
171*a3141fd3SAndroid Build Coastguard Worker     int matched;
172*a3141fd3SAndroid Build Coastguard Worker     int numSeparatorCharsInA = 0;
173*a3141fd3SAndroid Build Coastguard Worker     int numSeparatorCharsInB = 0;
174*a3141fd3SAndroid Build Coastguard Worker 
175*a3141fd3SAndroid Build Coastguard Worker     if (a == NULL || b == NULL) {
176*a3141fd3SAndroid Build Coastguard Worker         return false;
177*a3141fd3SAndroid Build Coastguard Worker     }
178*a3141fd3SAndroid Build Coastguard Worker 
179*a3141fd3SAndroid Build Coastguard Worker     ia = strlen(a);
180*a3141fd3SAndroid Build Coastguard Worker     ib = strlen(b);
181*a3141fd3SAndroid Build Coastguard Worker     if (ia == 0 || ib == 0) {
182*a3141fd3SAndroid Build Coastguard Worker         return false;
183*a3141fd3SAndroid Build Coastguard Worker     }
184*a3141fd3SAndroid Build Coastguard Worker 
185*a3141fd3SAndroid Build Coastguard Worker     // Compare from right to left
186*a3141fd3SAndroid Build Coastguard Worker     ia--;
187*a3141fd3SAndroid Build Coastguard Worker     ib--;
188*a3141fd3SAndroid Build Coastguard Worker 
189*a3141fd3SAndroid Build Coastguard Worker     matched = 0;
190*a3141fd3SAndroid Build Coastguard Worker 
191*a3141fd3SAndroid Build Coastguard Worker     while (ia >= 0 && ib >=0) {
192*a3141fd3SAndroid Build Coastguard Worker         char ca, cb;
193*a3141fd3SAndroid Build Coastguard Worker         bool skipCmp = false;
194*a3141fd3SAndroid Build Coastguard Worker 
195*a3141fd3SAndroid Build Coastguard Worker         ca = a[ia];
196*a3141fd3SAndroid Build Coastguard Worker 
197*a3141fd3SAndroid Build Coastguard Worker         if (!isNonSeparator(ca)) {
198*a3141fd3SAndroid Build Coastguard Worker             ia--;
199*a3141fd3SAndroid Build Coastguard Worker             skipCmp = true;
200*a3141fd3SAndroid Build Coastguard Worker             numSeparatorCharsInA++;
201*a3141fd3SAndroid Build Coastguard Worker         }
202*a3141fd3SAndroid Build Coastguard Worker 
203*a3141fd3SAndroid Build Coastguard Worker         cb = b[ib];
204*a3141fd3SAndroid Build Coastguard Worker 
205*a3141fd3SAndroid Build Coastguard Worker         if (!isNonSeparator(cb)) {
206*a3141fd3SAndroid Build Coastguard Worker             ib--;
207*a3141fd3SAndroid Build Coastguard Worker             skipCmp = true;
208*a3141fd3SAndroid Build Coastguard Worker             numSeparatorCharsInB++;
209*a3141fd3SAndroid Build Coastguard Worker         }
210*a3141fd3SAndroid Build Coastguard Worker 
211*a3141fd3SAndroid Build Coastguard Worker         if (!skipCmp) {
212*a3141fd3SAndroid Build Coastguard Worker             if (cb != ca) {
213*a3141fd3SAndroid Build Coastguard Worker                 break;
214*a3141fd3SAndroid Build Coastguard Worker             }
215*a3141fd3SAndroid Build Coastguard Worker             ia--; ib--; matched++;
216*a3141fd3SAndroid Build Coastguard Worker         }
217*a3141fd3SAndroid Build Coastguard Worker     }
218*a3141fd3SAndroid Build Coastguard Worker 
219*a3141fd3SAndroid Build Coastguard Worker     if (matched < min_match) {
220*a3141fd3SAndroid Build Coastguard Worker         const int effectiveALen = strlen(a) - numSeparatorCharsInA;
221*a3141fd3SAndroid Build Coastguard Worker         const int effectiveBLen = strlen(b) - numSeparatorCharsInB;
222*a3141fd3SAndroid Build Coastguard Worker 
223*a3141fd3SAndroid Build Coastguard Worker         // if the number of dialable chars in a and b match, but the matched chars < min_match,
224*a3141fd3SAndroid Build Coastguard Worker         // treat them as equal (i.e. 404-04 and 40404)
225*a3141fd3SAndroid Build Coastguard Worker         if (effectiveALen == effectiveBLen && effectiveALen == matched) {
226*a3141fd3SAndroid Build Coastguard Worker             return true;
227*a3141fd3SAndroid Build Coastguard Worker         }
228*a3141fd3SAndroid Build Coastguard Worker 
229*a3141fd3SAndroid Build Coastguard Worker         return false;
230*a3141fd3SAndroid Build Coastguard Worker     }
231*a3141fd3SAndroid Build Coastguard Worker 
232*a3141fd3SAndroid Build Coastguard Worker     // At least one string has matched completely;
233*a3141fd3SAndroid Build Coastguard Worker     if (matched >= min_match && (ia < 0 || ib < 0)) {
234*a3141fd3SAndroid Build Coastguard Worker         return true;
235*a3141fd3SAndroid Build Coastguard Worker     }
236*a3141fd3SAndroid Build Coastguard Worker 
237*a3141fd3SAndroid Build Coastguard Worker     /*
238*a3141fd3SAndroid Build Coastguard Worker      * Now, what remains must be one of the following for a
239*a3141fd3SAndroid Build Coastguard Worker      * match:
240*a3141fd3SAndroid Build Coastguard Worker      *
241*a3141fd3SAndroid Build Coastguard Worker      *  - a '+' on one and a '00' or a '011' on the other
242*a3141fd3SAndroid Build Coastguard Worker      *  - a '0' on one and a (+,00)<country code> on the other
243*a3141fd3SAndroid Build Coastguard Worker      *     (for this, a '0' and a '00' prefix would have succeeded above)
244*a3141fd3SAndroid Build Coastguard Worker      */
245*a3141fd3SAndroid Build Coastguard Worker 
246*a3141fd3SAndroid Build Coastguard Worker     if (matchIntlPrefix(a, ia + 1) && matchIntlPrefix(b, ib + 1)) {
247*a3141fd3SAndroid Build Coastguard Worker         return true;
248*a3141fd3SAndroid Build Coastguard Worker     }
249*a3141fd3SAndroid Build Coastguard Worker 
250*a3141fd3SAndroid Build Coastguard Worker     if (matchTrunkPrefix(a, ia + 1) && matchIntlPrefixAndCC(b, ib + 1)) {
251*a3141fd3SAndroid Build Coastguard Worker         return true;
252*a3141fd3SAndroid Build Coastguard Worker     }
253*a3141fd3SAndroid Build Coastguard Worker 
254*a3141fd3SAndroid Build Coastguard Worker     if (matchTrunkPrefix(b, ib + 1) && matchIntlPrefixAndCC(a, ia + 1)) {
255*a3141fd3SAndroid Build Coastguard Worker         return true;
256*a3141fd3SAndroid Build Coastguard Worker     }
257*a3141fd3SAndroid Build Coastguard Worker 
258*a3141fd3SAndroid Build Coastguard Worker     /*
259*a3141fd3SAndroid Build Coastguard Worker      * Last resort: if the number of unmatched characters on both sides is less than or equal
260*a3141fd3SAndroid Build Coastguard Worker      * to the length of the longest country code and only one number starts with a + accept
261*a3141fd3SAndroid Build Coastguard Worker      * the match. This is because some countries like France and Russia have an extra prefix
262*a3141fd3SAndroid Build Coastguard Worker      * digit that is used when dialing locally in country that does not show up when you dial
263*a3141fd3SAndroid Build Coastguard Worker      * the number using the country code. In France this prefix digit is used to determine
264*a3141fd3SAndroid Build Coastguard Worker      * which land line carrier to route the call over.
265*a3141fd3SAndroid Build Coastguard Worker      */
266*a3141fd3SAndroid Build Coastguard Worker     bool aPlusFirst = (*a == '+');
267*a3141fd3SAndroid Build Coastguard Worker     bool bPlusFirst = (*b == '+');
268*a3141fd3SAndroid Build Coastguard Worker     bool aIgnoreUnmatched = aPlusFirst && (ia - ib) >= 0 && (ia - ib) <= 1;
269*a3141fd3SAndroid Build Coastguard Worker     bool bIgnoreUnmatched = bPlusFirst && (ib - ia) >= 0 && (ib - ia) <= 1;
270*a3141fd3SAndroid Build Coastguard Worker     if (ia < 4 && ib < 4 && (aIgnoreUnmatched || bIgnoreUnmatched) && !(aPlusFirst && bPlusFirst)) {
271*a3141fd3SAndroid Build Coastguard Worker         return true;
272*a3141fd3SAndroid Build Coastguard Worker     }
273*a3141fd3SAndroid Build Coastguard Worker 
274*a3141fd3SAndroid Build Coastguard Worker     return false;
275*a3141fd3SAndroid Build Coastguard Worker }
276*a3141fd3SAndroid Build Coastguard Worker 
phone_number_compare_loose(const char * a,const char * b)277*a3141fd3SAndroid Build Coastguard Worker bool phone_number_compare_loose(const char* a, const char* b)
278*a3141fd3SAndroid Build Coastguard Worker {
279*a3141fd3SAndroid Build Coastguard Worker     return phone_number_compare_loose_with_minmatch(a, b, MIN_MATCH);
280*a3141fd3SAndroid Build Coastguard Worker }
281*a3141fd3SAndroid Build Coastguard Worker 
282*a3141fd3SAndroid Build Coastguard Worker }  // namespace android
283