Various minor thread safety fixes and fallback mechanisms for bad installations.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@22524 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2006-02-21 15:44:04 +00:00
parent 1c89b5daed
commit 82a690e0bd
2 changed files with 422 additions and 122 deletions

View file

@ -146,6 +146,8 @@
/* Many systems have this file */
#define SYSTEM_TIME_FILE @"/etc/localtime"
#define POSIX_TZONES @"posix/"
/* Possible location of system time zone files */
static NSString *tzdir = nil;
@ -187,6 +189,7 @@ typedef struct {
{
@public
NSString *timeZoneName;
NSArray *abbreviations;
NSData *timeZoneData;
unsigned int n_trans;
unsigned int n_types;
@ -636,7 +639,7 @@ static NSMapTable *absolutes = 0;
char buf[9];
sprintf(buf, "GMT%c%02d%02d", s, h, m);
name = [[NSString alloc] initWithCString: buf];
name = [[NSString alloc] initWithUTF8String: buf];
}
else
{
@ -872,22 +875,67 @@ static NSMapTable *absolutes = 0;
*/
+ (NSDictionary*) abbreviationDictionary
{
NSString *path;
if (abbreviationDictionary != nil)
return abbreviationDictionary;
path = _time_zone_path (ABBREV_DICT, @"plist");
if (path == NULL)
{
[NSException
raise: NSInternalInconsistencyException
format: @"Failed to open time zone abbreviation dictionary."];
return abbreviationDictionary;
}
abbreviationDictionary =
[[NSString stringWithContentsOfFile: path] propertyList];
abbreviationDictionary =
[abbreviationDictionary makeImmutableCopyOnFail: NO];
return RETAIN(abbreviationDictionary);
if (zone_mutex != nil)
{
[zone_mutex lock];
}
if (abbreviationDictionary == nil)
{
CREATE_AUTORELEASE_POOL(pool);
NSString *path;
path = _time_zone_path (ABBREV_DICT, @"plist");
if (path != nil)
{
/*
* Fast mechanism ... load prebuilt data from file so we don't
* need to load in all time zones.
*/
abbreviationDictionary
= RETAIN([[NSString stringWithContentsOfFile: path] propertyList]);
}
if (abbreviationDictionary == nil)
{
NSMutableDictionary *md;
NSString *name;
NSEnumerator *names;
/*
* Slow fallback ... load all time zones and generate
* abbreviation dictionary from them.
*/
md = [[NSMutableDictionary alloc] init];
names = [[NSTimeZone knownTimeZoneNames] objectEnumerator];
while ((name = [names nextObject]) != nil)
{
NSTimeZone *zone;
if ((zone = [NSTimeZone timeZoneWithName: name]))
{
NSEnumerator *details;
NSTimeZoneDetail *detail;
details = [[zone timeZoneDetailArray] objectEnumerator];
while ((detail = [details nextObject]) != nil)
{
[md setObject: name forKey: [detail abbreviation]];
}
}
}
[md makeImmutableCopyOnFail: NO];
abbreviationDictionary = md;
}
RELEASE(pool);
}
if (zone_mutex != nil)
{
[zone_mutex unlock];
}
return abbreviationDictionary;
}
/**
@ -896,64 +944,143 @@ static NSMapTable *absolutes = 0;
*/
+ (NSDictionary*) abbreviationMap
{
FILE *file; // For the file containing the abbreviation dictionary
char abbrev[80], name[80];
NSString *path;
/* Instead of creating the abbreviation dictionary when the class is
initialized, we create it when we first need it, since the
dictionary can be potentially very large, considering that it's
almost never used. */
if (abbreviationMap != nil)
return abbreviationMap;
/* Read dictionary from file. */
abbreviationMap = [[NSMutableDictionary alloc] init];
path = _time_zone_path (ABBREV_MAP, nil);
#if defined(__MINGW32__)
{
unichar mode[3];
mode[0] = 'r';
mode[1] = 'b';
mode[2] = '\0';
file = _wfopen((const unichar*)[path fileSystemRepresentation], mode);
}
#else
file = fopen([path fileSystemRepresentation], "r");
#endif
if (file == NULL)
[NSException
raise: NSInternalInconsistencyException
format: @"Failed to open time zone abbreviation map."];
while (fscanf(file, "%79s %79s", abbrev, name) == 2)
{
id a, the_name, the_abbrev;
the_name = [NSString stringWithCString: name];
the_abbrev = [NSString stringWithCString: abbrev];
a = [abbreviationMap objectForKey: the_abbrev];
if (a == nil)
{
a = AUTORELEASE([NSMutableArray new]);
[abbreviationMap setObject: a forKey: the_abbrev];
}
[a addObject: the_name];
return abbreviationMap;
}
fclose(file);
if (zone_mutex != nil)
{
[zone_mutex lock];
}
if (abbreviationMap == nil)
{
CREATE_AUTORELEASE_POOL(pool);
NSMutableDictionary *md;
NSMutableArray *ma;
NSString *the_name;
NSString *the_abbrev;
FILE *file;
char abbrev[80];
char name[80];
NSString *path;
/* Special case: Add the system time zone if it doesn't exist in the map */
{
id array;
id the_abbrev = [systemTimeZone abbreviation];
array = [abbreviationMap objectForKey: the_abbrev];
if (array == nil)
{
array = AUTORELEASE([NSMutableArray new]);
[abbreviationMap setObject: array forKey: the_abbrev];
}
if ([array containsObject: [systemTimeZone timeZoneName]] == NO)
[array addObject: [systemTimeZone timeZoneName]];
}
/*
* Read dictionary from file... fast mechanism because we don't have
* to create all timezoneas and parse all their data files.
*/
md = [NSMutableDictionary dictionaryWithCapacity: 100];
path = _time_zone_path (ABBREV_MAP, nil);
if (path != nil)
{
#if defined(__MINGW32__)
unichar mode[3];
mode[0] = 'r';
mode[1] = 'b';
mode[2] = '\0';
file = _wfopen((const unichar*)[path fileSystemRepresentation], mode);
#else
file = fopen([path fileSystemRepresentation], "r");
#endif
if (file == NULL)
{
if (zone_mutex != nil)
{
[zone_mutex unlock];
}
[NSException
raise: NSInternalInconsistencyException
format: @"Failed to open time zone abbreviation map."];
}
while (fscanf(file, "%79s %79s", abbrev, name) == 2)
{
the_name = [[NSString alloc] initWithUTF8String: name];
the_abbrev = [[NSString alloc] initWithUTF8String: abbrev];
ma = [md objectForKey: the_abbrev];
if (ma == nil)
{
ma = [[NSMutableArray alloc] initWithCapacity: 1];
[md setObject: ma forKey: the_abbrev];
RELEASE(ma);
}
RELEASE(the_abbrev);
[ma addObject: the_name];
RELEASE(the_name);
}
fclose(file);
}
else
{
NSArray *names;
NSString *name;
NSEnumerator *e;
int i;
/*
* Slow fallback mechanism ... go through all time names
* so we load all the time zone data and generate the info
* we need from it.
*/
names = [NSTimeZone knownTimeZoneNames];
for (i = 0; i < 24; i++)
{
e = [[names objectAtIndex: i] objectEnumerator];
while ((name = [e nextObject]) != nil)
{
NSTimeZone *zone;
if ((zone = [NSTimeZone timeZoneWithName: name]))
{
NSEnumerator *details;
NSTimeZoneDetail *detail;
details = [[zone timeZoneDetailArray] objectEnumerator];
while ((detail = [details nextObject]) != nil)
{
the_abbrev = [detail abbreviation];
ma = [md objectForKey: the_abbrev];
if (ma == nil)
{
ma = [[NSMutableArray alloc] initWithCapacity: 1];
[md setObject: ma forKey: the_abbrev];
RELEASE(ma);
}
[ma addObject: name];
}
}
}
}
}
/* Special case: Add the system time zone if
* it doesn't exist in the map */
the_abbrev = [systemTimeZone abbreviation];
ma = [md objectForKey: the_abbrev];
if (ma == nil)
{
ma = [NSMutableArray new];
[md setObject: ma forKey: the_abbrev];
RELEASE(ma);
}
the_name = [systemTimeZone timeZoneName];
if ([ma containsObject: the_name] == NO)
{
[ma addObject: the_name];
}
[md makeImmutableCopyOnFail: NO];
abbreviationMap = RETAIN(md);
RELEASE(pool);
}
if (zone_mutex != nil)
{
[zone_mutex unlock];
}
return abbreviationMap;
}
@ -964,24 +1091,40 @@ static NSMapTable *absolutes = 0;
+ (NSArray*) knownTimeZoneNames
{
static NSArray *namesArray = nil;
int i;
id temp_array;
NSArray *regionsArray, *array;
/* We create the array only when we need it to reduce overhead. */
if (namesArray != nil)
return namesArray;
temp_array = [NSMutableArray array];
regionsArray = [self timeZoneArray];
for (i = 0; i < [regionsArray count]; i++)
{
array = [regionsArray objectAtIndex: i];
[temp_array addObjectsFromArray: array];
return namesArray;
}
namesArray = [[NSArray alloc] initWithArray: temp_array];
if (zone_mutex != nil)
{
[zone_mutex lock];
}
if (namesArray == nil)
{
unsigned i;
NSMutableArray *ma;
NSArray *regionsArray;
ma = [NSMutableArray new];
regionsArray = [self timeZoneArray];
for (i = 0; i < [regionsArray count]; i++)
{
NSArray *names = [regionsArray objectAtIndex: i];
[ma addObjectsFromArray: names];
}
[ma makeImmutableCopyOnFail: NO];
namesArray = ma;
}
if (zone_mutex != nil)
{
[zone_mutex unlock];
}
return namesArray;
}
@ -1205,8 +1348,8 @@ static NSMapTable *absolutes = 0;
is a symlink to the time zone. Getting the actual data (which
is easier) doesn't help, since we won't know the name itself. */
#if defined(HAVE_TZHEAD) && defined(TZDEFAULT)
tzdir = RETAIN([NSString stringWithCString: TZDIR]);
localZoneString = [NSString stringWithCString: TZDEFAULT];
tzdir = RETAIN([NSString stringWithUTF8String: TZDIR]);
localZoneString = [NSString stringWithUTF8String: TZDEFAULT];
localZoneString = [localZoneString stringByResolvingSymlinksInPath];
#else
NSFileManager *dflt = [NSFileManager defaultManager];
@ -1257,7 +1400,7 @@ static NSMapTable *absolutes = 0;
{
tzset();
if (tzname[0] != NULL && *tzname[0] != '\0')
localZoneString = [NSString stringWithCString: tzname[0]];
localZoneString = [NSString stringWithUTF8String: tzname[0]];
}
#endif
@ -1291,7 +1434,7 @@ static NSMapTable *absolutes = 0;
bufsize--;
}
localZoneString
= [NSString stringWithCString: buf length: bufsize];
= [NSString stringWithUTF8String: buf length: bufsize];
}
RegCloseKey(regkey);
}
@ -1332,6 +1475,18 @@ static NSMapTable *absolutes = 0;
return zone;
}
/**
* Common locations for timezone info on unix systems.
*/
static NSString *zoneDirs[] = {
@"/usr/share/zoneinfo/",
@"/usr/lib/zoneinfo/",
@"/usr/local/share/zoneinfo/",
@"/usr/local/lib/zoneinfo/",
@"/etc/zoneinfo/",
@"/usr/local/etc/zoneinfo/"
};
/**
* Returns an array of all the known regions.<br />
* There are 24 elements, of course, one for each time zone.
@ -1341,39 +1496,159 @@ static NSMapTable *absolutes = 0;
+ (NSArray*) timeZoneArray
{
static NSArray *regionsArray = nil;
int index, i;
char name[80];
FILE *file;
id temp_array[24];
NSString *fileName;
/* We create the array only when we need it to reduce overhead. */
if (regionsArray != nil)
return regionsArray;
{
return regionsArray;
}
if (zone_mutex != nil)
{
[zone_mutex lock];
}
if (regionsArray == nil)
{
CREATE_AUTORELEASE_POOL(pool);
int index, i;
char name[80];
FILE *fp;
NSMutableArray *temp_array[24];
NSString *path;
for (i = 0; i < 24; i++)
temp_array[i] = [NSMutableArray array];
for (i = 0; i < 24; i++)
{
temp_array[i] = [NSMutableArray array];
}
fileName = _time_zone_path (REGIONS_FILE, nil);
path = _time_zone_path (REGIONS_FILE, nil);
if (path != nil)
{
#if defined(__MINGW32__)
{
unichar mode[3];
mode[0] = 'r';
mode[1] = 'b';
mode[2] = '\0';
file = _wfopen((const unichar*)[fileName fileSystemRepresentation], mode);
}
unichar mode[3];
mode[0] = 'r';
mode[1] = 'b';
mode[2] = '\0';
fp = _wfopen((const unichar*)[path fileSystemRepresentation], mode);
#else
file = fopen([fileName fileSystemRepresentation], "r");
fp = fopen([path fileSystemRepresentation], "r");
#endif
if (file == NULL)
[NSException
raise: NSInternalInconsistencyException
format: @"Failed to open time zone regions array file."];
while (fscanf(file, "%d %s", &index, name) == 2)
[temp_array[index] addObject: [NSString stringWithCString: name]];
fclose(file);
regionsArray = [[NSArray alloc] initWithObjects: temp_array count: 24];
if (fp == NULL)
{
if (zone_mutex != nil)
{
[zone_mutex unlock];
}
[NSException
raise: NSInternalInconsistencyException
format: @"Failed to open time zone regions array file."];
}
while (fscanf(fp, "%d %s", &index, name) == 2)
{
[temp_array[index]
addObject: [NSString stringWithUTF8String: name]];
}
fclose(fp);
}
else
{
NSFileManager *mgr = [NSFileManager defaultManager];
NSString *zonedir = nil;
unsigned i;
for (i = 0; i < sizeof(zoneDirs)/sizeof(zoneDirs[0]); i++)
{
BOOL isDir;
path = [zoneDirs[i] stringByAppendingString: POSIX_TZONES];
if ([mgr fileExistsAtPath: path isDirectory: &isDir] && isDir)
{
zonedir = path;
break; // use first one
}
}
if (zonedir == nil)
{
NSLog(@"no zone directory found!");
}
else
{
NSDirectoryEnumerator *enumerator;
NSString *name;
enumerator = [mgr enumeratorAtPath: zonedir];
while ((name = [enumerator nextObject]) != nil)
{
NSTimeZone *zone = nil;
BOOL isDir;
// FIXME: check file validity.
path = [zonedir stringByAppendingString: name];
if ([mgr fileExistsAtPath: path isDirectory: &isDir]
&& isDir == NO)
{
zone = [zoneDictionary objectForKey: name];
if (zone == nil)
{
NSData *data;
data = [NSData dataWithContentsOfFile: path];
zone = [[self alloc] initWithName: name data: data];
AUTORELEASE(zone);
}
if (zone != nil)
{
int offset;
NSArray *details;
NSTimeZoneDetail *detail;
NSEnumerator *e;
details = [zone timeZoneDetailArray];
e = [details objectEnumerator];
while ((detail = [e nextObject]) != nil)
{
if ([detail isDaylightSavingTime] == NO)
{
break; // Found a standard time
}
}
if (detail == nil && [details count] > 0)
{
// If no standard time
detail = [details objectAtIndex: 0];
}
offset = [detail secondsFromGMT];
if (offset < 0)
{
offset = -offset;
offset %= (60 * 60 * 24);
offset = -offset;
offset += (60 * 60 * 24);
}
else
{
offset %= (60 * 60 * 24);
}
offset /= (60 * 60);
[temp_array[offset] addObject: name];
}
}
}
}
}
regionsArray = [[NSArray alloc] initWithObjects: temp_array count: 24];
RELEASE(pool);
}
if (zone_mutex != nil)
{
[zone_mutex unlock];
}
return regionsArray;
}
@ -1403,7 +1678,7 @@ static NSMapTable *absolutes = 0;
NSTimeZone *zone;
zone = [self timeZoneWithName: [[self abbreviationDictionary]
objectForKey: abbreviation] data: nil];
objectForKey: abbreviation] data: nil];
return zone;
}
@ -1935,7 +2210,7 @@ int dayOfCommonEra(NSTimeInterval when);
int a, b;
ASSIGN(timeZoneName,
[NSString stringWithCString: standardName]);
[NSString stringWithUTF8String: standardName]);
/* Abbr generated here is IMHO a
* bit suspicous but I kept it */
@ -1946,7 +2221,7 @@ int dayOfCommonEra(NSTimeInterval when);
}
standardName[b] = 0;
ASSIGN(timeZoneNameAbbr,
[NSString stringWithCString: standardName]);
[NSString stringWithUTF8String: standardName]);
}
/* Set the daylight savings name for the time zone. */
@ -1955,7 +2230,7 @@ int dayOfCommonEra(NSTimeInterval when);
int a, b;
ASSIGN(daylightZoneName,
[NSString stringWithCString: daylightName]);
[NSString stringWithUTF8String: daylightName]);
/* Abbr generated here is IMHO
* a bit suspicous but I kept it */
@ -1966,7 +2241,7 @@ int dayOfCommonEra(NSTimeInterval when);
}
daylightName[b] = 0;
ASSIGN(daylightZoneNameAbbr,
[NSString stringWithCString: daylightName]);
[NSString stringWithUTF8String: daylightName]);
}
}
RegCloseKey(regKey);
@ -2228,14 +2503,9 @@ newDetailInZoneForType(GSTimeZone *zone, TypeInfo *type)
{
RELEASE(timeZoneName);
RELEASE(timeZoneData);
RELEASE(abbreviations);
if (types != 0)
{
unsigned i;
for (i = 0; i < n_types; i++)
{
RELEASE(types[i].abbreviation);
}
NSZoneFree(NSDefaultMallocZone(), types);
}
[super dealloc];
@ -2335,7 +2605,9 @@ newDetailInZoneForType(GSTimeZone *zone, TypeInfo *type)
}
abbr = (unsigned char*)(bytes + pos);
{
id abbrevs[charcnt];
id abbrevs[charcnt];
unsigned count = 0;
unsigned used = 0;
memset(abbrevs, '\0', sizeof(id)*charcnt);
for (i = 0; i < n_types; i++)
@ -2345,14 +2617,38 @@ newDetailInZoneForType(GSTimeZone *zone, TypeInfo *type)
if (abbrevs[loc] == nil)
{
abbrevs[loc]
= [[NSString alloc] initWithCString: (char*)abbr + loc];
}
else
{
RETAIN(abbrevs[loc]);
= [[NSString alloc] initWithUTF8String: (char*)abbr + loc];
count++;
}
types[i].abbreviation = abbrevs[loc];
}
/*
* Now we have created all the abbreviations, we put them in an
* array for easy access later and easy deallocation if/when
* the receiver is deallocated.
*/
i = charcnt;
while (i-- > count)
{
if (abbrevs[i] != nil)
{
while (abbrevs[used] != nil)
{
used++;
}
abbrevs[used] = abbrevs[i];
abbrevs[i] = nil;
if (++used >= count)
{
break;
}
}
}
abbreviations = [[NSArray alloc] initWithObjects: abbrevs count: count];
while (count-- > 0)
{
RELEASE(abbrevs[count]);
}
}
if (zone_mutex != nil)