Use defaults cleanups

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@39373 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2016-02-15 10:10:39 +00:00
parent 472e941dbe
commit 09f15d2b8d
6 changed files with 168 additions and 89 deletions

View file

@ -1,3 +1,16 @@
2016-02-15 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSFileManager.m: Use NSDebugLog for most debug warnings
* Source/NSUserDefaults.m: Restructure to cache flag information
from NSProcessInfo directly on initialization, so it can be used
when parsing property lists in the argument domain, avoiding any
recursion trying to parse arguments. Remove obsolete code for
for parsing -GS and --GS argument prefixes specially.
Make sure that argument parsiong behavior matches OSX apart from
The handling of a lone '-' used as key/value, and for this case
document the difference (since the GNUstep behavior is more useful
and changing it would have a real chance of breaking existing code).
2016-02-12 Niels Grewe <niels.grewe@halbordnung.de> 2016-02-12 Niels Grewe <niels.grewe@halbordnung.de>
* Headers/Foundation/NSObjCRuntime.h: Define macros for * Headers/Foundation/NSObjCRuntime.h: Define macros for

View file

@ -46,8 +46,15 @@ extern "C" {
/* Standard domains */ /* Standard domains */
/** /**
* User defaults domain for process arguments. Command-line arguments * User defaults domain for process arguments. Command-line arguments
* (attribute-value pairs, as in "-NSFoo bar") are placed into this domain. * (key-value pairs, as in "-NSFoo bar") are placed in this domain.<br />
* Where there is a sequence of arguments beginning with '-', only the
* last one is used (so "-a -b -c d" will produce a single user default
* 'c' with value 'd').<br />
* NB. On OSX the argument "-" means a key consisting of an empty string
* (so you can't use a '-' as a default value), while in GNUstep a "-" is
* a special case which does not mean a default key (so '-' may be used
* as a value).<br />
*/ */
GS_EXPORT NSString* const NSArgumentDomain; GS_EXPORT NSString* const NSArgumentDomain;

View file

@ -303,7 +303,7 @@ GS_EXPORT NSString* GSDebugMethodMsg(id obj, SEL sel, const char *file,
/** The DLog macro is a less powerful but commonly used logging macro, /** The DLog macro is a less powerful but commonly used logging macro,
* defined here for convenience when porting code. It will tell you * defined here for convenience when porting code. It will tell you
* the function name and line number but not the fle location. * the function name and line number but not the file location.
* It performs unconditional logging but is only compiled in when the * It performs unconditional logging but is only compiled in when the
* program is built with DEBUG defined. * program is built with DEBUG defined.
*/ */

View file

@ -1018,7 +1018,7 @@ static NSStringEncoding defaultEncoding;
NSFileOwnerAccountName, NSUserName(), nil]; NSFileOwnerAccountName, NSUserName(), nil];
if (![self changeFileAttributes: attributes atPath: path]) if (![self changeFileAttributes: attributes atPath: path])
{ {
NSLog(@"Failed to change ownership of '%@' to '%@'", NSDebugLog(@"Failed to change ownership of '%@' to '%@'",
path, NSUserName()); path, NSUserName());
} }
} }
@ -2105,7 +2105,7 @@ static NSStringEncoding defaultEncoding;
return [NSDictionary dictionaryWithObjects: values forKeys: keys count: 5]; return [NSDictionary dictionaryWithObjects: values forKeys: keys count: 5];
#else #else
NSLog(@"NSFileManager", @"no support for filesystem attributes"); GSOnceMLog(@"NSFileManager", @"no support for filesystem attributes");
ASSIGN(_lastError, @"no support for filesystem attributes"); ASSIGN(_lastError, @"no support for filesystem attributes");
return nil; return nil;
#endif #endif
@ -2389,7 +2389,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
} }
else else
{ {
NSLog(@"Failed to recurse into directory '%@' - %@", path, NSDebugLog(@"Failed to recurse into directory '%@' - %@", path,
[NSError _last]); [NSError _last]);
} }
} }
@ -2540,7 +2540,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
} }
if (S_IFDIR == (S_IFMT & statbuf.st_mode)) if (S_IFDIR == (S_IFMT & statbuf.st_mode))
{ {
_DIR* dir_pointer; _DIR *dir_pointer;
dir_pointer dir_pointer
= _OPENDIR([_mgr fileSystemRepresentationWithPath: = _OPENDIR([_mgr fileSystemRepresentationWithPath:
@ -2556,7 +2556,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
} }
else else
{ {
NSLog(@"Failed to recurse into directory '%@' - %@", NSDebugLog(@"Failed to recurse into directory '%@' - %@",
_currentFilePath, [NSError _last]); _currentFilePath, [NSError _last]);
} }
} }
@ -2944,7 +2944,7 @@ static inline void gsedRelease(GSEnumeratedDirectory X)
s = [NSString stringWithFormat: @"cannot copy file type '%@'", s = [NSString stringWithFormat: @"cannot copy file type '%@'",
fileType]; fileType];
ASSIGN(_lastError, s); ASSIGN(_lastError, s);
NSLog(@"%@: %@", sourceFile, s); NSDebugLog(@"%@: %@", sourceFile, s);
continue; continue;
} }
[self changeFileAttributes: attributes atPath: destinationFile]; [self changeFileAttributes: attributes atPath: destinationFile];
@ -3639,7 +3639,7 @@ static NSSet *fileKeys = nil;
} }
if (count >= 2) if (count >= 2)
{ {
NSLog(@"Warning ... key '%@' not handled", key); NSDebugLog(@"Warning ... key '%@' not handled", key);
} }
return nil; return nil;
} }

