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