blob: efbda77d52b19da51a80be6547de83703abd2075 [file] [log] [blame]
/* //device/vmlibs-android/com.android.internal.telephony/PhoneNumberUtils.java
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include <string.h>
namespace android {
/*
* Special characters
*
* (See "What is a phone number?" doc)
* 'p' --- GSM pause character, same as comma
* 'n' --- GSM wild character
* 'w' --- GSM wait character
*/
static char PAUSE = 'p';
static char WAIT = 'w';
static char WILD = 'n';
static int MIN_MATCH = 5;
/** True if c is ISO-LATIN characters 0-9 */
static bool isISODigit (char c)
{
return c >= '0' && c <= '9';
}
/** True if c is ISO-LATIN characters 0-9, *, # , +, WILD */
static bool isDialable(char c)
{
return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == WILD;
}
/** True if c is ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE */
static bool isNonSeparator(char c)
{
return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+'
|| c == WILD || c == WAIT || c == PAUSE;
}
/**
* Phone numbers are stored in "lookup" form in the database
* as reversed strings to allow for caller ID lookup
*
* This method takes a phone number and makes a valid SQL "LIKE"
* string that will match the lookup form
*
*/
/** all of a up to len must be an international prefix or
* separators/non-dialing digits
*/
static bool matchIntlPrefix(const char* a, int len)
{
/* '([^0-9*#+pwn]\+[^0-9*#+pwn] | [^0-9*#+pwn]0(0|11)[^0-9*#+pwn] )$' */
/* 0 1 2 3 45 */
int state = 0;
for (int i = 0 ; i < len ; i++) {
char c = a[i];
switch (state) {
case 0:
if (c == '+') state = 1;
else if (c == '0') state = 2;
else if (isNonSeparator(c)) return false;
break;
case 2:
if (c == '0') state = 3;
else if (c == '1') state = 4;
else if (isNonSeparator(c)) return false;
break;
case 4:
if (c == '1') state = 5;
else if (isNonSeparator(c)) return false;
break;
default:
if (isNonSeparator(c)) return false;
break;
}
}
return state == 1 || state == 3 || state == 5;
}
/** all of 'a' up to len must match non-US trunk prefix ('0') */
static bool matchTrunkPrefix(const char* a, int len)
{
bool found;
found = false;
for (int i = 0 ; i < len ; i++) {
char c = a[i];
if (c == '0' && !found) {
found = true;
} else if (isNonSeparator(c)) {
return false;
}
}
return found;
}
/** all of 'a' up to len must be a (+|00|011)country code)
* We're fast and loose with the country code. Any \d{1,3} matches */
static bool matchIntlPrefixAndCC(const char* a, int len)
{
/* [^0-9*#+pwn]*(\+|0(0|11)\d\d?\d? [^0-9*#+pwn] $ */
/* 0 1 2 3 45 6 7 8 */
int state = 0;
for (int i = 0 ; i < len ; i++ ) {
char c = a[i];
switch (state) {
case 0:
if (c == '+') state = 1;
else if (c == '0') state = 2;
else if (isNonSeparator(c)) return false;
break;
case 2:
if (c == '0') state = 3;
else if (c == '1') state = 4;
else if (isNonSeparator(c)) return false;
break;
case 4:
if (c == '1') state = 5;
else if (isNonSeparator(c)) return false;
break;
case 1:
case 3:
case 5:
if (isISODigit(c)) state = 6;
else if (isNonSeparator(c)) return false;
break;
case 6:
case 7:
if (isISODigit(c)) state++;
else if (isNonSeparator(c)) return false;
break;
default:
if (isNonSeparator(c)) return false;
}
}
return state == 6 || state == 7 || state == 8;
}
/** or -1 if both are negative */
static int minPositive(int a, int b)
{
if (a >= 0 && b >= 0) {
return (a < b) ? a : b;
} else if (a >= 0) { /* && b < 0 */
return a;
} else if (b >= 0) { /* && a < 0 */
return b;
} else { /* a < 0 && b < 0 */
return -1;
}
}
/**
* Return the offset into a of the first appearance of b, or -1 if there
* is no such character in a.
*/
static int indexOf(const char *a, char b) {
char *ix = strchr(a, b);
if (ix == NULL)
return -1;
else
return ix - a;
}
/** index of the last character of the network portion
* (eg anything after is a post-dial string)
*/
static int indexOfLastNetworkChar(const char* a)
{
int pIndex, wIndex;
int origLength;
int trimIndex;
origLength = strlen(a);
pIndex = indexOf(a, PAUSE);
wIndex = indexOf(a, WAIT);
trimIndex = minPositive(pIndex, wIndex);
if (trimIndex < 0) {
return origLength - 1;
} else {
return trimIndex - 1;
}
}
/**
* Compare phone numbers a and b, return true if they're identical
* enough for caller ID purposes.
*
* - Compares from right to left
* - requires MIN_MATCH (5) characters to match
* - handles common trunk prefixes and international prefixes
* (basically, everything except the Russian trunk prefix)
*
* Tolerates nulls
*/
bool phone_number_compare(const char* a, const char* b)
{
int ia, ib;
int matched;
if (a == NULL || b == NULL)
{
return false;
}
if (strlen(a) == 0 || strlen(b) == 0)
{
return false;
}
ia = indexOfLastNetworkChar(a);
ib = indexOfLastNetworkChar(b);
matched = 0;
while (ia >= 0 && ib >=0) {
char ca, cb;
bool skipCmp = false;
ca = a[ia];
if (!isDialable(ca)) {
ia--;
skipCmp = true;
}
cb = b[ib];
if (!isDialable(cb)) {
ib--;
skipCmp = true;
}
if (!skipCmp) {
if (cb != ca && ca != WILD && cb != WILD) {
break;
}
ia--; ib--; matched++;
}
}
if (matched < MIN_MATCH) {
int aLen = strlen(a);
// if the input strings match, but their lengths < MIN_MATCH,
// treat them as equal.
if (aLen == strlen(b) && aLen == matched) {
return true;
}
return false;
}
// At least one string has matched completely;
if (matched >= MIN_MATCH && (ia < 0 || ib < 0)) {
return true;
}
/*
* Now, what remains must be one of the following for a
* match:
*
* - a '+' on one and a '00' or a '011' on the other
* - a '0' on one and a (+,00)<country code> on the other
* (for this, a '0' and a '00' prefix would have succeeded above)
*/
if (matchIntlPrefix(a, ia + 1)
&& matchIntlPrefix (b, ib +1)
) {
return true;
}
if (matchTrunkPrefix(a, ia + 1)
&& matchIntlPrefixAndCC(b, ib +1)
) {
return true;
}
if (matchTrunkPrefix(b, ib + 1)
&& matchIntlPrefixAndCC(a, ia +1)
) {
return true;
}
return false;
}
} // namespace android