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 CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
);
91 static unsigned long __CFSafeLaunchLevel
= 0;
93 static CFURLRef
_preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
94 CFAllocatorRef alloc
= __CFPreferencesAllocator();
98 // if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted
99 if (userName
== kCFPreferencesAnyUser
) {
100 if (!home
) home
= CFURLCreateWithFileSystemPath(alloc
, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle
, true);
102 if (hostName
== kCFPreferencesCurrentHost
) url
= home
;
104 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Network/"), kCFURLPOSIXPathStyle
, true, home
);
109 home
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
111 url
= (safeLevel
> 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle
, true, home
) :
112 CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle
, true, home
);
115 if (hostName
!= kCFPreferencesAnyHost
) {
117 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("ByHost/"), kCFURLPOSIXPathStyle
, true, home
);
128 static CFURLRef
_preferencesDirectoryForUserHost(CFStringRef userName
, CFStringRef hostName
) {
129 return _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, __CFSafeLaunchLevel
);
132 // Bindings internals
133 __private_extern__ CFSpinLock_t userDefaultsLock
= 0;
134 __private_extern__
void *userDefaults
= NULL
;
136 void _CFPreferencesSetStandardUserDefaults(void *sudPtr
) {
137 __CFSpinLock(&userDefaultsLock
);
138 userDefaults
= sudPtr
;
139 __CFSpinUnlock(&userDefaultsLock
);
143 #define CF_OBJC_KVO_WILLCHANGE(obj, sel)
144 #define CF_OBJC_KVO_DIDCHANGE(obj, sel)
147 static Boolean __CFPreferencesWritesXML
= false;
149 Boolean
__CFPreferencesShouldWriteXML(void) {
150 return __CFPreferencesWritesXML
;
153 void __CFPreferencesCheckFormatType(void) {
154 static int checked
= 0;
157 __CFPreferencesWritesXML
= CFPreferencesGetAppBooleanValue(CFSTR("CFPreferencesWritesXML"), kCFPreferencesCurrentApplication
, NULL
);
161 static CFSpinLock_t domainCacheLock
= 0;
162 static CFMutableDictionaryRef domainCache
= NULL
; // mutable
166 CFTypeRef
CFPreferencesCopyValue(CFStringRef key
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
167 CFPreferencesDomainRef domain
;
168 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
169 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
171 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
173 return _CFPreferencesDomainCreateValueForKey(domain
, key
);
179 CFDictionaryRef
CFPreferencesCopyMultiple(CFArrayRef keysToFetch
, CFStringRef appName
, CFStringRef userName
, CFStringRef hostName
) {
180 CFPreferencesDomainRef domain
;
181 CFMutableDictionaryRef result
;
184 CFAssert1(appName
!= NULL
&& userName
!= NULL
&& hostName
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
185 __CFGenericValidateType(appName
, CFStringGetTypeID());
186 __CFGenericValidateType(userName
, CFStringGetTypeID());
187 __CFGenericValidateType(hostName
, CFStringGetTypeID());
189 domain
= _CFPreferencesStandardDomain(appName
, userName
, hostName
);
190 if (!domain
) return NULL
;
192 return _CFPreferencesDomainDeepCopyDictionary(domain
);
194 __CFGenericValidateType(keysToFetch
, CFArrayGetTypeID());
195 count
= CFArrayGetCount(keysToFetch
);
196 result
= CFDictionaryCreateMutable(CFGetAllocator(domain
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
197 if (!result
) return NULL
;
198 for (idx
= 0; idx
< count
; idx
++) {
199 CFStringRef key
= CFArrayGetValueAtIndex(keysToFetch
, idx
);
200 CFPropertyListRef value
;
201 __CFGenericValidateType(key
, CFStringGetTypeID());
202 value
= _CFPreferencesDomainCreateValueForKey(domain
, key
);
204 CFDictionarySetValue(result
, key
, value
);
212 void CFPreferencesSetValue(CFStringRef key
, CFTypeRef value
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
213 CFPreferencesDomainRef domain
;
214 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
215 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
217 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
220 __CFSpinLock(&userDefaultsLock
);
222 __CFSpinUnlock(&userDefaultsLock
);
223 CF_OBJC_KVO_WILLCHANGE(defs
, key
);
224 _CFPreferencesDomainSet(domain
, key
, value
);
225 _CFApplicationPreferencesDomainHasChanged(domain
);
226 CF_OBJC_KVO_DIDCHANGE(defs
, key
);
231 void CFPreferencesSetMultiple(CFDictionaryRef keysToSet
, CFArrayRef keysToRemove
, CFStringRef appName
, CFStringRef userName
, CFStringRef hostName
) {
232 CFPreferencesDomainRef domain
;
234 CFAssert1(appName
!= NULL
&& userName
!= NULL
&& hostName
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
235 if (keysToSet
) __CFGenericValidateType(keysToSet
, CFDictionaryGetTypeID());
236 if (keysToRemove
) __CFGenericValidateType(keysToRemove
, CFArrayGetTypeID());
237 __CFGenericValidateType(appName
, CFStringGetTypeID());
238 __CFGenericValidateType(userName
, CFStringGetTypeID());
239 __CFGenericValidateType(hostName
, CFStringGetTypeID());
241 CFTypeRef
*keys
= NULL
;
243 CFIndex numOfKeysToSet
= 0;
245 domain
= _CFPreferencesStandardDomain(appName
, userName
, hostName
);
248 CFAllocatorRef alloc
= CFGetAllocator(domain
);
251 __CFSpinLock(&userDefaultsLock
);
253 __CFSpinUnlock(&userDefaultsLock
);
255 if (keysToSet
&& (count
= CFDictionaryGetCount(keysToSet
))) {
256 numOfKeysToSet
= count
;
257 keys
= CFAllocatorAllocate(alloc
, 2*count
*sizeof(CFTypeRef
), 0);
259 values
= &(keys
[count
]);
260 CFDictionaryGetKeysAndValues(keysToSet
, keys
, values
);
261 for (idx
= 0; idx
< count
; idx
++) {
262 CF_OBJC_KVO_WILLCHANGE(defs
, keys
[idx
]);
263 _CFPreferencesDomainSet(domain
, keys
[idx
], values
[idx
]);
267 if (keysToRemove
&& (count
= CFArrayGetCount(keysToRemove
))) {
268 for (idx
= 0; idx
< count
; idx
++) {
269 CFStringRef removedKey
= CFArrayGetValueAtIndex(keysToRemove
, idx
);
270 CF_OBJC_KVO_WILLCHANGE(defs
, removedKey
);
271 _CFPreferencesDomainSet(domain
, removedKey
, NULL
);
276 _CFApplicationPreferencesDomainHasChanged(domain
);
278 // here, we have to do things in reverse order.
280 count
= CFArrayGetCount(keysToRemove
);
281 for(idx
= count
- 1; idx
>= 0; idx
--) {
282 CF_OBJC_KVO_DIDCHANGE(defs
, CFArrayGetValueAtIndex(keysToRemove
, idx
));
286 if(numOfKeysToSet
> 0) {
287 for(idx
= numOfKeysToSet
- 1; idx
>= 0; idx
--) {
288 CF_OBJC_KVO_DIDCHANGE(defs
, keys
[idx
]);
292 if(keys
) CFAllocatorDeallocate(alloc
, keys
);
295 Boolean
CFPreferencesSynchronize(CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
296 CFPreferencesDomainRef domain
;
297 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
299 __CFPreferencesCheckFormatType();
301 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
302 if(domain
) _CFApplicationPreferencesDomainHasChanged(domain
);
304 return domain
? _CFPreferencesDomainSynchronize(domain
) : false;
307 CFArrayRef
CFPreferencesCopyApplicationList(CFStringRef userName
, CFStringRef hostName
) {
309 CFAssert1(userName
!= NULL
&& hostName
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__
);
310 array
= _CFPreferencesCreateDomainList(userName
, hostName
);
314 CFArrayRef
CFPreferencesCopyKeyList(CFStringRef appName
, CFStringRef userName
, CFStringRef hostName
) {
315 CFPreferencesDomainRef domain
;
316 CFAssert1(appName
!= NULL
&& userName
!= NULL
&& hostName
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
318 domain
= _CFPreferencesStandardDomain(appName
, userName
, hostName
);
323 CFAllocatorRef alloc
= __CFPreferencesAllocator();
325 CFIndex numPairs
= 0;
326 _CFPreferencesDomainGetKeysAndValues(alloc
, domain
, &buf
, &numPairs
);
330 // It would be nice to avoid this allocation....
331 result
= CFArrayCreate(alloc
, (const void **)buf
, numPairs
, &kCFTypeArrayCallBacks
);
332 CFAllocatorDeallocate(alloc
, buf
);
339 /****************************/
340 /* CFPreferencesDomain */
341 /****************************/
343 static CFStringRef
__CFPreferencesDomainCopyDescription(CFTypeRef cf
) {
344 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("<Private CFType 0x%x>\n"), (UInt32
)cf
);
347 static void __CFPreferencesDomainDeallocate(CFTypeRef cf
) {
348 const struct __CFPreferencesDomain
*domain
= cf
;
349 CFAllocatorRef alloc
= __CFPreferencesAllocator();
350 domain
->_callBacks
->freeDomain(alloc
, domain
->_context
, domain
->_domain
);
351 if (domain
->_context
) CFRelease(domain
->_context
);
354 static CFTypeID __kCFPreferencesDomainTypeID
= _kCFRuntimeNotATypeID
;
356 static const CFRuntimeClass __CFPreferencesDomainClass
= {
358 "CFPreferencesDomain",
361 __CFPreferencesDomainDeallocate
,
365 __CFPreferencesDomainCopyDescription
368 /* This is called once at CFInitialize() time. */
369 __private_extern__
void __CFPreferencesDomainInitialize(void) {
370 __kCFPreferencesDomainTypeID
= _CFRuntimeRegisterClass(&__CFPreferencesDomainClass
);
373 /* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
374 __private_extern__ CFStringRef
_CFPreferencesCachePrefixForUserHost(CFStringRef userName
, CFStringRef hostName
) {
375 Boolean freeHost
= false;
377 if (userName
== kCFPreferencesCurrentUser
) {
378 userName
= CFGetUserName();
379 } else if (userName
== kCFPreferencesAnyUser
) {
380 userName
= CFSTR("*");
383 if (hostName
== kCFPreferencesCurrentHost
) {
384 hostName
= __CFCopyEthernetAddrString();
385 if (!hostName
) hostName
= _CFStringCreateHostName();
387 } else if (hostName
== kCFPreferencesAnyHost
) {
388 hostName
= CFSTR("*");
390 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@/%@/"), userName
, hostName
);
391 if (freeHost
&& hostName
!= NULL
) CFRelease(hostName
);
395 // 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
396 static CFStringRef
_CFPreferencesStandardDomainCacheKey(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
397 CFStringRef prefix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
398 CFStringRef result
= NULL
;
401 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@%@"), prefix
, domainName
);
407 #if defined(__MACOS8__)
408 // Define a custom hash function so that we don't inadvertantly make the
409 // result of CFHash() on a string persistent, and locked-in for all time.
410 static UInt16
hashString(CFStringRef str
) {
413 cnt
= CFStringGetLength(str
);
415 for (idx
= 0; idx
< cnt
; idx
++) {
417 h
+= CFStringGetCharacterAtIndex(str
, idx
);
419 return (h
>> 16) ^ (h
& 0xFFFF);
423 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
424 CFURLRef theURL
= NULL
;
425 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
426 #if defined(__MACH__)
427 CFURLRef prefDir
= _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, safeLevel
);
429 CFStringRef fileName
;
430 Boolean mustFreeAppName
= false;
432 if (!prefDir
) return NULL
;
433 if (domainName
== kCFPreferencesAnyApplication
) {
434 appName
= CFSTR(".GlobalPreferences");
435 } else if (domainName
== kCFPreferencesCurrentApplication
) {
436 CFBundleRef mainBundle
= CFBundleGetMainBundle();
437 appName
= mainBundle
? CFBundleGetIdentifier(mainBundle
) : NULL
;
438 if (!appName
|| CFStringGetLength(appName
) == 0) {
439 appName
= _CFProcessNameString();
442 appName
= domainName
;
444 if (userName
!= kCFPreferencesAnyUser
) {
445 if (hostName
== kCFPreferencesAnyHost
) {
446 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
447 } else if (hostName
== kCFPreferencesCurrentHost
) {
448 CFStringRef host
= __CFCopyEthernetAddrString();
449 if (!host
) host
= _CFStringCreateHostName();
450 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, host
);
453 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostName
);
456 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
458 if (mustFreeAppName
) {
462 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLPOSIXPathStyle
, false, prefDir
);
463 if (prefDir
) CFRelease(prefDir
);
467 #error Do not know where to store NSUserDefaults on this platform
472 static CFURLRef
_CFPreferencesURLForStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
473 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName
, userName
, hostName
, __CFSafeLaunchLevel
);
476 CFPreferencesDomainRef
_CFPreferencesStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
477 CFPreferencesDomainRef domain
;
478 CFStringRef domainKey
;
479 Boolean shouldReleaseDomain
= true;
480 domainKey
= _CFPreferencesStandardDomainCacheKey(domainName
, userName
, hostName
);
481 __CFSpinLock(&domainCacheLock
);
483 CFAllocatorRef alloc
= __CFPreferencesAllocator();
484 domainCache
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
486 domain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
487 __CFSpinUnlock(&domainCacheLock
);
489 // Domain's not in the cache; load from permanent storage
490 CFURLRef theURL
= _CFPreferencesURLForStandardDomain(domainName
, userName
, hostName
);
492 domain
= _CFPreferencesDomainCreate(theURL
, &__kCFXMLPropertyListDomainCallBacks
);
493 if (userName
== kCFPreferencesAnyUser
) {
494 _CFPreferencesDomainSetIsWorldReadable(domain
, true);
498 __CFSpinLock(&domainCacheLock
);
499 if (domain
&& domainCache
) {
500 // 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.
501 CFPreferencesDomainRef checkDomain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
503 // 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.
504 // checkDomain was retrieved with a Get, so we don't want to over-release.
505 shouldReleaseDomain
= false;
506 CFRelease(domain
); // release the domain we synthesized earlier.
507 domain
= checkDomain
; // repoint it at the domain picked up out of the cache.
509 // We must not have found the domain in the cache, so it's ok for us to put this in.
510 CFDictionarySetValue(domainCache
, domainKey
, domain
);
512 if(shouldReleaseDomain
) CFRelease(domain
);
514 __CFSpinUnlock(&domainCacheLock
);
516 CFRelease(domainKey
);
520 static void __CFPreferencesPerformSynchronize(const void *key
, const void *value
, void *context
) {
521 CFPreferencesDomainRef domain
= (CFPreferencesDomainRef
)value
;
522 Boolean
*cumulativeResult
= (Boolean
*)context
;
523 if (!_CFPreferencesDomainSynchronize(domain
)) *cumulativeResult
= false;
526 __private_extern__ Boolean
_CFSynchronizeDomainCache(void) {
527 Boolean result
= true;
528 __CFSpinLock(&domainCacheLock
);
530 CFDictionaryApplyFunction(domainCache
, __CFPreferencesPerformSynchronize
, &result
);
532 __CFSpinUnlock(&domainCacheLock
);
536 __private_extern__
void _CFPreferencesPurgeDomainCache(void) {
537 _CFSynchronizeDomainCache();
538 __CFSpinLock(&domainCacheLock
);
540 CFRelease(domainCache
);
543 __CFSpinUnlock(&domainCacheLock
);
546 __private_extern__ CFArrayRef
_CFPreferencesCreateDomainList(CFStringRef userName
, CFStringRef hostName
) {
547 #if 0 && defined(__WIN32__)
548 DWORD idx
, numSubkeys
, maxSubKey
, cnt
;
549 CFMutableArrayRef retVal
;
551 id
*list
, buffer
[512];
552 result
= RegQueryInfoKeyA(_masterKey
, NULL
, NULL
, NULL
, &numSubkeys
, &maxSubKey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
553 if (result
!= ERROR_SUCCESS
) {
554 NSLog(@
"%@: cannot query master key info; %d", _NSMethodExceptionProem(self
, _cmd
), result
);
555 return [NSArray array
];
558 list
= (numSubkeys
<= 512) ? buffer
: NSZoneMalloc(NULL
, numSubkeys
* sizeof(void *));
559 if (_useCStringDomains
< 0)
560 _useCStringDomains
= (NSWindows95OperatingSystem
== [[NSProcessInfo processInfo
] operatingSystem
]);
561 if (_useCStringDomains
) {
562 for (idx
= 0, cnt
= 0; idx
< numSubkeys
; idx
++) {
563 char name
[maxSubKey
+ 1];
564 DWORD nameSize
= maxSubKey
;
565 if (RegEnumKeyExA(_masterKey
, idx
, name
, &nameSize
, NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
)
566 list
[cnt
++] = [NSString stringWithCString
:name length
:nameSize
];
569 for (idx
= 0, cnt
= 0; idx
< numSubkeys
; idx
++) {
570 unichar name
[maxSubKey
+ 1];
571 DWORD nameSize
= maxSubKey
;
572 if (RegEnumKeyExW(_masterKey
, idx
, name
, &nameSize
, NULL
, NULL
, NULL
, NULL
) == ERROR_SUCCESS
)
573 list
[cnt
++] = [NSString stringWithCharacters
:name length
:nameSize
];
576 retVal
= [NSArray arrayWithObjects
:list count
:cnt
];
577 if (list
!= buffer
) NSZoneFree(NULL
, list
);
579 #elif defined(__MACH__) || defined(__svr4__) || defined(__hpux__)
580 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
582 CFMutableArrayRef marray
;
583 CFStringRef
*cachedDomainKeys
;
584 CFPreferencesDomainRef
*cachedDomains
;
588 CFURLRef prefDir
= _preferencesDirectoryForUserHost(userName
, hostName
);
593 if (hostName
== kCFPreferencesAnyHost
) {
594 suffix
= CFStringCreateWithCString(prefAlloc
, ".plist", kCFStringEncodingASCII
);
595 } else if (hostName
== kCFPreferencesCurrentHost
) {
596 CFStringRef host
= __CFCopyEthernetAddrString();
597 if (!host
) host
= _CFStringCreateHostName();
598 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), host
);
601 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostName
);
603 suffixLen
= CFStringGetLength(suffix
);
605 domains
= CFURLCreatePropertyFromResource(prefAlloc
, prefDir
, kCFURLFileDirectoryContents
, NULL
);
608 marray
= CFArrayCreateMutableCopy(prefAlloc
, 0, domains
);
611 marray
= CFArrayCreateMutable(prefAlloc
, 0, & kCFTypeArrayCallBacks
);
613 for (idx
= CFArrayGetCount(marray
)-1; idx
>= 0; idx
--) {
614 CFURLRef url
= CFArrayGetValueAtIndex(marray
, idx
);
615 CFStringRef string
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
616 if (!CFStringHasSuffix(string
, suffix
)) {
617 CFArrayRemoveValueAtIndex(marray
, idx
);
619 CFStringRef dom
= CFStringCreateWithSubstring(prefAlloc
, string
, CFRangeMake(0, CFStringGetLength(string
) - suffixLen
));
620 if (CFEqual(dom
, CFSTR(".GlobalPreferences"))) {
621 CFArraySetValueAtIndex(marray
, idx
, kCFPreferencesAnyApplication
);
623 CFArraySetValueAtIndex(marray
, idx
, dom
);
631 // Now add any domains added in the cache; delete any that have been deleted in the cache
632 __CFSpinLock(&domainCacheLock
);
634 __CFSpinUnlock(&domainCacheLock
);
637 cnt
= CFDictionaryGetCount(domainCache
);
638 cachedDomainKeys
= CFAllocatorAllocate(prefAlloc
, 2 * cnt
* sizeof(CFStringRef
), 0);
639 cachedDomains
= (CFPreferencesDomainRef
*)(cachedDomainKeys
+ cnt
);
640 CFDictionaryGetKeysAndValues(domainCache
, (const void **)cachedDomainKeys
, (const void **)cachedDomains
);
641 __CFSpinUnlock(&domainCacheLock
);
642 suffix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
643 suffixLen
= CFStringGetLength(suffix
);
645 for (idx
= 0; idx
< cnt
; idx
++) {
646 CFStringRef domainKey
= cachedDomainKeys
[idx
];
647 CFPreferencesDomainRef domain
= cachedDomains
[idx
];
648 CFStringRef domainName
;
649 CFIndex keyCount
= 0;
651 if (!CFStringHasPrefix(domainKey
, suffix
)) continue;
652 domainName
= CFStringCreateWithSubstring(prefAlloc
, domainKey
, CFRangeMake(suffixLen
, CFStringGetLength(domainKey
) - suffixLen
));
653 if (CFEqual(domainName
, CFSTR("*"))) {
654 CFRelease(domainName
);
655 domainName
= CFRetain(kCFPreferencesAnyApplication
);
656 } else if (CFEqual(domainName
, kCFPreferencesCurrentApplication
)) {
657 CFRelease(domainName
);
658 domainName
= CFRetain(_CFProcessNameString());
660 _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull
, domain
, NULL
, &keyCount
);
662 // Domain was deleted
663 SInt32 firstIndexOfValue
= CFArrayGetFirstIndexOfValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
);
664 if (0 <= firstIndexOfValue
) {
665 CFArrayRemoveValueAtIndex(marray
, firstIndexOfValue
);
667 } else if (!CFArrayContainsValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
)) {
668 CFArrayAppendValue(marray
, domainName
);
670 CFRelease(domainName
);
673 CFAllocatorDeallocate(prefAlloc
, cachedDomainKeys
);
680 // CFPreferencesDomain functions
683 CFPreferencesDomainRef
_CFPreferencesDomainCreate(CFTypeRef context
, const _CFPreferencesDomainCallBacks
*callBacks
) {
684 CFAllocatorRef alloc
= __CFPreferencesAllocator();
685 CFPreferencesDomainRef newDomain
;
686 CFAssert(callBacks
!= NULL
&& callBacks
->createDomain
!= NULL
&& callBacks
->freeDomain
!= NULL
&& callBacks
->fetchValue
!= NULL
&& callBacks
->writeValue
!= NULL
, __kCFLogAssertion
, "Cannot create a domain with NULL callbacks");
687 newDomain
= (CFPreferencesDomainRef
)_CFRuntimeCreateInstance(alloc
, __kCFPreferencesDomainTypeID
, sizeof(struct __CFPreferencesDomain
) - sizeof(CFRuntimeBase
), NULL
);
689 newDomain
->_callBacks
= callBacks
;
690 if (context
) CFRetain(context
);
691 newDomain
->_context
= context
;
692 newDomain
->_domain
= callBacks
->createDomain(alloc
, context
);
697 CFTypeRef
_CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain
, CFStringRef key
) {
698 return domain
->_callBacks
->fetchValue(domain
->_context
, domain
->_domain
, key
);
701 void _CFPreferencesDomainSet(CFPreferencesDomainRef domain
, CFStringRef key
, CFTypeRef value
) {
702 domain
->_callBacks
->writeValue(domain
->_context
, domain
->_domain
, key
, value
);
705 __private_extern__ Boolean
_CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain
) {
706 return domain
->_callBacks
->synchronize(domain
->_context
, domain
->_domain
);
709 __private_extern__
void _CFPreferencesDomainGetKeysAndValues(CFAllocatorRef alloc
, CFPreferencesDomainRef domain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
710 domain
->_callBacks
->getKeysAndValues(alloc
, domain
->_context
, domain
->_domain
, buf
, numKeyValuePairs
);
713 __private_extern__
void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain
, Boolean isWorldReadable
) {
714 if (domain
->_callBacks
->setIsWorldReadable
) {
715 domain
->_callBacks
->setIsWorldReadable(domain
->_context
, domain
->_domain
, isWorldReadable
);
719 void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain
, CFDictionaryRef dict
) {
720 CFTypeRef buf
[32], *keys
= buf
;
721 CFIndex idx
, count
= 16;
722 CFAllocatorRef alloc
= __CFPreferencesAllocator();
724 _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull
, domain
, (void ***)(&keys
), &count
);
729 _CFPreferencesDomainGetKeysAndValues(alloc
, domain
, (void ***)(&keys
), &count
);
731 for (idx
= 0; idx
< count
; idx
++) {
732 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], NULL
);
735 CFAllocatorDeallocate(alloc
, keys
);
738 if (dict
&& (count
= CFDictionaryGetCount(dict
)) != 0) {
739 CFStringRef
*newKeys
= (count
< 32) ? buf
: CFAllocatorAllocate(alloc
, count
* sizeof(CFStringRef
), 0);
740 CFDictionaryGetKeysAndValues(dict
, (const void **)newKeys
, NULL
);
741 for (idx
= 0; idx
< count
; idx
++) {
742 CFStringRef key
= newKeys
[idx
];
743 _CFPreferencesDomainSet(domain
, key
, (CFTypeRef
)CFDictionaryGetValue(dict
, key
));
745 if (((CFTypeRef
)newKeys
) != buf
) {
746 CFAllocatorDeallocate(alloc
, newKeys
);
751 CFDictionaryRef
_CFPreferencesDomainCopyDictionary(CFPreferencesDomainRef domain
) {
752 CFTypeRef
*keys
= NULL
;
754 CFAllocatorRef alloc
= __CFPreferencesAllocator();
755 CFDictionaryRef dict
= NULL
;
756 _CFPreferencesDomainGetKeysAndValues(alloc
, domain
, (void ***)&keys
, &count
);
758 CFTypeRef
*values
= keys
+ count
;
759 dict
= CFDictionaryCreate(alloc
, keys
, values
, count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
760 CFAllocatorDeallocate(alloc
, keys
);
765 CFDictionaryRef
_CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain
) {
766 CFDictionaryRef result
= domain
->_callBacks
->copyDomainDictionary(domain
->_context
, domain
->_domain
);
767 if(result
&& CFDictionaryGetCount(result
) == 0) {
774 Boolean
_CFPreferencesDomainExists(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
775 CFPreferencesDomainRef domain
;
777 domain
= _CFPreferencesStandardDomain(domainName
, userName
, hostName
);
779 _CFPreferencesDomainGetKeysAndValues(kCFAllocatorNull
, domain
, NULL
, &count
);
786 /* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */
787 static void *createVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
) {
788 return CFDictionaryCreateMutable(allocator
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
791 static void freeVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
, void *domain
) {
792 CFRelease((CFTypeRef
)domain
);
795 static CFTypeRef
fetchVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
) {
796 CFTypeRef result
= CFDictionaryGetValue((CFMutableDictionaryRef
)domain
, key
);
797 if (result
) CFRetain(result
);
801 static void writeVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
, CFTypeRef value
) {
803 CFDictionarySetValue((CFMutableDictionaryRef
)domain
, key
, value
);
805 CFDictionaryRemoveValue((CFMutableDictionaryRef
)domain
, key
);
808 static Boolean
synchronizeVolatileDomain(CFTypeRef context
, void *domain
) {
812 static void getVolatileKeysAndValues(CFAllocatorRef alloc
, CFTypeRef context
, void *domain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
813 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)domain
;
814 CFIndex count
= CFDictionaryGetCount(dict
);
818 if ( count
< *numKeyValuePairs
) {
819 values
= *buf
+ count
;
820 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
821 } else if (alloc
!= kCFAllocatorNull
) {
823 *buf
= CFAllocatorReallocate(alloc
, *buf
, count
* 2 * sizeof(void *), 0);
825 *buf
= CFAllocatorAllocate(alloc
, count
*2*sizeof(void *), 0);
828 values
= *buf
+ count
;
829 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
833 *numKeyValuePairs
= count
;
836 static CFDictionaryRef
copyVolatileDomainDictionary(CFTypeRef context
, void *volatileDomain
) {
837 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)volatileDomain
;
839 CFDictionaryRef result
= (CFDictionaryRef
)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict
, kCFPropertyListImmutable
);
843 const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks
= {createVolatileDomain
, freeVolatileDomain
, fetchVolatileValue
, writeVolatileValue
, synchronizeVolatileDomain
, getVolatileKeysAndValues
, copyVolatileDomainDictionary
, NULL
};