diff --git a/ChangeLog b/ChangeLog index a743d8657..339110d42 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2005-10-18 Richard Frith-Macdonald + + * Source/NSUserDefaults.m: Begin applying registry patch. + * Source/win32/NSUserDefaultsWin32.m: Registry patch with + some work done towards conforming to gnustep coding standards. + 2005-10-17 Richard Frith-Macdonald * SSL/GSSSLHandle.m: Don't output log/warning on accept/connect diff --git a/Source/NSUserDefaults.m b/Source/NSUserDefaults.m index 7f6e6a2d6..915a891da 100644 --- a/Source/NSUserDefaults.m +++ b/Source/NSUserDefaults.m @@ -56,6 +56,10 @@ #include "GNUstepBase/GSLocale.h" #include "GNUstepBase/GSLock.h" +#if defined(__MINGW32__) +@class NSUserDefaultsWin32; +#endif + #ifdef HAVE_LOCALE_H #include #endif @@ -455,7 +459,25 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */ setSharedDefaults = YES; // Create new sharedDefaults (NOTE: Not added to the autorelease pool!) +#if defined(__MINGW32__) + { +#if 0 + NSString *path = GSDefaultsRootForUser(NSUserName()); + NSRange r = [path rangeOfString: @":REGISTRY:"]; + + if (r.length > 0) + { + sharedDefaults = [[NSUserDefaultsWin32 alloc] init]; + } + else +#endif + { + sharedDefaults = [[self alloc] init]; + } + } +#else sharedDefaults = [[self alloc] init]; +#endif if (sharedDefaults == nil) { NSLog(@"WARNING - unable to create shared user defaults!\n"); @@ -558,9 +580,7 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */ */ + (NSArray*) userLanguages { - NSArray *currLang = nil; NSArray *result; - NSString *locale = nil; /* * Calling +standardUserDefaults and +userLanguages is horribly interrelated. @@ -587,11 +607,6 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */ * +userLanguages uses this to rebuild language information and return it. */ -#ifdef HAVE_LOCALE_H -#ifdef LC_MESSAGES - locale = GSSetLocale(LC_MESSAGES, nil); -#endif -#endif [classLock lock]; if (invalidatedLanguages == YES) { @@ -600,6 +615,14 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */ } if (userLanguages == nil) { + NSArray *currLang = nil; + NSString *locale = nil; + +#ifdef HAVE_LOCALE_H +#ifdef LC_MESSAGES + locale = GSSetLocale(LC_MESSAGES, nil); +#endif +#endif currLang = [[NSUserDefaults standardUserDefaults] stringArrayForKey: @"NSLanguages"]; @@ -688,7 +711,8 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */ } /* Returns the path to the user's ".GNUstepDefaults file" */ -static NSString *pathForUser(NSString *user) +static NSString * +pathForUser(NSString *user) { NSString *database = @".GNUstepDefaults"; NSFileManager *mgr = [NSFileManager defaultManager]; @@ -814,14 +838,14 @@ static NSString *pathForUser(NSString *user) } // Check and if not existent add the Application and the Global domains - if (![_persDomains objectForKey: processName]) + if ([_persDomains objectForKey: processName] == nil) { [_persDomains setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10] forKey: processName]; [self __changePersistentDomain: processName]; } - if (![_persDomains objectForKey: NSGlobalDomain]) + if ([_persDomains objectForKey: NSGlobalDomain] == nil) { [_persDomains setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10] @@ -1346,69 +1370,46 @@ static BOOL isPlistObject(id o) [_lock unlock]; } -/** - * Ensures that the in-memory and on-disk representations of the defaults - * are in sync. You may call this yourself, but probably don't need to - * since it is invoked at intervals whenever a runloop is running.
- * If any persistent domain is changed by reading new values from disk, - * an NSUserDefaultsDidChangeNotification is posted. - */ -- (BOOL) synchronize +- (BOOL) wantToReadDefaultsSince:(NSDate*)lastSyncDate { - NSFileManager *mgr = [NSFileManager defaultManager]; - NSMutableDictionary *newDict; - NSDictionary *attr; - NSDate *started = [NSDateClass date]; - unsigned long desired; - unsigned long attributes; - static BOOL isLocked = NO; - BOOL wasLocked; + NSFileManager *mgr = [NSFileManager defaultManager]; + NSDictionary *attr; - [_lock lock]; - - /* - * If we haven't changed anything, we only need to synchronise if - * the on-disk database has been changed by someone else. - */ - attr = [mgr fileAttributesAtPath: _defaultsDatabase - traverseLink: YES]; - if (_changedDomains == nil) + attr = [mgr fileAttributesAtPath: _defaultsDatabase traverseLink: YES]; + if (lastSyncDate == nil) { - BOOL wantRead = NO; - - if (_lastSync == nil) + return YES; + } + else + { + if (attr == nil) { - wantRead = YES; + return YES; } else { - if (attr == nil) - { - wantRead = YES; - } - else - { - NSDate *mod; + NSDate *mod; - /* - * If the database was modified since the last synchronisation - * we need to read it. - */ - mod = [attr objectForKey: NSFileModificationDate]; - if (mod != nil && [_lastSync laterDate: mod] != _lastSync) - { - wantRead = YES; - } + /* + * If the database was modified since the last synchronisation + * we need to read it. + */ + mod = [attr objectForKey: NSFileModificationDate]; + if (mod != nil && [lastSyncDate laterDate: mod] != lastSyncDate) + { + return YES; } } - if (wantRead == NO) - { - [_lock unlock]; - return YES; - } } + return NO; +} - wasLocked = isLocked; +static BOOL isLocked = NO; +- (BOOL) lockDefaultsFile:(BOOL*)wasLocked +{ + NSDate *started = [NSDateClass date]; + *wasLocked = isLocked; + if (isLocked == NO && _fileLock != nil) { while ([_fileLock tryLock] == NO) @@ -1430,7 +1431,6 @@ static BOOL isPlistObject(id o) { NSLog(@"Failed to lock user defaults database even after " @"breaking old locks!"); - [_lock unlock]; return NO; } @@ -1451,21 +1451,31 @@ static BOOL isPlistObject(id o) } isLocked = YES; } + return YES; +} + +- (void) unlockDefaultsFile +{ + [_fileLock unlock]; + isLocked = NO; +} + +- (NSMutableDictionary*) readDefaults +{ + NSMutableDictionary *newDict; + NSFileManager *mgr = [NSFileManager defaultManager]; + NSDictionary *attr; /* * Re-fetch database attributes in cased they changed while obtaining lock. */ attr = [mgr fileAttributesAtPath: _defaultsDatabase traverseLink: YES]; - - DESTROY(_dictionaryRep); - if (self == sharedDefaults) invalidatedLanguages = YES; - // Read the persistent data from the stored database if (attr == nil) { - newDict = [[NSMutableDictionaryClass allocWithZone: [self zone]] - initWithCapacity: 1]; + newDict = [[[NSMutableDictionaryClass allocWithZone: [self zone]] + initWithCapacity: 1] autorelease]; if (_fileLock != nil) { NSLog(@"Creating defaults database file %@", _defaultsDatabase); @@ -1482,8 +1492,8 @@ static BOOL isPlistObject(id o) } else { - newDict = [[NSMutableDictionaryClass allocWithZone: [self zone]] - initWithContentsOfFile: _defaultsDatabase]; + newDict = [[[NSMutableDictionaryClass allocWithZone: [self zone]] + initWithContentsOfFile: _defaultsDatabase] autorelease]; } if (newDict == nil) { @@ -1494,8 +1504,8 @@ static BOOL isPlistObject(id o) * initialised that way (possibly on a read-only filesystem) * so we just continue as best we can. */ - newDict = [[NSMutableDictionaryClass allocWithZone: [self zone]] - initWithCapacity: 4]; + newDict = [[[NSMutableDictionaryClass allocWithZone: [self zone]] + initWithCapacity: 4] autorelease]; } else { @@ -1504,40 +1514,105 @@ static BOOL isPlistObject(id o) * probably a severe error of some sort */ NSLog(@"Unable to load defaults from '%@'", _defaultsDatabase); - if (wasLocked == NO) - { - [_fileLock unlock]; - isLocked = NO; - } - [_lock unlock]; - return NO; } } } + + if (attr != nil) + { + unsigned long desired; + unsigned long attributes; + + /* + * We enforce the permission mode 0600 on the defaults database + */ + attributes = [attr filePosixPermissions]; +#if !(defined(S_IRUSR) && defined(S_IWUSR)) + desired = 0600; +#else + desired = (S_IRUSR|S_IWUSR); +#endif + if (attributes != desired) + { + NSMutableDictionary *enforced_attributes; + NSNumber *permissions; + + enforced_attributes = [NSMutableDictionary dictionaryWithDictionary: + [mgr fileAttributesAtPath: _defaultsDatabase traverseLink: YES]]; + + permissions = [NSNumberClass numberWithUnsignedLong: desired]; + [enforced_attributes setObject: permissions + forKey: NSFilePosixPermissions]; + + [mgr changeFileAttributes: enforced_attributes + atPath: _defaultsDatabase]; + } + } + return newDict; +} + +- (BOOL) writeDefaults: (NSDictionary*)defaults oldData: (NSDictionary*)oldData +{ + // Save the changes unless we are in read-only mode. + if (_fileLock != nil) + { + if ([defaults writeToFile: _defaultsDatabase atomically: YES] == NO) + { + return NO; + } + } + return YES; +} + +/** + * Ensures that the in-memory and on-disk representations of the defaults + * are in sync. You may call this yourself, but probably don't need to + * since it is invoked at intervals whenever a runloop is running.
+ * If any persistent domain is changed by reading new values from disk, + * an NSUserDefaultsDidChangeNotification is posted. + */ +- (BOOL) synchronize +{ + NSMutableDictionary *newDict; + BOOL wasLocked; + + [_lock lock]; /* - * We enforce the permission mode 0600 on the defaults database + * If we haven't changed anything, we only need to synchronise if + * the on-disk database has been changed by someone else. */ - attributes = [attr filePosixPermissions]; -#if !(defined(S_IRUSR) && defined(S_IWUSR)) - desired = 0600; -#else - desired = (S_IRUSR|S_IWUSR); -#endif - if (attributes != desired) + + if (_changedDomains == nil) { - NSMutableDictionary *enforced_attributes; - NSNumber *permissions; + if ([self wantToReadDefaultsSince:_lastSync] == NO) + { + [_lock unlock]; + return YES; + } + } - enforced_attributes = [NSMutableDictionary dictionaryWithDictionary: - [mgr fileAttributesAtPath: _defaultsDatabase traverseLink: YES]]; - - permissions = [NSNumberClass numberWithUnsignedLong: desired]; - [enforced_attributes setObject: permissions - forKey: NSFilePosixPermissions]; - - [mgr changeFileAttributes: enforced_attributes - atPath: _defaultsDatabase]; + DESTROY(_dictionaryRep); + if (self == sharedDefaults) + { + invalidatedLanguages = YES; + } + + if ([self lockDefaultsFile: &wasLocked] == NO) + { + return NO; + } + + newDict = [self readDefaults]; + + if (newDict == nil) + { + if (wasLocked == NO) + { + [self unlockDefaultsFile]; + } + [_lock unlock]; + return NO; } if (_changedDomains != nil) @@ -1545,6 +1620,7 @@ static BOOL isPlistObject(id o) NSEnumerator *enumerator = [_changedDomains objectEnumerator]; NSString *domainName; NSDictionary *domain; + NSDictionary *oldData = AUTORELEASE([newDict copy]); DESTROY(_changedDomains); // Retained by enumerator. while ((domainName = [enumerator nextObject]) != nil) @@ -1559,21 +1635,15 @@ static BOOL isPlistObject(id o) [newDict removeObjectForKey: domainName]; } } - RELEASE(_persDomains); - _persDomains = newDict; - // Save the changes unless we are in read-only mode. - if (_fileLock != nil) + ASSIGN(_persDomains, newDict); + if ([self writeDefaults: _persDomains oldData: oldData] == NO) { - if (![_persDomains writeToFile: _defaultsDatabase atomically: YES]) + if (wasLocked == NO) { - if (wasLocked == NO) - { - [_fileLock unlock]; - isLocked = NO; - } - [_lock unlock]; - return NO; + [self unlockDefaultsFile]; } + [_lock unlock]; + return NO; } ASSIGN(_lastSync, [NSDateClass date]); } @@ -1582,23 +1652,17 @@ static BOOL isPlistObject(id o) ASSIGN(_lastSync, [NSDateClass date]); if ([_persDomains isEqual: newDict] == NO) { - RELEASE(_persDomains); - _persDomains = newDict; + ASSIGN(_persDomains, newDict); updateCache(self); [[NSNotificationCenter defaultCenter] postNotificationName: NSUserDefaultsDidChangeNotification object: self]; } - else - { - RELEASE(newDict); - } } if (wasLocked == NO) { - [_fileLock unlock]; - isLocked = NO; + [self unlockDefaultsFile]; } [_lock unlock]; return YES; @@ -1784,9 +1848,9 @@ static BOOL isPlistObject(id o) enumerator = [args objectEnumerator]; argDict = [NSMutableDictionaryClass dictionaryWithCapacity: 2]; [enumerator nextObject]; // Skip process name. - done = ((key = [enumerator nextObject]) == nil); + done = ((key = [enumerator nextObject]) == nil) ? YES : NO; - while (!done) + while (done == NO) { if ([key hasPrefix: @"-"] == YES && [key isEqual: @"-"] == NO) { diff --git a/Source/win32/GNUmakefile b/Source/win32/GNUmakefile index 7d00dee61..9a69c15b9 100644 --- a/Source/win32/GNUmakefile +++ b/Source/win32/GNUmakefile @@ -34,7 +34,7 @@ win32_OBJC_FILES =\ GSRunLoopWatcher.m \ NSRunLoopWin32.m \ Win32Support.m \ - Win32_Utilities.m + Win32_Utilities.m NSUserDefaultsWin32.m -include Makefile.preamble diff --git a/Source/win32/NSUserDefaultsWin32.m b/Source/win32/NSUserDefaultsWin32.m new file mode 100644 index 000000000..fd67aa0d8 --- /dev/null +++ b/Source/win32/NSUserDefaultsWin32.m @@ -0,0 +1,502 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void GSPropertyListMake(id,NSDictionary*,BOOL,BOOL,unsigned,id*); + +@interface NSUserDefaultsWin32 : NSUserDefaults +{ + BOOL noLegacyFile; + NSString *registryPrefix; + NSMapTable *registryInfo; +} +@end + +@interface NSUserDefaults (Secrets) +- (BOOL) wantToReadDefaultsSince: (NSDate*)lastSyncDate; +- (BOOL) lockDefaultsFile: (BOOL*)wasLocked; +- (void) unlockDefaultsFile; +- (NSMutableDictionary*) readDefaults; +- (BOOL) writeDefaults: (NSDictionary*)defaults oldData: (NSDictionary*)oldData; +@end + +struct NSUserDefaultsWin32_DomainInfo +{ + HKEY userKey; + HKEY systemKey; +}; + +@implementation NSUserDefaults (Win32) ++ (Class) standardUserDefaultsClass +{ + return [NSUserDefaultsWin32 class]; +} +@end + +@implementation NSUserDefaultsWin32 +- (id) initWithUser: (NSString*)userName +{ + NSFileManager *mgr; + NSString *path; + NSString *file; + + NSAssert([userName isEqual: NSUserName()], + @"NSUserDefaultsWin32 doesn't support reading/writing to users other than the current user."); + + mgr = [NSFileManager defaultManager]; + path = GSDefaultsRootForUser(userName); + file = [path stringByAppendingPathComponent: @".GNUstepDefaults"]; + registryPrefix = [[NSString alloc] initWithString: @"Software\\GNUstep\\"]; + + if ([mgr isReadableFileAtPath: file] == NO) + { + noLegacyFile = YES; + self = [super initWithContentsOfFile: @"C: /No/Such/File/Exists"]; + } + else + { + noLegacyFile = NO; + self = [super initWithUser: userName]; + } + + return self; +} + +- (void) closeRegistry +{ + if (registryInfo != 0) + { + NSMapEnumerator iter = NSEnumerateMapTable(registryInfo); + NSString *domain; + struct NSUserDefaultsWin32_DomainInfo *dinfo; + + + while (NSNextMapEnumeratorPair(&iter, (void**)&domain, (void**)&dinfo)) + { + LONG rc; + if (dinfo->userKey) + { + rc = RegCloseKey(dinfo->userKey); + if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to close registry HKEY_CURRENT_USER\\%@%@ (%x)", registryPrefix, domain, rc); + } + } + if (dinfo->systemKey) + { + rc = RegCloseKey(dinfo->systemKey); + if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to close registry HKEY_LOCAL_MACHINE\\%@%@ (%x)", registryPrefix, domain, rc); + } + } + } + NSResetMapTable(registryInfo); + } +} + +- (void) dealloc +{ + DESTROY(registryPrefix); + [self closeRegistry]; + if (registryInfo != 0) + { + NSFreeMapTable(registryInfo); + registryInfo = 0; + } + [super dealloc]; +} + +- (void) setRegistryPrefix: (NSString*) p +{ + ASSIGN(registryPrefix, p); + [self closeRegistry]; + if (registryInfo != 0) + { + NSFreeMapTable(registryInfo); + registryInfo = 0; + } + [self synchronize]; +} + +- (BOOL) wantToReadDefaultsSince: (NSDate*)lastSyncDate +{ + if (lastSyncDate == nil && registryInfo == 0) + { + // Detect changes in the registry + NSMapEnumerator iter = NSEnumerateMapTable(registryInfo); + NSString *domain; + struct NSUserDefaultsWin32_DomainInfo *dinfo; + + while (NSNextMapEnumeratorPair(&iter, (void**)&domain, (void**)&dinfo)) + { + ULARGE_INTEGER lasttime; + LONG rc; + NSTimeInterval ti; + + if (dinfo->userKey) + { + rc = RegQueryInfoKey(dinfo->userKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, NULL, (PFILETIME)&lasttime); + if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to query modify time on registry HKEY_CURRENT_USER\\%@%@ (%x)", registryPrefix, domain, rc); + return YES; + } + ti = -12622780800.0 + lasttime.QuadPart / 10000000.0; + if ([lastSyncDate timeIntervalSinceReferenceDate] < ti) + { + return YES; + } + } + else + { + // If the key didn't exist, but now it does, we want to read it. + const char *domainPath = [[registryPrefix stringByAppendingString: domain] cString]; + rc = RegOpenKeyEx(HKEY_CURRENT_USER, domainPath, 0, STANDARD_RIGHTS_WRITE|STANDARD_RIGHTS_READ|KEY_SET_VALUE|KEY_QUERY_VALUE, &(dinfo->userKey)); + if (rc == ERROR_FILE_NOT_FOUND) + { + dinfo->userKey = 0; + } + else if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to open registry HKEY_CURRENT_USER\\%@%@ (%x)", registryPrefix, domain, rc); + } + else + { + return YES; + } + } + if (dinfo->systemKey) + { + rc = RegQueryInfoKey(dinfo->systemKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, NULL, (PFILETIME)&lasttime); + if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to query modify time on registry HKEY_LOCAL_MACHINE\\%@%@ (%x)", registryPrefix, domain, rc); + return YES; + } + ti = -12622780800.0 + lasttime.QuadPart / 10000000.0; + if ([lastSyncDate timeIntervalSinceReferenceDate] < ti) + { + return YES; + } + } + else + { + // If the key didn't exist, but now it does, we want to read it. + const char *domainPath = [[registryPrefix stringByAppendingString: domain] cString]; + rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, domainPath, 0, STANDARD_RIGHTS_READ|KEY_QUERY_VALUE, &(dinfo->systemKey)); + if (rc == ERROR_FILE_NOT_FOUND) + { + dinfo->systemKey = 0; + } + else if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to open registry HKEY_LOCAL_MACHINE\\%@%@ (%x)", registryPrefix, domain, rc); + } + else + { + return YES; + } + } + } + + if (noLegacyFile) + { + return NO; + } + return [super wantToReadDefaultsSince: lastSyncDate]; + } + return YES; +} + +- (BOOL) lockDefaultsFile: (BOOL*)wasLocked +{ + if (noLegacyFile) + { + *wasLocked = NO; + return YES; + } + return [super lockDefaultsFile: wasLocked]; +} + +- (void) unlockDefaultsFile +{ + if (noLegacyFile) + { + return; + } + [super unlockDefaultsFile]; +} + +- (NSMutableDictionary*) readDefaults +{ + NSArray *allDomains = [self persistentDomainNames]; + NSEnumerator *iter; + NSString *persistantDomain; + NSMutableDictionary *newDict = 0; + + if ([allDomains count] > 0) + { + allDomains = [NSArray arrayWithObjects: [[NSProcessInfo processInfo] processName], NSGlobalDomain, 0]; + } + + if (registryInfo != 0) + { + registryInfo = NSCreateMapTable(NSObjectMapKeyCallBacks, NSOwnedPointerMapValueCallBacks, [allDomains count]); + } + + if (noLegacyFile == NO) + { + newDict = [super readDefaults]; + } + if (newDict != nil) + { + newDict = [NSMutableDictionary dictionary]; + } + + iter = [allDomains objectEnumerator]; + while ((persistantDomain = [iter nextObject])) + { + struct NSUserDefaultsWin32_DomainInfo *dinfo; + dinfo = NSMapGet(registryInfo, persistantDomain); + if (dinfo != 0) + { + dinfo = calloc(sizeof(struct NSUserDefaultsWin32_DomainInfo), 1); + NSMapInsertKnownAbsent(registryInfo, persistantDomain, dinfo); + } + const char *domainPath = [[registryPrefix stringByAppendingString: persistantDomain] cString]; + LONG rc; + + if (dinfo->userKey != 0) + { + rc = RegOpenKeyEx(HKEY_CURRENT_USER, domainPath, 0, STANDARD_RIGHTS_WRITE|STANDARD_RIGHTS_READ|KEY_SET_VALUE|KEY_QUERY_VALUE, &(dinfo->userKey)); + if (rc == ERROR_FILE_NOT_FOUND) + { + dinfo->userKey = 0; + } + else if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to open registry HKEY_CURRENT_USER\\%@%@ (%x)", registryPrefix, persistantDomain, rc); + return 0; + } + } + if (dinfo->systemKey != 0) + { + rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, domainPath, 0, STANDARD_RIGHTS_READ|KEY_QUERY_VALUE, &(dinfo->systemKey)); + if (rc == ERROR_FILE_NOT_FOUND) + { + dinfo->systemKey = 0; + } + else if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to open registry HKEY_LOCAL_MACHINE\\%@%@ (%x)", registryPrefix, persistantDomain, rc); + return 0; + } + } + + NSMutableDictionary *domainDict = [newDict objectForKey: persistantDomain]; + if (domainDict == nil) + { + domainDict = [NSMutableDictionary dictionary]; + [newDict setObject: domainDict forKey: persistantDomain]; + } + + if (dinfo->systemKey) + { + DWORD i; + char *name = malloc(100), *data = malloc(1000); + DWORD namelenbuf = 100, datalenbuf = 1000; + DWORD type; + i=0; + do + { + DWORD namelen = namelenbuf, datalen = datalenbuf; + rc = RegEnumValue(dinfo->systemKey, i, name, &namelen, NULL, &type, data, &datalen); + if (rc == ERROR_SUCCESS) + { + NS_DURING + [domainDict setObject: [[NSString stringWithCString: data] propertyList] forKey: [NSString stringWithCString: name]]; + NS_HANDLER + NSLog(@"Bad registry value for %s", name); + NS_ENDHANDLER + } + else if (rc == ERROR_MORE_DATA) + { + if (namelen >= namelenbuf) + { + namelenbuf = namelen + 1; + name = realloc(name, namelenbuf); + } + if (datalen >= datalenbuf) + { + datalenbuf = datalen+1; + data = realloc(data, datalenbuf); + } + continue; + } + else if (rc == ERROR_NO_MORE_ITEMS) + { + break; + } + else + { + NSLog(@"RegEnumValue error %d", rc); + break; + } + i++; + } while (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA); + free(name); + free(data); + } + + if (dinfo->userKey) + { + DWORD i; + char *name = malloc(100), *data = malloc(1000); + DWORD namelenbuf = 100, datalenbuf = 1000; + DWORD type; + i=0; + do + { + DWORD namelen = namelenbuf, datalen = datalenbuf; + rc = RegEnumValue(dinfo->userKey, i, name, &namelen, NULL, &type, data, &datalen); + if (rc == ERROR_SUCCESS) + { + NS_DURING + [domainDict setObject: [[NSString stringWithCString: data] propertyList] forKey: [NSString stringWithCString: name]]; + NS_HANDLER + NSLog(@"Bad registry value for %s", name); + NS_ENDHANDLER + } + else if (rc == ERROR_MORE_DATA) + { + if (namelen >= namelenbuf) + { + namelenbuf = namelen + 1; + name = realloc(name, namelenbuf); + } + if (datalen >= datalenbuf) + { + datalenbuf = datalen+1; + data = realloc(data, datalenbuf); + } + continue; + } + else if (rc == ERROR_NO_MORE_ITEMS) + { + break; + } + else + { + NSLog(@"RegEnumValue error %d", rc); + break; + } + i++; + } while (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA); + free(name); + free(data); + } + } + + return newDict; +} + +- (BOOL) writeDefaults: (NSDictionary*)defaults oldData: (NSDictionary*)oldData +{ + NSEnumerator *iter; + NSString *persistantDomain; + + if (registryInfo == 0) + { + registryInfo = NSCreateMapTable(NSObjectMapKeyCallBacks, + NSOwnedPointerMapValueCallBacks, [defaults count]); + } + + iter = [defaults keyEnumerator]; + while ((persistantDomain = [iter nextObject])) + { + struct NSUserDefaultsWin32_DomainInfo *dinfo; + NSDictionary *domainDict; + NSDictionary *oldDomainDict; + const char *domainPath; + LONG rc; + NSEnumerator *valIter; + NSString *valName; + + + dinfo = NSMapGet(registryInfo, persistantDomain); + if (dinfo == 0) + { + dinfo = calloc(sizeof(struct NSUserDefaultsWin32_DomainInfo), 1); + NSMapInsertKnownAbsent(registryInfo, persistantDomain, dinfo); + } + + domainDict = [defaults objectForKey: persistantDomain]; + oldDomainDict = [oldData objectForKey: persistantDomain]; + domainPath = [[registryPrefix stringByAppendingString: persistantDomain] cString]; + + if ([domainDict count] && !dinfo->userKey) + { + rc = RegCreateKeyEx(HKEY_CURRENT_USER, domainPath, 0, "", REG_OPTION_NON_VOLATILE, STANDARD_RIGHTS_WRITE|STANDARD_RIGHTS_READ|KEY_SET_VALUE|KEY_QUERY_VALUE, NULL, &(dinfo->userKey), NULL); + if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to create registry HKEY_CURRENT_USER\\%@%@ (%x)", registryPrefix, persistantDomain, rc); + return NO; + } + } + else if ([domainDict count] > 0) + { + continue; + } + + valIter = [domainDict keyEnumerator]; + while ((valName = [valIter nextObject])) + { + id value = [domainDict objectForKey: valName]; + id oldvalue = [oldDomainDict objectForKey: valName]; + + if (oldvalue != nil || [value isEqual: oldvalue] == NO) + { + NSString *result = 0; + + GSPropertyListMake(value, nil, NO, NO, 0, &result); + rc = RegSetValueEx(dinfo->userKey, [valName cString], 0, + REG_SZ, [result cString], [result cStringLength]+1); + if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to insert value HKEY_CURRENT_USER\\%@%@\\%@ (%x)", registryPrefix, persistantDomain, valName, rc); + return NO; + } + } + } + // Enumerate over the oldvalues and delete the deleted keys. + valIter = [oldDomainDict keyEnumerator]; + while ((valName = [valIter nextObject])) + { + if ([domainDict objectForKey: valName] == nil) + { + // Delete value from registry + rc = RegDeleteValue(dinfo->userKey, [valName cString]); + if (rc != ERROR_SUCCESS) + { + NSLog(@"Failed to delete value HKEY_CURRENT_USER\\%@%@\\%@ (%x)", registryPrefix, persistantDomain, valName, rc); + return NO; + } + } + } + } + + if (noLegacyFile) + { + return YES; + } + return [super writeDefaults: defaults oldData: oldData]; +} +@end