2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Chris Parker
28 #include <CoreFoundation/CFPreferences.h>
29 #include <CoreFoundation/CFURLAccess.h>
30 #include <CoreFoundation/CFUserNotification.h>
31 #include <CoreFoundation/CFPropertyList.h>
32 #include <CoreFoundation/CFBundle.h>
33 #include <CoreFoundation/CFNumber.h>
34 #include <CoreFoundation/CFPriv.h>
35 #include <CoreFoundation/CFUtilitiesPriv.h>
36 #include "CFInternal.h"
39 #if defined(__WIN32__)
42 #if DEBUG_PREFERENCES_MEMORY
43 #include "../Tests/CFCountingAllocator.c"
46 struct __CFPreferencesDomain
{
48 /* WARNING - not copying the callbacks; we know they are always static structs */
49 const _CFPreferencesDomainCallBacks
*_callBacks
;
54 CONST_STRING_DECL(kCFPreferencesAnyApplication
, "kCFPreferencesAnyApplication")
55 CONST_STRING_DECL(kCFPreferencesAnyHost
, "kCFPreferencesAnyHost")
56 CONST_STRING_DECL(kCFPreferencesAnyUser
, "kCFPreferencesAnyUser")
57 CONST_STRING_DECL(kCFPreferencesCurrentApplication
, "kCFPreferencesCurrentApplication")
58 CONST_STRING_DECL(kCFPreferencesCurrentHost
, "kCFPreferencesCurrentHost")
59 CONST_STRING_DECL(kCFPreferencesCurrentUser
, "kCFPreferencesCurrentUser")
62 static CFAllocatorRef _preferencesAllocator
= NULL
;
63 __private_extern__ CFAllocatorRef
__CFPreferencesAllocator(void) {
64 if (!_preferencesAllocator
) {
65 #if DEBUG_PREFERENCES_MEMORY
66 _preferencesAllocator
= CFCountingAllocatorCreate(NULL
);
68 _preferencesAllocator
= __CFGetDefaultAllocator();
69 CFRetain(_preferencesAllocator
);
72 return _preferencesAllocator
;
75 // declaration for telling the
76 void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef
);
78 #if DEBUG_PREFERENCES_MEMORY
79 #warning Preferences debugging on
80 CF_EXPORT
void CFPreferencesDumpMem(void) {
81 if (_preferencesAllocator
) {
82 // CFCountingAllocatorPrintSummary(_preferencesAllocator);
83 CFCountingAllocatorPrintPointers(_preferencesAllocator
);
85 // CFCountingAllocatorReset(_preferencesAllocator);
89 static unsigned long __CFSafeLaunchLevel
= 0;
91 static CFURLRef
_preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
92 CFAllocatorRef alloc
= __CFPreferencesAllocator();
96 // if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted
97 if (userName
== kCFPreferencesAnyUser
) {
98 if (!home
) home
= CFURLCreateWithFileSystemPath(alloc
, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle
, true);
100 if (hostName
== kCFPreferencesCurrentHost
) url
= home
;
102 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Network/"), kCFURLPOSIXPathStyle
, true, home
);
107 home
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
109 url
= (safeLevel
> 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle
, true, home
) :
110 CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle
, true, home
);
113 if (hostName
!= kCFPreferencesAnyHost
) {
115 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("ByHost/"), kCFURLPOSIXPathStyle
, true, home
);
126 static CFURLRef
_preferencesDirectoryForUserHost(CFStringRef userName
, CFStringRef hostName
) {
127 return _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, __CFSafeLaunchLevel
);
130 // Bindings internals
131 __private_extern__ CFSpinLock_t userDefaultsLock
= 0;
132 __private_extern__
void *userDefaults
= NULL
;
134 void _CFPreferencesSetStandardUserDefaults(void *sudPtr
) {
135 __CFSpinLock(&userDefaultsLock
);
136 userDefaults
= sudPtr
;
137 __CFSpinUnlock(&userDefaultsLock
);
141 #define CF_OBJC_KVO_WILLCHANGE(obj, sel)
142 #define CF_OBJC_KVO_DIDCHANGE(obj, sel)
145 static Boolean __CFPreferencesWritesXML
= false;
147 Boolean
__CFPreferencesShouldWriteXML(void) {
148 return __CFPreferencesWritesXML
;
151 void __CFPreferencesCheckFormatType(void) {
152 static int checked
= 0;
155 __CFPreferencesWritesXML
= CFPreferencesGetAppBooleanValue(CFSTR("CFPreferencesWritesXML"), kCFPreferencesCurrentApplication
, NULL
);
159 static CFSpinLock_t domainCacheLock
= 0;
160 static CFMutableDictionaryRef domainCache
= NULL
; // mutable
164 CFTypeRef
CFPreferencesCopyValue(CFStringRef key
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
165 CFPreferencesDomainRef domain
;
166 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
167 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
169 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
171 return _CFPreferencesDomainCreateValueForKey(domain
, key
);
177 CFDictionaryRef
CFPreferencesCopyMultiple(CFArrayRef keysToFetch
, CFStringRef appName
, CFStringRef userName
, CFStringRef hostName
) {
178 CFPreferencesDomainRef domain
;
179 CFMutableDictionaryRef result
;
182 CFAssert1(appName
!= NULL
&& userName
!= NULL
&& hostName
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
183 __CFGenericValidateType(appName
, CFStringGetTypeID());
184 __CFGenericValidateType(userName
, CFStringGetTypeID());
185 __CFGenericValidateType(hostName
, CFStringGetTypeID());
187 domain
= _CFPreferencesStandardDomain(appName
, userName
, hostName
);
188 if (!domain
) return NULL
;
190 return _CFPreferencesDomainDeepCopyDictionary(domain
);
192 __CFGenericValidateType(keysToFetch
, CFArrayGetTypeID());
193 count
= CFArrayGetCount(keysToFetch
);
194 result
= CFDictionaryCreateMutable(CFGetAllocator(domain
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
195 if (!result
) return NULL
;
196 for (idx
= 0; idx
< count
; idx
++) {
197 CFStringRef key
= CFArrayGetValueAtIndex(keysToFetch
, idx
);
198 CFPropertyListRef value
;
199 __CFGenericValidateType(key
, CFStringGetTypeID());
200 value
= _CFPreferencesDomainCreateValueForKey(domain
, key
);
202 CFDictionarySetValue(result
, key
, value
);
210 void CFPreferencesSetValue(CFStringRef key
, CFTypeRef value
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
211 CFPreferencesDomainRef domain
;
212 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
213 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
215 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
218 __CFSpinLock(&userDefaultsLock
);
220 __CFSpinUnlock(&userDefaultsLock
);
221 CF_OBJC_KVO_WILLCHANGE(defs
, key
);
222 _CFPreferencesDomainSet(domain
, key
, value
);
223 _CFApplicationPreferencesDomainHasChanged(domain
);
224 CF_OBJC_KVO_DIDCHANGE(defs
, key
);
229 void CFPreferencesSetMultiple(CFDictionaryRef keysToSet
, CFArrayRef keysToRemove
, CFStringRef appName
, CFStringRef userName
, CFStringRef hostName
) {
230 CFPreferencesDomainRef domain
;
232 CFAssert1(appName
!= NULL
&& userName
!= NULL
&& hostName
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
233 if (keysToSet
) __CFGenericValidateType(keysToSet
, CFDictionaryGetTypeID());
234 if (keysToRemove
) __CFGenericValidateType(keysToRemove
, CFArrayGetTypeID());
235 __CFGenericValidateType(appName
, CFStringGetTypeID());
236 __CFGenericValidateType(userName
, CFStringGetTypeID());
237 __CFGenericValidateType(hostName
, CFStringGetTypeID());
239 CFTypeRef
*keys
= NULL
;
241 CFIndex numOfKeysToSet
= 0;
243 domain
= _CFPreferencesStandardDomain(appName
, userName
, hostName
);
246 CFAllocatorRef alloc
= CFGetAllocator(domain
);
249 __CFSpinLock(&userDefaultsLock
);
251 __CFSpinUnlock(&userDefaultsLock
);
253 if (keysToSet
&& (count
= CFDictionaryGetCount(keysToSet
))) {
254 numOfKeysToSet
= count
;
255 keys
= CFAllocatorAllocate(alloc
, 2*count
*sizeof(CFTypeRef
), 0);
257 values
= &(keys
[count
]);
258 CFDictionaryGetKeysAndValues(keysToSet
, keys
, values
);
259 for (idx
= 0; idx
< count
; idx
++) {
260 CF_OBJC_KVO_WILLCHANGE(defs
, keys
[idx
]);
261 _CFPreferencesDomainSet(domain
, keys
[idx
], values
[idx
]);
265 if (keysToRemove
&& (count
= CFArrayGetCount(keysToRemove
))) {
266 for (idx
= 0; idx
< count
; idx
++) {
267 CFStringRef removedKey
= CFArrayGetValueAtIndex(keysToRemove
, idx
);
268 CF_OBJC_KVO_WILLCHANGE(defs
, removedKey
);
269 _CFPreferencesDomainSet(domain
, removedKey
, NULL
);
274 _CFApplicationPreferencesDomainHasChanged(domain
);
276 // here, we have to do things in reverse order.
278 count
= CFArrayGetCount(keysToRemove
);
279 for(idx
= count
- 1; idx
>= 0; idx
--) {
280 CF_OBJC_KVO_DIDCHANGE(defs
, CFArrayGetValueAtIndex(keysToRemove
, idx
));
284 if(numOfKeysToSet
> 0) {
285 for(idx
= numOfKeysToSet
- 1; idx
>= 0; idx
--) {
286 CF_OBJC_KVO_DIDCHANGE(defs
, keys
[idx
]);
290 if(keys
) CFAllocatorDeallocate(alloc
, keys
);
293 Boolean
CFPreferencesSynchronize(CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
294 CFPreferencesDomainRef domain
;
295 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
297 __CFPreferencesCheckFormatType();
299 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
300 if(domain
) _CFApplicationPreferencesDomainHasChanged(domain
);
302 return domain
? _CFPreferencesDomainSynchronize(domain
) : false;
305 CFArrayRef
CFPreferencesCopyApplicationList(CFStringRef userName
, CFStringRef hostName
) {
307 CFAssert1(userName
!= NULL
&& hostName
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__
);
308 array
= _CFPreferencesCreateDomainList(userName
, hostName
);
312 CFArrayRef
CFPreferencesCopyKeyList(CFStringRef appName
, CFStringRef userName
, CFStringRef hostName
) {
313 CFPreferencesDomainRef domain
;
314 CFAssert1(appName
!= NULL
&& userName
!= NULL
&& hostName
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
316 domain
= _CFPreferencesStandardDomain(appName
, userName
, hostName
);
321 CFAllocatorRef alloc
= __CFPreferencesAllocator();
323 CFIndex numPairs
= 0;
324 _CFPreferencesDomainGetKeysAndValues(alloc
, domain
, &buf
, &numPairs
);
328 // It would be nice to avoid this allocation....
329 result
= CFArrayCreate(alloc
, (const void **)buf
, numPairs
, &kCFTypeArrayCallBacks
);
330 CFAllocatorDeallocate(alloc
, buf
);
337 /****************************/
338 /* CFPreferencesDomain */
339 /****************************/
341 static CFStringRef
__CFPreferencesDomainCopyDescription(CFTypeRef cf
) {
342 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("<Private CFType 0x%x>\n"), (UInt32
)cf
);
345 static void __CFPreferencesDomainDeallocate(CFTypeRef cf
) {
346 const struct __CFPreferencesDomain
*domain
= cf
;
347 CFAllocatorRef alloc
= __CFPreferencesAllocator();
348 domain
->_callBacks
->freeDomain(alloc
, domain
->_context
, domain
->_domain
);
349 if (domain
->_context
) CFRelease(domain
->_context
);
352 static CFTypeID __kCFPreferencesDomainTypeID
= _kCFRuntimeNotATypeID
;
354 static const CFRuntimeClass __CFPreferencesDomainClass
= {
356 "CFPreferencesDomain",
359 __CFPreferencesDomainDeallocate
,
363 __CFPreferencesDomainCopyDescription
366 /* This is called once at CFInitialize() time. */
367 __private_extern__
void __CFPreferencesDomainInitialize(void) {
368 __kCFPreferencesDomainTypeID
= _CFRuntimeRegisterClass(&__CFPreferencesDomainClass
);
371 /* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
372 __private_extern__ CFStringRef
_CFPreferencesCachePrefixForUserHost(CFStringRef userName
, CFStringRef hostName
) {
373 Boolean freeHost
= false;
375 if (userName
== kCFPreferencesCurrentUser
) {
376 userName
= CFGetUserName();
377 } else if (userName
== kCFPreferencesAnyUser
) {
378 userName
= CFSTR("*");
381 if (hostName
== kCFPreferencesCurrentHost
) {
382 hostName
= __CFCopyEthernetAddrString();
383 if (!hostName
) hostName
= _CFStringCreateHostName();
385 } else if (hostName
== kCFPreferencesAnyHost
) {
386 hostName
= CFSTR("*");
388 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@/%@/"), userName
, hostName
);
389 if (freeHost
&& hostName
!= NULL
) CFRelease(hostName
);
393 // It would be nice if we could remember the key for "well-known" combinations, so we're not constantly allocing more strings.... - REW 2/3/99
394 static CFStringRef
_CFPreferencesStandardDomainCacheKey(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
395 CFStringRef prefix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
396 CFStringRef result
= NULL
;
399 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@%@"), prefix
, domainName
);
405 #if defined(__MACOS8__)
406 // Define a custom hash function so that we don't inadvertantly make the
407 // result of CFHash() on a string persistent, and locked-in for all time.
408 static UInt16
hashString(CFStringRef str
) {
411 cnt
= CFStringGetLength(str
);
413 for (idx
= 0; idx
< cnt
; idx
++) {
415 h
+= CFStringGetCharacterAtIndex(str
, idx
);
417 return (h
>> 16) ^ (h
& 0xFFFF);
421 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
422 CFURLRef theURL
= NULL
;
423 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
424 #if defined(__MACH__)
425 CFURLRef prefDir
= _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, safeLevel
);
427 CFStringRef fileName
;
428 Boolean mustFreeAppName
= false;
430 if (!prefDir
) return NULL
;
431 if (domainName
== kCFPreferencesAnyApplication
) {
432 appName
= CFSTR(".GlobalPreferences");
433 } else if (domainName
== kCFPreferencesCurrentApplication
) {
434 CFBundleRef mainBundle
= CFBundleGetMainBundle();
435 appName
= mainBundle
? CFBundleGetIdentifier(mainBundle
) : NULL
;
436 if (!appName
|| CFStringGetLength(appName
) == 0) {
437 appName
= _CFProcessNameString();
440 appName
= domainName
;
442 if (userName
!= kCFPreferencesAnyUser
) {
443 if (hostName
== kCFPreferencesAnyHost
) {
444 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
445 } else if (hostName
== kCFPreferencesCurrentHost
) {
446 CFStringRef host
= __CFCopyEthernetAddrString();
447 if (!host
) host
= _CFStringCreateHostName();
448 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, host
);
451 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostName
);
454 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
456 if (mustFreeAppName
) {
460 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLPOSIXPathStyle
, false, prefDir
);
461 if (prefDir
) CFRelease(prefDir
);
465 #error Do not know where to store NSUserDefaults on this platform
470 static CFURLRef
_CFPreferencesURLForStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
471 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName
, userName
, hostName
, __CFSafeLaunchLevel
);
474 CFPreferencesDomainRef
_CFPreferencesStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
475 CFPreferencesDomainRef domain
;
476 CFStringRef domainKey
;
477 Boolean shouldReleaseDomain
= true;
478 domainKey
= _CFPreferencesStandardDomainCacheKey(domainName
, userName
, hostName
);
479 __CFSpinLock(&domainCacheLock
);
481 CFAllocatorRef alloc
= __CFPreferencesAllocator();
482 domainCache
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
484 domain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
485 __CFSpinUnlock(&domainCacheLock
);
487 // Domain's not in the cache; load from permanent storage
488 CFURLRef theURL
= _CFPreferencesURLForStandardDomain(domainName
, userName
, hostName
);
490 domain
= _CFPreferencesDomainCreate(theURL
, &__kCFXMLPropertyListDomainCallBacks
);
491 if (userName
== kCFPreferencesAnyUser
) {
492 _CFPreferencesDomainSetIsWorldReadable(domain
, true);
496 __CFSpinLock(&domainCacheLock
);
497 if (domain
&& domainCache
) {
498 // We've just synthesized a domain & we're about to throw it in the domain cache. The problem is that someone else might have gotten in here behind our backs, so we can't just blindly set the domain (3021920). We'll need to check to see if this happened, and compensate.
499 CFPreferencesDomainRef checkDomain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
501 // Someone got in here ahead of us, so we shouldn't smash the domain we're given. checkDomain is the current version, we should use that.
502 // checkDomain was retrieved with a Get, so we don't want to over-release.
503 shouldReleaseDomain
= false;
504 CFRelease(domain
); // release the domain we synthesized earlier.
505 domain
= checkDomain
; // repoint it at the domain picked up out of the cache.
507 // We must not have found the domain in the cache, so it's ok for us to put this in.
508 CFDictionarySetValue(domainCache
, domainKey
, domain
);
510 if(shouldReleaseDomain
) CFRelease(domain
);
512 __CFSpinUnlock(&domainCacheLock
);
514 CFRelease(domainKey
);
518 static void __CFPreferencesPerformSynchronize(const void *key
, const void *value
, void *context
) {
519 CFPreferencesDomainRef domain
= (CFPreferencesDomainRef
)value
;
520 Boolean
*cumulativeResult
= (Boolean
*)context
;
521 if (!_CFPreferencesDomainSynchronize(domain
)) *cumulativeResult
= false;
524 __private_extern__ Boolean
_CFSynchronizeDomainCache(void) {
525 Boolean result
= true;
526 __CFSpinLock(&domainCacheLock
);
528 CFDictionaryApplyFunction(domainCache
, __CFPreferencesPerformSynchronize
, &result
);
530 __CFSpinUnlock(&domainCacheLock
);
534 __private_extern__
void _CFPreferencesPurgeDomainCache(void) {
535 _CFSynchronizeDomainCache();
536 __CFSpinLock(&domainCacheLock
);
538 CFRelease(domainCache
);
541 __CFSpinUnlock(&domainCacheLock
);
544 __private_extern__ CFArrayRef
_CFPreferencesCreateDomainList(CFStringRef userName
, CFStringRef hostName
) {
545 #if 0 && defined(__WIN32__)
546 DWORD idx
, numSubkeys
, maxSubKey
, cnt
;
547 CFMutableArrayRef retVal
;
549 id
*list
, buffer
[512];
550 result
= RegQueryInfoKeyA(_masterKey
, NULL
, NULL
, NULL
, &numSubkeys
, &maxSubKey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
551 if (result
!= ERROR_SUCCESS
) {
552 NSLog(@
"%@: cannot query master key info; %d", _NSMethodExceptionProem(self
, _cmd
), result
);
553 return [NSArray array
];
556 list
= (numSubkeys
<= 512) ? buffer
: NSZoneMalloc(NULL
, numSubkeys
* sizeof(void *));
557 if (_useCStringDomains
< 0)
558 _useCStringDomains
= (NSWindows95OperatingSystem
== [[NSProcessInfo processInfo
] operatingSystem
]);
559 if (_useCStringDomains
) {
560 for (idx
= 0, cnt
= 0; idx
< numSubkeys
; idx
++) {
561 char name
[maxSubKey
+ 1];
562 DWORD nameSize
= maxSubKey
;
563 if (RegEnumKeyExA(_masterKey
, idx
, name
, &nameSize
, NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
)
564 list
[cnt
++] = [NSString stringWithCString
:name length
:nameSize
];
567 for (idx
= 0, cnt
= 0; idx
< numSubkeys
; idx
++) {
568 unichar name
[maxSubKey
+ 1];
569 DWORD nameSize
= maxSubKey
;
570 if (RegEnumKeyExW(_masterKey
, idx
, name
, &nameSize
, NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
)
571 list
[cnt
++] = [NSString stringWithCharacters
:name length
:nameSize
];
574 retVal
= [NSArray arrayWithObjects
:list count
:cnt
];
575 if (list
!= buffer
) NSZoneFree(NULL
, list
);
577 #elif defined(__MACH__) || defined(__svr4__) || defined(__hpux__)
578 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
580 CFMutableArrayRef marray
;
581 CFStringRef
*cachedDomainKeys
;
582 CFPreferencesDomainRef
*cachedDomains
;
586 CFURLRef prefDir
= _preferencesDirectoryForUserHost(userName
, hostName
);
591 if (hostName
== kCFPreferencesAnyHost
) {
592 suffix
= CFStringCreateWithCString(prefAlloc
, ".plist", kCFStringEncodingASCII
);
593 } else if (hostName
== kCFPreferencesCurrentHost
) {
594 CFStringRef host
= __CFCopyEthernetAddrString();
595 if (!host
) host
= _CFStringCreateHostName();
596 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), host
);
599 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostName
);
601 suffixLen
= CFStringGetLength(suffix
);
603 domains
= CFURLCreatePropertyFromResource(prefAlloc
, prefDir
, kCFURLFileDirectoryContents
, NULL
);
606 marray
= CFArrayCreateMutableCopy(prefAlloc
, 0, domains
);
609 marray
= CFArrayCreateMutable(prefAlloc
, 0, & kCFTypeArrayCallBacks
);
611 for (idx
= CFArrayGetCount(marray
)-1; idx
>= 0; idx
--) {
612 CFURLRef url
= CFArrayGetValueAtIndex(marray
, idx
);
613 CFStringRef string
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
614 if (!CFStringHasSuffix(string
, suffix
)) {
615 CFArrayRemoveValueAtIndex(marray
, idx
);
617 CFStringRef dom
= CFStringCreateWithSubstring(prefAlloc
, string
, CFRangeMake(0, CFStringGetLength(string
) - suffixLen
));
618 if (CFEqual(dom
, CFSTR(".GlobalPreferences"))) {
619 CFArraySetValueAtIndex(marray
, idx
, kCFPreferencesAnyApplication
);
621 CFArraySetValueAtIndex(marray
, idx
, dom
);
629 // Now add any domains added in the cache; delete any that have been deleted in the cache
630 __CFSpinLock(&domainCacheLock
);
632 __CFSpinUnlock(&domainCacheLock
);
635 cnt
= CFDictionaryGetCount(domainCache
);
636 cachedDomainKeys
= CFAllocatorAllocate(prefAlloc
, 2 * cnt
* sizeof(CFStringRef
), 0);
637 cachedDomains
= (CFPreferencesDomainRef
*)(cachedDomainKeys
+ cnt
);
638 CFDictionaryGetKeysAndValues(domainCache
, (const void **)cachedDomainKeys
, (const void **)cachedDomains
);
639 __CFSpinUnlock(&domainCacheLock
);
640 suffix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
641 suffixLen
= CFStringGetLength(suffix
);
643 for (idx
= 0; idx
< cnt
; idx
++) {
644 CFStringRef domainKey
= cachedDomainKeys
[idx
];
645 CFPreferencesDomainRef domain
= cachedDomains
[idx
];
646 CFStringRef domainName
;
647 CFIndex keyCount
= 0;
649 if (!CFStringHasPrefix(domainKey
, suffix
)) continue;
650 domainName
= CFStringCreateWithSubstring(prefAlloc
, domainKey
, CFRangeMake(suffixLen
, CFStringGetLength(domainKey
) - suffixLen
));
651 if (CFEqual(domainName
, CFSTR("*"))) {
652 CFRelease(domainName
);
653 domainName
= CFRetain(kCFPreferencesAnyApplication
);
654 } else if (CFEqual(domainName
, kCFPreferencesCurrentApplication
)) {
655 CFRelease(domainName
);
656 domainName
= CFRetain(_CFProcessNameString());
658 _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull
, domain
, NULL
, &keyCount
);
660 // Domain was deleted
661 SInt32 firstIndexOfValue
= CFArrayGetFirstIndexOfValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
);
662 if (0 <= firstIndexOfValue
) {
663 CFArrayRemoveValueAtIndex(marray
, firstIndexOfValue
);
665 } else if (!CFArrayContainsValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
)) {
666 CFArrayAppendValue(marray
, domainName
);
668 CFRelease(domainName
);
671 CFAllocatorDeallocate(prefAlloc
, cachedDomainKeys
);
678 // CFPreferencesDomain functions
681 CFPreferencesDomainRef
_CFPreferencesDomainCreate(CFTypeRef context
, const _CFPreferencesDomainCallBacks
*callBacks
) {
682 CFAllocatorRef alloc
= __CFPreferencesAllocator();
683 CFPreferencesDomainRef newDomain
;
684 CFAssert(callBacks
!= NULL
&& callBacks
->createDomain
!= NULL
&& callBacks
->freeDomain
!= NULL
&& callBacks
->fetchValue
!= NULL
&& callBacks
->writeValue
!= NULL
, __kCFLogAssertion
, "Cannot create a domain with NULL callbacks");
685 newDomain
= (CFPreferencesDomainRef
)_CFRuntimeCreateInstance(alloc
, __kCFPreferencesDomainTypeID
, sizeof(struct __CFPreferencesDomain
) - sizeof(CFRuntimeBase
), NULL
);
687 newDomain
->_callBacks
= callBacks
;
688 if (context
) CFRetain(context
);
689 newDomain
->_context
= context
;
690 newDomain
->_domain
= callBacks
->createDomain(alloc
, context
);
695 CFTypeRef
_CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain
, CFStringRef key
) {
696 return domain
->_callBacks
->fetchValue(domain
->_context
, domain
->_domain
, key
);
699 void _CFPreferencesDomainSet(CFPreferencesDomainRef domain
, CFStringRef key
, CFTypeRef value
) {
700 domain
->_callBacks
->writeValue(domain
->_context
, domain
->_domain
, key
, value
);
703 __private_extern__ Boolean
_CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain
) {
704 return domain
->_callBacks
->synchronize(domain
->_context
, domain
->_domain
);
707 __private_extern__
void _CFPreferencesDomainGetKeysAndValues(CFAllocatorRef alloc
, CFPreferencesDomainRef domain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
708 domain
->_callBacks
->getKeysAndValues(alloc
, domain
->_context
, domain
->_domain
, buf
, numKeyValuePairs
);
711 __private_extern__
void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain
, Boolean isWorldReadable
) {
712 if (domain
->_callBacks
->setIsWorldReadable
) {
713 domain
->_callBacks
->setIsWorldReadable(domain
->_context
, domain
->_domain
, isWorldReadable
);
717 void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain
, CFDictionaryRef dict
) {
718 CFTypeRef buf
[32], *keys
= buf
;
719 CFIndex idx
, count
= 16;
720 CFAllocatorRef alloc
= __CFPreferencesAllocator();
722 _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull
, domain
, (void ***)(&keys
), &count
);
727 _CFPreferencesDomainGetKeysAndValues(alloc
, domain
, (void ***)(&keys
), &count
);
729 for (idx
= 0; idx
< count
; idx
++) {
730 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], NULL
);
733 CFAllocatorDeallocate(alloc
, keys
);
736 if (dict
&& (count
= CFDictionaryGetCount(dict
)) != 0) {
737 CFStringRef
*newKeys
= (count
< 32) ? buf
: CFAllocatorAllocate(alloc
, count
* sizeof(CFStringRef
), 0);
738 CFDictionaryGetKeysAndValues(dict
, (const void **)newKeys
, NULL
);
739 for (idx
= 0; idx
< count
; idx
++) {
740 CFStringRef key
= newKeys
[idx
];
741 _CFPreferencesDomainSet(domain
, key
, (CFTypeRef
)CFDictionaryGetValue(dict
, key
));
743 if (((CFTypeRef
)newKeys
) != buf
) {
744 CFAllocatorDeallocate(alloc
, newKeys
);
749 CFDictionaryRef
_CFPreferencesDomainCopyDictionary(CFPreferencesDomainRef domain
) {
750 CFTypeRef
*keys
= NULL
;
752 CFAllocatorRef alloc
= __CFPreferencesAllocator();
753 CFDictionaryRef dict
= NULL
;
754 _CFPreferencesDomainGetKeysAndValues(alloc
, domain
, (void ***)&keys
, &count
);
756 CFTypeRef
*values
= keys
+ count
;
757 dict
= CFDictionaryCreate(alloc
, keys
, values
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
758 CFAllocatorDeallocate(alloc
, keys
);
763 CFDictionaryRef
_CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain
) {
764 CFDictionaryRef result
= domain
->_callBacks
->copyDomainDictionary(domain
->_context
, domain
->_domain
);
765 if(result
&& CFDictionaryGetCount(result
) == 0) {
772 Boolean
_CFPreferencesDomainExists(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
773 CFPreferencesDomainRef domain
;
775 domain
= _CFPreferencesStandardDomain(domainName
, userName
, hostName
);
777 _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull
, domain
, NULL
, &count
);
784 /* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */
785 static void *createVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
) {
786 return CFDictionaryCreateMutable(allocator
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
789 static void freeVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
, void *domain
) {
790 CFRelease((CFTypeRef
)domain
);
793 static CFTypeRef
fetchVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
) {
794 CFTypeRef result
= CFDictionaryGetValue((CFMutableDictionaryRef
)domain
, key
);
795 if (result
) CFRetain(result
);
799 static void writeVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
, CFTypeRef value
) {
801 CFDictionarySetValue((CFMutableDictionaryRef
)domain
, key
, value
);
803 CFDictionaryRemoveValue((CFMutableDictionaryRef
)domain
, key
);
806 static Boolean
synchronizeVolatileDomain(CFTypeRef context
, void *domain
) {
810 static void getVolatileKeysAndValues(CFAllocatorRef alloc
, CFTypeRef context
, void *domain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
811 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)domain
;
812 CFIndex count
= CFDictionaryGetCount(dict
);
816 if ( count
< *numKeyValuePairs
) {
817 values
= *buf
+ count
;
818 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
819 } else if (alloc
!= kCFAllocatorNull
) {
821 *buf
= CFAllocatorReallocate(alloc
, *buf
, count
* 2 * sizeof(void *), 0);
823 *buf
= CFAllocatorAllocate(alloc
, count
*2*sizeof(void *), 0);
826 values
= *buf
+ count
;
827 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
831 *numKeyValuePairs
= count
;
834 static CFDictionaryRef
copyVolatileDomainDictionary(CFTypeRef context
, void *volatileDomain
) {
835 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)volatileDomain
;
837 CFDictionaryRef result
= (CFDictionaryRef
)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict
, kCFPropertyListImmutable
);
841 const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks
= {createVolatileDomain
, freeVolatileDomain
, fetchVolatileValue
, writeVolatileValue
, synchronizeVolatileDomain
, getVolatileKeysAndValues
, copyVolatileDomainDictionary
, NULL
};