mirror of
https://github.com/danog/Telegram.git
synced 2024-12-12 09:29:55 +01:00
755 lines
27 KiB
Objective-C
755 lines
27 KiB
Objective-C
#import "RMPhoneFormat.h"
|
|
|
|
@interface PhoneRule : NSObject
|
|
|
|
@property (nonatomic, assign) int minVal;
|
|
@property (nonatomic, assign) int maxVal;
|
|
@property (nonatomic, assign) int byte8;
|
|
@property (nonatomic, assign) int maxLen;
|
|
@property (nonatomic, assign) int otherFlag;
|
|
@property (nonatomic, assign) int prefixLen;
|
|
@property (nonatomic, assign) int flag12;
|
|
@property (nonatomic, assign) int flag13;
|
|
@property (nonatomic) NSString *format;
|
|
#ifdef DEBUG
|
|
@property (nonatomic) NSSet *countries;
|
|
@property (nonatomic) NSString *callingCode;
|
|
@property (nonatomic, assign) int matchLen;
|
|
#endif
|
|
|
|
- (NSString *)format:(NSString *)str intlPrefix:(NSString *)intlPrefix trunkPrefix:(NSString *)trunkPrefix;
|
|
|
|
@end
|
|
|
|
@implementation PhoneRule
|
|
|
|
@synthesize minVal = _minVal;
|
|
@synthesize maxVal = _maxVal;
|
|
@synthesize byte8 = _byte8;
|
|
@synthesize maxLen = _maxLen;
|
|
@synthesize otherFlag = _otherFlag;
|
|
@synthesize prefixLen = _prefixLen;
|
|
@synthesize flag12 = _flag12;
|
|
@synthesize flag13 = _flag13;
|
|
@synthesize format = _format;
|
|
|
|
#ifdef DEBUG
|
|
@synthesize countries = _countries;
|
|
@synthesize callingCode = _callingCode;
|
|
@synthesize matchLen = _matchLen;
|
|
#endif
|
|
|
|
- (NSString *)format:(NSString *)str intlPrefix:(NSString *)intlPrefix trunkPrefix:(NSString *)trunkPrefix {
|
|
BOOL hadC = NO;
|
|
BOOL hadN = NO;
|
|
BOOL hasOpen = NO;
|
|
int spot = 0;
|
|
NSMutableString *res = [[NSMutableString alloc] initWithCapacity:20];
|
|
for (int i = 0; i < (int)[self.format length]; i++) {
|
|
unichar ch = [self.format characterAtIndex:i];
|
|
switch (ch) {
|
|
case 'c':
|
|
// Add international prefix if there is one.
|
|
hadC = YES;
|
|
if (intlPrefix != nil) {
|
|
[res appendString:intlPrefix];
|
|
}
|
|
break;
|
|
case 'n':
|
|
// Add trunk prefix if there is one.
|
|
hadN = YES;
|
|
if (trunkPrefix != nil) {
|
|
[res appendString:trunkPrefix];
|
|
}
|
|
break;
|
|
case '#':
|
|
// Add next digit from number. If there aren't enough digits left then do nothing unless we need to
|
|
// space-fill a pair of parenthesis.
|
|
if (spot < (int)[str length]) {
|
|
[res appendString:[str substringWithRange:NSMakeRange(spot, 1)]];
|
|
spot++;
|
|
} else if (hasOpen) {
|
|
[res appendString:@" "];
|
|
}
|
|
break;
|
|
case '(':
|
|
// Flag we found an open paren so it can be space-filled. But only do so if we aren't beyond the
|
|
// end of the number.
|
|
if (spot < (int)[str length]) {
|
|
hasOpen = YES;
|
|
}
|
|
// fall through
|
|
default: // rest like ) and -
|
|
// Don't show space after n if no trunkPrefix or after c if no intlPrefix
|
|
if (!(ch == ' ' && i > 0 && (([self.format characterAtIndex:i - 1] == 'n' && trunkPrefix == nil) || ([self.format characterAtIndex:i - 1] == 'c' && intlPrefix == nil)))) {
|
|
// Only show punctuation if not beyond the end of the supplied number.
|
|
// The only exception is to show a close paren if we had found
|
|
if (spot < (int)[str length] || (hasOpen && ch == ')')) {
|
|
[res appendString:[self.format substringWithRange:NSMakeRange(i, 1)]];
|
|
if (ch == ')') {
|
|
hasOpen = NO; // close it
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Not all format strings have a 'c' or 'n' in them. If we have an international prefix or a trunk prefix but the
|
|
// format string doesn't explictly say where to put it then simply add it to the beginning.
|
|
if (intlPrefix != nil && !hadC) {
|
|
[res insertString:[NSString stringWithFormat:@"%@ ", intlPrefix] atIndex:0];
|
|
} else if (trunkPrefix != nil && !hadN) {
|
|
[res insertString:trunkPrefix atIndex:0];
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
- (NSString *)description {
|
|
#ifdef DEBUG
|
|
return [NSString stringWithFormat:@"Rule: { countries: %@, calling code: %@, matchlen: %d, minVal: %d, maxVal: %d, byte8: %d, maxLen: %d, nFlag: %d, prefixLen: %d, flag12: %d, flag13: %d, format: %@ }", self.countries, self.callingCode, self.matchLen, self.minVal, self.maxVal, self.byte8, self.maxLen, self.otherFlag, self.prefixLen, self.flag12, self.flag13, self.format];
|
|
#else
|
|
return [NSString stringWithFormat:@"Rule: { minVal: %d, maxVal: %d, byte8: %d, maxLen: %d, nFlag: %d, prefixLen: %d, flag12: %d, flag13: %d, format: %@ }", self.minVal, self.maxVal, self.byte8, self.maxLen, self.otherFlag, self.prefixLen, self.flag12, self.flag13, self.format];
|
|
#endif
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
|
|
@interface RuleSet : NSObject
|
|
|
|
@property (nonatomic, assign) int matchLen;
|
|
@property (nonatomic) NSMutableArray *rules;
|
|
|
|
- (NSString *)format:(NSString *)str intlPrefix:(NSString *)intlPrefix trunkPrefix:(NSString *)trunkPrefix prefixRequired:(BOOL)prefixRequired;
|
|
|
|
@end
|
|
|
|
@implementation RuleSet
|
|
|
|
@synthesize matchLen = _matchLen;
|
|
@synthesize rules = _rules;
|
|
|
|
- (NSString *)format:(NSString *)str intlPrefix:(NSString *)intlPrefix trunkPrefix:(NSString *)trunkPrefix prefixRequired:(BOOL)prefixRequired {
|
|
if ((int)[str length] >= self.matchLen)
|
|
{
|
|
NSString *begin = [str substringToIndex:self.matchLen];
|
|
int val = [begin intValue];
|
|
for (PhoneRule *rule in self.rules)
|
|
{
|
|
if (val >= rule.minVal && val <= rule.maxVal && (int)[str length] <= rule.maxLen)
|
|
{
|
|
if (prefixRequired)
|
|
{
|
|
if (
|
|
((rule.flag12 & 0x03) == 0 &&
|
|
trunkPrefix == nil &&
|
|
intlPrefix == nil) ||
|
|
(trunkPrefix != nil && (rule.flag12 & 0x01)) ||
|
|
(intlPrefix != nil && (rule.flag12 & 0x02))
|
|
)
|
|
{
|
|
return [rule format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((trunkPrefix == nil && intlPrefix == nil) || (trunkPrefix != nil && (rule.flag12 & 0x01)) || (intlPrefix != nil && (rule.flag12 & 0x02)))
|
|
{
|
|
return [rule format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!prefixRequired)
|
|
{
|
|
if (intlPrefix != nil)
|
|
{
|
|
for (PhoneRule *rule in self.rules)
|
|
{
|
|
if (val >= rule.minVal && val <= rule.maxVal && (int)[str length] <= rule.maxLen)
|
|
{
|
|
if (trunkPrefix == nil || (rule.flag12 & 0x01))
|
|
{
|
|
return [rule format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix];
|
|
}
|
|
}
|
|
}
|
|
} else if (trunkPrefix != nil)
|
|
{
|
|
for (PhoneRule *rule in self.rules)
|
|
{
|
|
if (val >= rule.minVal && val <= rule.maxVal && (int)[str length] <= rule.maxLen)
|
|
{
|
|
if (intlPrefix == nil || (rule.flag12 & 0x02))
|
|
{
|
|
return [rule format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil; // no match found
|
|
} else {
|
|
return nil; // not long enough to compare
|
|
}
|
|
}
|
|
|
|
- (NSString *)description {
|
|
NSMutableString *res = [NSMutableString stringWithCapacity:100];
|
|
[res appendFormat:@"RuleSet: { matchLen: %d, rules: %@ }", self.matchLen, self.rules];
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
|
|
@interface CallingCodeInfo : NSObject
|
|
|
|
@property (nonatomic) NSSet *countries;
|
|
@property (nonatomic) NSString *callingCode;
|
|
@property (nonatomic) NSMutableArray *trunkPrefixes;
|
|
@property (nonatomic) NSMutableArray *intlPrefixes;
|
|
@property (nonatomic) NSMutableArray *ruleSets;
|
|
@property (nonatomic) NSMutableArray *formatStrings;
|
|
|
|
- (NSString *)matchingAccessCode:(NSString *)str;
|
|
- (NSString *)format:(NSString *)str;
|
|
|
|
@end
|
|
|
|
@implementation CallingCodeInfo
|
|
|
|
@synthesize countries = _countries;
|
|
@synthesize callingCode = _callingCode;
|
|
@synthesize trunkPrefixes = _trunkPrefixes;
|
|
@synthesize intlPrefixes = _intlPrefixes;
|
|
@synthesize ruleSets = _ruleSets;
|
|
@synthesize formatStrings = _formatStrings;
|
|
|
|
- (NSString *)matchingAccessCode:(NSString *)str {
|
|
for (NSString *code in self.intlPrefixes) {
|
|
if ([str hasPrefix:code]) {
|
|
return code;
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *)matchingTrunkCode:(NSString *)str {
|
|
for (NSString *code in self.trunkPrefixes) {
|
|
if ([str hasPrefix:code]) {
|
|
return code;
|
|
}
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
- (NSString *)format:(NSString *)orig
|
|
{
|
|
NSString *str = orig;
|
|
NSString *trunkPrefix = nil;
|
|
NSString *intlPrefix = nil;
|
|
if ([str hasPrefix:self.callingCode])
|
|
{
|
|
intlPrefix = self.callingCode;
|
|
str = [str substringFromIndex:[intlPrefix length]];
|
|
}
|
|
else
|
|
{
|
|
NSString *trunk = [self matchingTrunkCode:str];
|
|
if (trunk)
|
|
{
|
|
trunkPrefix = trunk;
|
|
str = [str substringFromIndex:[trunkPrefix length]];
|
|
}
|
|
}
|
|
|
|
for (RuleSet *set in self.ruleSets)
|
|
{
|
|
NSString *phone = [set format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix prefixRequired:YES];
|
|
if (phone)
|
|
{
|
|
return phone;
|
|
}
|
|
}
|
|
|
|
for (RuleSet *set in self.ruleSets)
|
|
{
|
|
NSString *phone = [set format:str intlPrefix:intlPrefix trunkPrefix:trunkPrefix prefixRequired:NO];
|
|
if (phone)
|
|
{
|
|
return phone;
|
|
}
|
|
}
|
|
|
|
if (intlPrefix != nil && [str length])
|
|
{
|
|
return [NSString stringWithFormat:@"%@ %@", intlPrefix, str];
|
|
}
|
|
|
|
return orig;
|
|
}
|
|
|
|
- (NSString *)description {
|
|
NSMutableString *res = [NSMutableString stringWithCapacity:100];
|
|
[res appendFormat:@"CountryInfo { countries: %@, code: %@, trunkPrefixes: %@, intlPrefixes: %@", self.countries, self.callingCode, self.trunkPrefixes, self.intlPrefixes];
|
|
[res appendFormat:@", rule sets: %@ }", self.ruleSets];
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
@end
|
|
|
|
static NSCharacterSet *phoneChars = nil;
|
|
#ifdef DEBUG
|
|
static NSMutableDictionary *extra1CallingCodes = nil;
|
|
static NSMutableDictionary *extra2CallingCodes = nil;
|
|
static NSMutableDictionary *extra3CallingCodes = nil;
|
|
static NSMutableDictionary *flagRules = nil;
|
|
#endif
|
|
|
|
@implementation RMPhoneFormat {
|
|
NSData *_data;
|
|
NSString *_defaultCountry;
|
|
NSString *_defaultCallingCode;
|
|
NSMutableDictionary *_callingCodeOffsets;
|
|
NSMutableDictionary *_callingCodeCountries;
|
|
NSMutableDictionary *_callingCodeData;
|
|
NSMutableDictionary *_countryCallingCode;
|
|
}
|
|
|
|
+ (void)initialize {
|
|
phoneChars = [NSCharacterSet characterSetWithCharactersInString:@"0123456789+*#"];
|
|
|
|
#ifdef DEBUG
|
|
extra1CallingCodes = [[NSMutableDictionary alloc] init];
|
|
extra2CallingCodes = [[NSMutableDictionary alloc] init];
|
|
extra3CallingCodes = [[NSMutableDictionary alloc] init];
|
|
flagRules = [[NSMutableDictionary alloc] init];
|
|
#endif
|
|
}
|
|
|
|
+ (NSString *)strip:(NSString *)str {
|
|
NSMutableString *res = [NSMutableString stringWithString:str];
|
|
for (int i = (int)[res length] - 1; i >= 0; i--) {
|
|
if (![phoneChars characterIsMember:[res characterAtIndex:i]]) {
|
|
[res deleteCharactersInRange:NSMakeRange(i, 1)];
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
+ (RMPhoneFormat *)instance {
|
|
static RMPhoneFormat *instance = nil;
|
|
static dispatch_once_t predicate = 0;
|
|
|
|
dispatch_once(&predicate, ^{ instance = [self new]; });
|
|
|
|
return instance;
|
|
}
|
|
|
|
- (id)init {
|
|
self = [self initWithDefaultCountry:nil];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id)initWithDefaultCountry:(NSString *)countryCode {
|
|
if ((self = [super init])) {
|
|
_data = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"PhoneFormats" ofType:@"dat"]];
|
|
NSAssert(_data, @"The file PhoneFormats.dat is not in the resource bundle. See the README.");
|
|
|
|
if (countryCode.length) {
|
|
_defaultCountry = countryCode;
|
|
} else {
|
|
NSLocale *loc = [NSLocale currentLocale];
|
|
_defaultCountry = [[loc objectForKey:NSLocaleCountryCode] lowercaseString];
|
|
}
|
|
_callingCodeOffsets = [[NSMutableDictionary alloc] initWithCapacity:255];
|
|
_callingCodeCountries = [[NSMutableDictionary alloc] initWithCapacity:255];
|
|
_callingCodeData = [[NSMutableDictionary alloc] initWithCapacity:10];
|
|
_countryCallingCode = [[NSMutableDictionary alloc] initWithCapacity:255];
|
|
|
|
[self parseDataHeader];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (NSString *)defaultCallingCode {
|
|
return [self callingCodeForCountryCode:_defaultCountry];
|
|
}
|
|
|
|
- (NSString *)callingCodeForCountryCode:(NSString *)countryCode {
|
|
return [_countryCallingCode objectForKey:[countryCode lowercaseString]];
|
|
}
|
|
|
|
- (NSSet *)countriesForCallingCode:(NSString *)callingCode {
|
|
if ([callingCode hasPrefix:@"+"]) {
|
|
callingCode = [callingCode substringFromIndex:1];
|
|
}
|
|
|
|
return [_callingCodeCountries objectForKey:callingCode];
|
|
}
|
|
|
|
- (CallingCodeInfo *)findCallingCodeInfo:(NSString *)str {
|
|
CallingCodeInfo *res = nil;
|
|
for (int i = 0; i < 3; i++) {
|
|
if (i < (int)[str length]) {
|
|
res = [self callingCodeInfo:[str substringToIndex:i + 1]];
|
|
if (res) {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
- (NSString *)format:(NSString *)orig implicitPlus:(bool)implicitPlus
|
|
{
|
|
NSString *str = [RMPhoneFormat strip:orig];
|
|
|
|
bool hasPlusPrefix = [str hasPrefix:@"+"];
|
|
|
|
if ([str hasPrefix:@"+"] || implicitPlus)
|
|
{
|
|
NSString *rest = hasPlusPrefix ? [str substringFromIndex:1] : str;
|
|
|
|
CallingCodeInfo *info = [self findCallingCodeInfo:rest];
|
|
if (info)
|
|
{
|
|
NSString *phone = [info format:rest];
|
|
return [@"+" stringByAppendingString:phone];
|
|
} else
|
|
{
|
|
return orig;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CallingCodeInfo *info = [self callingCodeInfo:_defaultCallingCode];
|
|
if (info == nil)
|
|
{
|
|
return orig;
|
|
}
|
|
|
|
NSString *accessCode = [info matchingAccessCode:str];
|
|
if (accessCode)
|
|
{
|
|
NSString *rest = [str substringFromIndex:[accessCode length]];
|
|
NSString *phone = rest;
|
|
|
|
CallingCodeInfo *info2 = [self findCallingCodeInfo:rest];
|
|
if (info2)
|
|
{
|
|
phone = [info2 format:rest];
|
|
}
|
|
|
|
if ([phone length] == 0)
|
|
{
|
|
return accessCode;
|
|
}
|
|
else
|
|
{
|
|
return [NSString stringWithFormat:@"%@ %@", accessCode, phone];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NSString *phone = [info format:str];
|
|
|
|
return phone;
|
|
}
|
|
}
|
|
|
|
return orig;
|
|
}
|
|
|
|
- (uint32_t)value32:(NSUInteger)offset {
|
|
if (offset + 4 <= [_data length]) {
|
|
return OSReadLittleInt32([_data bytes], offset);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
- (int)value16:(NSUInteger)offset {
|
|
if (offset + 2 <= [_data length]) {
|
|
return OSReadLittleInt16([_data bytes], offset);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
- (int)value16BE:(NSUInteger)offset {
|
|
if (offset + 2 <= [_data length]) {
|
|
return OSReadBigInt16([_data bytes], offset);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
- (CallingCodeInfo *)callingCodeInfo:(NSString *)callingCode
|
|
{
|
|
CallingCodeInfo *res = [_callingCodeData objectForKey:callingCode];
|
|
if (res == nil)
|
|
{
|
|
NSNumber *num = [_callingCodeOffsets objectForKey:callingCode];
|
|
if (num)
|
|
{
|
|
const uint8_t *bytes = [_data bytes];
|
|
uint32_t start = (int)[num longValue];
|
|
uint32_t offset = start;
|
|
res = [[CallingCodeInfo alloc] init];
|
|
res.callingCode = callingCode;
|
|
res.countries = [_callingCodeCountries objectForKey:callingCode];
|
|
[_callingCodeData setObject:res forKey:callingCode];
|
|
|
|
uint16_t block1Len = (uint16_t)[self value16:offset];
|
|
offset += 2;
|
|
#ifdef DEBUG
|
|
uint16_t extra1 = (uint16_t)[self value16:offset];
|
|
#endif
|
|
offset += 2;
|
|
uint16_t block2Len = (uint16_t)[self value16:offset];
|
|
offset += 2;
|
|
#ifdef DEBUG
|
|
uint16_t extra2 = (uint16_t)[self value16:offset];
|
|
#endif
|
|
offset += 2;
|
|
uint16_t setCnt = (uint16_t)[self value16:offset];
|
|
offset += 2;
|
|
#ifdef DEBUG
|
|
uint16_t extra3 = (uint16_t)[self value16:offset];
|
|
#endif
|
|
offset += 2;
|
|
|
|
#ifdef DEBUG
|
|
if (extra1) {
|
|
NSMutableArray *vals = [extra1CallingCodes objectForKey:[NSNumber numberWithInt:extra1]];
|
|
if (!vals) {
|
|
vals = [[NSMutableArray alloc] init];
|
|
[extra1CallingCodes setObject:vals forKey:[NSNumber numberWithInt:extra1]];
|
|
}
|
|
[vals addObject:res];
|
|
}
|
|
if (extra2) {
|
|
NSMutableArray *vals = [extra2CallingCodes objectForKey:[NSNumber numberWithInt:extra2]];
|
|
if (!vals) {
|
|
vals = [[NSMutableArray alloc] init];
|
|
[extra2CallingCodes setObject:vals forKey:[NSNumber numberWithInt:extra2]];
|
|
}
|
|
[vals addObject:res];
|
|
}
|
|
if (extra3) {
|
|
NSMutableArray *vals = [extra3CallingCodes objectForKey:[NSNumber numberWithInt:extra3]];
|
|
if (!vals) {
|
|
vals = [[NSMutableArray alloc] init];
|
|
[extra3CallingCodes setObject:vals forKey:[NSNumber numberWithInt:extra3]];
|
|
}
|
|
[vals addObject:res];
|
|
}
|
|
#endif
|
|
|
|
NSMutableArray *strs = [NSMutableArray arrayWithCapacity:5];
|
|
NSString *str;
|
|
while ([(str = [NSString stringWithCString:(char *)bytes + offset encoding:NSUTF8StringEncoding]) length]) {
|
|
[strs addObject:str];
|
|
offset += [str length] + 1;
|
|
}
|
|
res.trunkPrefixes = strs;
|
|
offset++; // skip NULL
|
|
|
|
strs = [NSMutableArray arrayWithCapacity:5];
|
|
while ([(str = [NSString stringWithCString:(char *)bytes + offset encoding:NSUTF8StringEncoding]) length]) {
|
|
[strs addObject:str];
|
|
offset += [str length] + 1;
|
|
}
|
|
res.intlPrefixes = strs;
|
|
|
|
NSMutableArray *ruleSets = [NSMutableArray arrayWithCapacity:setCnt];
|
|
offset = start + block1Len; // Start of rule sets
|
|
for (int s = 0; s < setCnt; s++) {
|
|
RuleSet *ruleSet = [[RuleSet alloc] init];
|
|
int matchCnt = [self value16:offset];
|
|
ruleSet.matchLen = matchCnt;
|
|
offset += 2;
|
|
int ruleCnt = [self value16:offset];
|
|
offset += 2;
|
|
NSMutableArray *rules = [NSMutableArray arrayWithCapacity:ruleCnt];
|
|
for (int r = 0; r < ruleCnt; r++) {
|
|
PhoneRule *rule = [[PhoneRule alloc] init];
|
|
rule.minVal = [self value32:offset];
|
|
offset += 4;
|
|
rule.maxVal = [self value32:offset];
|
|
offset += 4;
|
|
rule.byte8 = (int)bytes[offset++];
|
|
rule.maxLen = (int)bytes[offset++];
|
|
rule.otherFlag = (int)bytes[offset++];
|
|
rule.prefixLen = (int)bytes[offset++];
|
|
rule.flag12 = (int)bytes[offset++];
|
|
rule.flag13 = (int)bytes[offset++];
|
|
uint16_t strOffset = (uint16_t)[self value16:offset];
|
|
offset += 2;
|
|
rule.format = [NSString stringWithCString:(char *)bytes + start + block1Len + block2Len + strOffset encoding:NSUTF8StringEncoding];
|
|
// Several formats contain [[9]] or [[8]]. Using the Contacts app as a test, I can find no use
|
|
// for these. Do they mean "optional"? They don't seem to have any use. This code strips out
|
|
// anything in [[..]]
|
|
NSRange openPos = [rule.format rangeOfString:@"[["];
|
|
if (openPos.location != NSNotFound) {
|
|
NSRange closePos = [rule.format rangeOfString:@"]]"];
|
|
rule.format = [NSString stringWithFormat:@"%@%@", [rule.format substringToIndex:openPos.location], [rule.format substringFromIndex:closePos.location + closePos.length]];
|
|
}
|
|
|
|
[rules addObject:rule];
|
|
#ifdef DEBUG
|
|
rule.countries = res.countries;
|
|
rule.callingCode = res.callingCode;
|
|
rule.matchLen = matchCnt;
|
|
if (rule.byte8) {
|
|
NSMutableDictionary *data = [flagRules objectForKey:@"byte8"];
|
|
if (!data) {
|
|
data = [[NSMutableDictionary alloc] init];
|
|
[flagRules setObject:data forKey:@"byte8"];
|
|
}
|
|
NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.byte8]];
|
|
if (!list) {
|
|
list = [[NSMutableArray alloc] init];
|
|
[data setObject:list forKey:[NSNumber numberWithInt:rule.byte8]];
|
|
}
|
|
|
|
[list addObject:rule];
|
|
}
|
|
if (rule.prefixLen) {
|
|
NSMutableDictionary *data = [flagRules objectForKey:@"prefixLen"];
|
|
if (!data) {
|
|
data = [[NSMutableDictionary alloc] init];
|
|
[flagRules setObject:data forKey:@"prefixLen"];
|
|
}
|
|
NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.prefixLen]];
|
|
if (!list) {
|
|
list = [[NSMutableArray alloc] init];
|
|
[data setObject:list forKey:[NSNumber numberWithInt:rule.prefixLen]];
|
|
}
|
|
|
|
[list addObject:rule];
|
|
}
|
|
if (rule.otherFlag) {
|
|
NSMutableDictionary *data = [flagRules objectForKey:@"otherFlag"];
|
|
if (!data) {
|
|
data = [[NSMutableDictionary alloc] init];
|
|
[flagRules setObject:data forKey:@"otherFlag"];
|
|
}
|
|
NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.otherFlag]];
|
|
if (!list) {
|
|
list = [[NSMutableArray alloc] init];
|
|
[data setObject:list forKey:[NSNumber numberWithInt:rule.otherFlag]];
|
|
}
|
|
|
|
[list addObject:rule];
|
|
}
|
|
if (rule.flag12) {
|
|
NSMutableDictionary *data = [flagRules objectForKey:@"flag12"];
|
|
if (!data) {
|
|
data = [[NSMutableDictionary alloc] init];
|
|
[flagRules setObject:data forKey:@"flag12"];
|
|
}
|
|
NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.flag12]];
|
|
if (!list) {
|
|
list = [[NSMutableArray alloc] init];
|
|
[data setObject:list forKey:[NSNumber numberWithInt:rule.flag12]];
|
|
}
|
|
|
|
[list addObject:rule];
|
|
}
|
|
if (rule.flag13) {
|
|
NSMutableDictionary *data = [flagRules objectForKey:@"flag13"];
|
|
if (!data) {
|
|
data = [[NSMutableDictionary alloc] init];
|
|
[flagRules setObject:data forKey:@"flag13"];
|
|
}
|
|
NSMutableArray *list = [data objectForKey:[NSNumber numberWithInt:rule.flag13]];
|
|
if (!list) {
|
|
list = [[NSMutableArray alloc] init];
|
|
[data setObject:list forKey:[NSNumber numberWithInt:rule.flag13]];
|
|
}
|
|
|
|
[list addObject:rule];
|
|
}
|
|
#endif
|
|
}
|
|
ruleSet.rules = rules;
|
|
[ruleSets addObject:ruleSet];
|
|
}
|
|
res.ruleSets = ruleSets;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
- (void)parseDataHeader {
|
|
int count = [self value32:0];
|
|
uint32_t base = count * 12 + 4;
|
|
const void *bytes = [_data bytes];
|
|
NSUInteger spot = 4;
|
|
for (int i = 0; i < count; i++) {
|
|
NSString *callingCode = [NSString stringWithCString:bytes + spot encoding:NSUTF8StringEncoding];
|
|
spot += 4;
|
|
NSString *country = [NSString stringWithCString:bytes + spot encoding:NSUTF8StringEncoding];
|
|
spot += 4;
|
|
uint32_t offset = [self value32:spot] + base;
|
|
spot += 4;
|
|
|
|
if ([country isEqualToString:_defaultCountry]) {
|
|
_defaultCallingCode = callingCode;
|
|
}
|
|
|
|
[_countryCallingCode setObject:callingCode forKey:country];
|
|
|
|
[_callingCodeOffsets setObject:[NSNumber numberWithLong:offset] forKey:callingCode];
|
|
NSMutableSet *countries = [_callingCodeCountries objectForKey:callingCode];
|
|
if (!countries) {
|
|
countries = [[NSMutableSet alloc] init];
|
|
[_callingCodeCountries setObject:countries forKey:callingCode];
|
|
}
|
|
[countries addObject:country];
|
|
}
|
|
|
|
if (_defaultCallingCode) {
|
|
[self callingCodeInfo:_defaultCallingCode];
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
- (void)dump {
|
|
NSArray *callingCodes = [[_callingCodeOffsets allKeys] sortedArrayUsingSelector:@selector(compare:)];
|
|
for (NSString *callingCode in callingCodes) {
|
|
CallingCodeInfo *info = [self callingCodeInfo:callingCode];
|
|
NSLog(@"%@", info);
|
|
}
|
|
|
|
NSLog(@"flagRules: %@", flagRules);
|
|
NSLog(@"extra1 calling codes: %@", extra1CallingCodes);
|
|
NSLog(@"extra2 calling codes: %@", extra2CallingCodes);
|
|
NSLog(@"extra3 calling codes: %@", extra3CallingCodes);
|
|
}
|
|
#endif
|
|
|
|
|
|
@end
|