View file

@ -2622,7 +2622,7 @@ GSPropertyListMake(id obj, NSDictionary *loc, BOOL xml,
} }
if (length - index > 2 if (length - index > 2
&& bytes[index] == '<' && bytes[index+1] == '?') && bytes[index] == '<' && bytes[index+1] == '?')
{ {
// It begins with '<?' so it is xml // It begins with '<?' so it is xml
format = NSPropertyListXMLFormat_v1_0; format = NSPropertyListXMLFormat_v1_0;
@ -2630,7 +2630,7 @@ GSPropertyListMake(id obj, NSDictionary *loc, BOOL xml,
else else
{ {
// Assume openstep format unless we find otherwise. // Assume openstep format unless we find otherwise.
format = NSPropertyListOpenStepFormat; format = NSPropertyListOpenStepFormat;
} }
} }
} }

View file

@ -88,6 +88,7 @@ static NSString *GSPrimaryDomain = @"GSPrimaryDomain";
static NSString *defaultsFile = @".GNUstepDefaults"; static NSString *defaultsFile = @".GNUstepDefaults";
static NSUserDefaults *sharedDefaults = nil; static NSUserDefaults *sharedDefaults = nil;
static NSDictionary *argumentsDictionary = nil;
static NSMutableString *processName = nil; static NSMutableString *processName = nil;
static NSRecursiveLock *classLock = nil; static NSRecursiveLock *classLock = nil;
@ -97,10 +98,10 @@ static NSRecursiveLock *classLock = nil;
* have no defaults available. * have no defaults available.
*/ */
static BOOL hasSharedDefaults = NO; static BOOL hasSharedDefaults = NO;
/*
* Caching some defaults. /* Caching some default flag values. Until the standard defaults have
* been loaded, these values are taken from the process arguments.
*/ */
static BOOL parsingArguments = NO;
static BOOL flags[GSUserDefaultMaxFlag] = { 0 }; static BOOL flags[GSUserDefaultMaxFlag] = { 0 };
/* An instance of the GSPersistentDomain class is used to encapsulate /* An instance of the GSPersistentDomain class is used to encapsulate
@ -244,6 +245,8 @@ updateCache(NSUserDefaults *self)
} }
} }
/* NB the following flags are first set up, in the +initialize method.
*/
flags[GSMacOSXCompatible] flags[GSMacOSXCompatible]
= [self boolForKey: @"GSMacOSXCompatible"]; = [self boolForKey: @"GSMacOSXCompatible"];
flags[GSOldStyleGeometry] flags[GSOldStyleGeometry]
@ -429,7 +432,7 @@ newLanguages(NSArray *oldNames)
*** Private method definitions *** Private method definitions
*************************************************************************/ *************************************************************************/
@interface NSUserDefaults (Private) @interface NSUserDefaults (Private)
- (NSDictionary*) _createArgumentDictionary; + (void) _createArgumentDictionary: (NSArray*)args;
- (void) _changePersistentDomain: (NSString*)domainName; - (void) _changePersistentDomain: (NSString*)domainName;
- (NSString*) _directory; - (NSString*) _directory;
- (BOOL) _lockDefaultsFile: (BOOL*)wasLocked; - (BOOL) _lockDefaultsFile: (BOOL*)wasLocked;
@ -547,6 +550,7 @@ newLanguages(NSArray *oldNames)
{ {
DESTROY(sharedDefaults); DESTROY(sharedDefaults);
DESTROY(processName); DESTROY(processName);
DESTROY(argumentsDictionary);
DESTROY(classLock); DESTROY(classLock);
} }
@ -554,6 +558,11 @@ newLanguages(NSArray *oldNames)
{ {
if (self == [NSUserDefaults class]) if (self == [NSUserDefaults class])
{ {
CREATE_AUTORELEASE_POOL(pool);
NSEnumerator *enumerator;
NSArray *args;
NSString *key;
nextObjectSel = @selector(nextObject); nextObjectSel = @selector(nextObject);
objectForKeySel = @selector(objectForKey:); objectForKeySel = @selector(objectForKey:);
addSel = @selector(addEntriesFromDictionary:); addSel = @selector(addEntriesFromDictionary:);
@ -567,8 +576,67 @@ newLanguages(NSArray *oldNames)
NSNumberClass = [NSNumber class]; NSNumberClass = [NSNumber class];
NSMutableDictionaryClass = [NSMutableDictionary class]; NSMutableDictionaryClass = [NSMutableDictionary class];
NSStringClass = [NSString class]; NSStringClass = [NSString class];
classLock = [GSLazyRecursiveLock new];
[self registerAtExit]; [self registerAtExit];
/* Initialise the defaults flags to take values from the
* process arguments. These are otherwise set in updateCache()
* We do this early on so that the boolean argument settings can
* be used while parsing property list values of other args in
* the +_createArgumentDictionary: method.
*/
args = [[NSProcessInfo processInfo] arguments];
enumerator = [[[NSProcessInfo processInfo] arguments] objectEnumerator];
[enumerator nextObject]; // Skip process name.
while (nil != (key = [enumerator nextObject]))
{
if ([key hasPrefix: @"-"] == YES && [key isEqual: @"-"] == NO)
{
id val;
/* Anything beginning with a '-' is a defaults key and we
* must strip the '-' from it.
*/
key = [key substringFromIndex: 1];
while (nil != (val = [enumerator nextObject]))
{
if ([val hasPrefix: @"-"] == YES && [val isEqual: @"-"] == NO)
{
key = val;
}
else if ([key isEqualToString: @"GSMacOSXCompatible"])
{
flags[GSMacOSXCompatible] = [val boolValue];
}
else if ([key isEqualToString: @"GSOldStyleGeometry"])
{
flags[GSOldStyleGeometry] = [val boolValue];
}
else if ([key isEqualToString: @"GSLogSyslog"])
{
flags[GSLogSyslog] = [val boolValue];
}
else if ([key isEqualToString: @"GSLogThread"])
{
flags[GSLogThread] = [val boolValue];
}
else if ([key isEqualToString: @"GSLogOffset"])
{
flags[GSLogOffset] = [val boolValue];
}
else if ([key isEqual: @"NSWriteOldStylePropertyLists"])
{
flags[NSWriteOldStylePropertyLists] = [val boolValue];
}
}
}
}
/* The classLock must be created after setting up the flags[] array,
* so once it exists we know we can used them safely.
*/
classLock = [NSRecursiveLock new];
[self _createArgumentDictionary: args];
DESTROY(pool);
} }
} }
@ -804,19 +872,20 @@ newLanguages(NSArray *oldNames)
if (nil == defs) if (nil == defs)
{ {
const unsigned retryCount = 100; const unsigned retryCount = 100;
const NSTimeInterval retryInterval = 0.1; const NSTimeInterval retryInterval = 0.1;
unsigned i; unsigned i;
for (i = 0; i < retryCount; i++) for (i = 0; i < retryCount; i++)
{ {
[NSThread sleepForTimeInterval:retryInterval]; [NSThread sleepForTimeInterval: retryInterval];
[classLock lock]; [classLock lock];
defs = [sharedDefaults retain]; defs = RETAIN(sharedDefaults);
setup = hasSharedDefaults; setup = hasSharedDefaults;
[classLock unlock]; [classLock unlock];
if (YES == setup) if (YES == setup)
{ {
NS_VALRETURN([defs autorelease]); NS_VALRETURN(AUTORELEASE(defs));
} }
RELEASE(defs); RELEASE(defs);
} }
@ -1117,7 +1186,6 @@ newLanguages(NSArray *oldNames)
NSFileManager *mgr = [NSFileManager defaultManager]; NSFileManager *mgr = [NSFileManager defaultManager];
NSRange r; NSRange r;
BOOL flag; BOOL flag;
NSDictionary *argumentsDictionary = nil;
self = [super init]; self = [super init];
@ -1175,12 +1243,7 @@ newLanguages(NSArray *oldNames)
// Create volatile defaults and add the Argument and the Registration domains // Create volatile defaults and add the Argument and the Registration domains
_tempDomains = [[NSMutableDictionaryClass alloc] initWithCapacity: 10]; _tempDomains = [[NSMutableDictionaryClass alloc] initWithCapacity: 10];
argumentsDictionary = [self _createArgumentDictionary]; [_tempDomains setObject: argumentsDictionary forKey: NSArgumentDomain];
if (nil != argumentsDictionary)
{
[_tempDomains setObject: argumentsDictionary
forKey: NSArgumentDomain];
}
[_tempDomains [_tempDomains
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10] setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
forKey: NSRegistrationDomain]; forKey: NSRegistrationDomain];
@ -2132,9 +2195,18 @@ static BOOL isPlistObject(id o)
BOOL BOOL
GSPrivateDefaultsFlag(GSUserDefaultFlagType type) GSPrivateDefaultsFlag(GSUserDefaultFlagType type)
{ {
if (nil == sharedDefaults && NO == parsingArguments) if (nil == classLock)
{ {
[NSUserDefaults standardUserDefaults]; /* The order of +initialise of NSUserDefaults is such that our
* flags[] array is set up directly from the process arguments
* before classLock is created, so once * that variable exists
* this function may be used safely.
*/
[NSUserDefaults class];
if (NO == hasSharedDefaults)
{
[NSUserDefaults standardUserDefaults];
}
} }
return flags[type]; return flags[type];
} }
@ -2150,7 +2222,7 @@ NSDictionary *GSPrivateDefaultLocale()
NSDictionary *locale = nil; NSDictionary *locale = nil;
NSUserDefaults *defs = nil; NSUserDefaults *defs = nil;
if (classLock == nil) if (nil == classLock)
{ {
[NSUserDefaults standardUserDefaults]; [NSUserDefaults standardUserDefaults];
} }
@ -2177,26 +2249,16 @@ NSDictionary *GSPrivateDefaultLocale()
@implementation NSUserDefaults (Private) @implementation NSUserDefaults (Private)
- (NSDictionary*) _createArgumentDictionary + (void) _createArgumentDictionary: (NSArray*)args
{ {
NSArray *args;
NSEnumerator *enumerator; NSEnumerator *enumerator;
NSMutableDictionary *argDict = nil; NSMutableDictionary *argDict = nil;
BOOL done; BOOL done;
id key, val; id key, val;
[classLock lock]; [classLock lock];
if (YES == parsingArguments)
{
[classLock unlock];
return nil; // Prevent recursion
}
parsingArguments = YES;
[classLock unlock];
[_lock lock];
NS_DURING NS_DURING
{ {
args = [[NSProcessInfo processInfo] arguments];
enumerator = [args objectEnumerator]; enumerator = [args objectEnumerator];
argDict = [NSMutableDictionaryClass dictionaryWithCapacity: 2]; argDict = [NSMutableDictionaryClass dictionaryWithCapacity: 2];
[enumerator nextObject]; // Skip process name. [enumerator nextObject]; // Skip process name.
@ -2204,40 +2266,35 @@ NSDictionary *GSPrivateDefaultLocale()
while (done == NO) while (done == NO)
{ {
if ([key hasPrefix: @"-"] == YES && [key isEqual: @"-"] == NO) /* Any value with a leading '-' may be the name of a default
* in the argument domain.
* NB. Testing on OSX shows that this includes a single '-'
* (where the key is an empty string), but GNUstep disallows
* en empty string as a key (so it can be a value).
*/
if ([key hasPrefix: @"-"] == YES
&& [key isEqual: @"-"] == NO)
{ {
NSString *old = nil; /* Strip the '-' before the defaults key, and get the
* corresponding value (the next argument).
/* anything beginning with a '-' is a defaults key and we
* must strip the '-' from it.
* As a special case, we leave the '- in place for '-GS...'
* and '--GS...' for backward compatibility.
*/ */
if ([key hasPrefix: @"-GS"] == YES
|| [key hasPrefix: @"--GS"] == YES)
{
old = key;
}
key = [key substringFromIndex: 1]; key = [key substringFromIndex: 1];
val = [enumerator nextObject]; val = [enumerator nextObject];
if (val == nil) if (nil == val)
{ // No more args {
[argDict setObject: @"" forKey: key]; // arg is empty. /* No more arguments and no value ... arg is not set.
if (old != nil) */
{
[argDict setObject: @"" forKey: old];
}
done = YES; done = YES;
continue; continue;
} }
else if ([val hasPrefix: @"-"] == YES else if ([val hasPrefix: @"-"] == YES
&& [val isEqual: @"-"] == NO) && [val isEqual: @"-"] == NO)
{ // Yet another argument {
[argDict setObject: @"" forKey: key]; // arg is empty. /* Value is actually an argument key ...
if (old != nil) * current key is not used (behavior matches OSX).
{ * NB. GNUstep allows a '-' as the value for a default,
[argDict setObject: @"" forKey: old]; * but OSX does not.
} */
key = val; key = val;
continue; continue;
} }
@ -2250,15 +2307,26 @@ NSDictionary *GSPrivateDefaultLocale()
foreign environment. */ foreign environment. */
NSObject *plist_val; NSObject *plist_val;
NS_DURING NS_DURING
{ {
plist_val = [val propertyList]; NSData *data;
}
NS_HANDLER data = [val dataUsingEncoding: NSUTF8StringEncoding];
{ plist_val = [NSPropertyListSerialization
plist_val = val; propertyListFromData: data
} mutabilityOption: NSPropertyListMutableContainers
NS_ENDHANDLER format: 0
errorDescription: 0];
if (nil == plist_val)
{
plist_val = val;
}
}
NS_HANDLER
{
plist_val = val;
}
NS_ENDHANDLER
/* Make sure we don't crash being caught adding nil to /* Make sure we don't crash being caught adding nil to
a dictionary. */ a dictionary. */
@ -2268,29 +2336,20 @@ NSDictionary *GSPrivateDefaultLocale()
} }
[argDict setObject: plist_val forKey: key]; [argDict setObject: plist_val forKey: key];
if (old != nil)
{
[argDict setObject: plist_val forKey: old];
}
} }
} }
done = ((key = [enumerator nextObject]) == nil); done = ((key = [enumerator nextObject]) == nil);
} }
[classLock lock]; argumentsDictionary = [argDict copy];
parsingArguments = NO;
[classLock unlock]; [classLock unlock];
[_lock unlock];
} }
NS_HANDLER NS_HANDLER
{ {
[classLock lock]; argumentsDictionary = [NSDictionary new];
parsingArguments = NO;
[classLock unlock]; [classLock unlock];
[_lock unlock];
[localException raise]; [localException raise];
} }
NS_ENDHANDLER NS_ENDHANDLER
return argDict;
} }
- (void) _changePersistentDomain: (NSString*)domainName - (void) _changePersistentDomain: (NSString*)domainName