2 * Copyright (c) 2011 Apple 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@
25 Copyright (c) 1999-2011, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
29 #include "CFInternal.h"
30 #include <CoreFoundation/CFPriv.h>
31 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
38 #include <crt_externs.h>
39 #include <mach-o/dyld.h>
42 #if DEPLOYMENT_TARGET_WINDOWS
47 #define getcwd _NS_getcwd
51 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
52 #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
54 #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
57 static CFStringRef
_CFUserName(void);
59 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
60 // CoreGraphics and LaunchServices are only projects (1 Dec 2006) that use these
61 char **_CFArgv(void) { return *_NSGetArgv(); }
62 int _CFArgc(void) { return *_NSGetArgc(); }
66 __private_extern__ Boolean
_CFGetCurrentDirectory(char *path
, int maxlen
) {
67 return getcwd(path
, maxlen
) != NULL
;
71 static Boolean __CFIsCFM
= false;
73 // If called super early, we just return false
74 __private_extern__ Boolean
_CFIsCFM(void) {
79 #if DEPLOYMENT_TARGET_WINDOWS
86 #if DEPLOYMENT_TARGET_WINDOWS
87 // Returns the path to the CF DLL, which we can then use to find resources like char sets
88 bool bDllPathCached
= false;
89 __private_extern__
const wchar_t *_CFDLLPath(void) {
90 static wchar_t cachedPath
[MAX_PATH
+1];
92 if (!bDllPathCached
) {
94 // might be nice to get this from the project file at some point
95 wchar_t *DLLFileName
= L
"CoreFoundation_debug.dll";
97 wchar_t *DLLFileName
= L
"CoreFoundation.dll";
99 HMODULE ourModule
= GetModuleHandleW(DLLFileName
);
101 CFAssert(ourModule
, __kCFLogAssertion
, "GetModuleHandle failed");
103 DWORD wResult
= GetModuleFileNameW(ourModule
, cachedPath
, MAX_PATH
+1);
104 CFAssert1(wResult
> 0, __kCFLogAssertion
, "GetModuleFileName failed: %d", GetLastError());
105 CFAssert1(wResult
< MAX_PATH
+1, __kCFLogAssertion
, "GetModuleFileName result truncated: %s", cachedPath
);
107 // strip off last component, the DLL name
109 for (idx
= wResult
- 1; idx
; idx
--) {
110 if ('\\' == cachedPath
[idx
]) {
111 cachedPath
[idx
] = '\0';
115 bDllPathCached
= true;
121 static const char *__CFProcessPath
= NULL
;
122 static const char *__CFprogname
= NULL
;
124 const char **_CFGetProgname(void) {
126 _CFProcessPath(); // sets up __CFprogname as a side-effect
127 return &__CFprogname
;
130 const char **_CFGetProcessPath(void) {
131 if (!__CFProcessPath
)
132 _CFProcessPath(); // sets up __CFProcessPath as a side-effect
133 return &__CFProcessPath
;
136 #if DEPLOYMENT_TARGET_WINDOWS
137 const char *_CFProcessPath(void) {
138 if (__CFProcessPath
) return __CFProcessPath
;
139 wchar_t buf
[CFMaxPathSize
] = {0};
140 DWORD rlen
= GetModuleFileNameW(NULL
, buf
, sizeof(buf
) / sizeof(buf
[0]));
142 char asciiBuf
[CFMaxPathSize
] = {0};
143 int res
= WideCharToMultiByte(CP_UTF8
, 0, buf
, rlen
, asciiBuf
, sizeof(asciiBuf
) / sizeof(asciiBuf
[0]), NULL
, NULL
);
145 __CFProcessPath
= strdup(asciiBuf
);
146 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
147 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
150 if (!__CFProcessPath
) {
151 __CFProcessPath
= "";
152 __CFprogname
= __CFProcessPath
;
154 return __CFProcessPath
;
158 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
159 const char *_CFProcessPath(void) {
160 if (__CFProcessPath
) return __CFProcessPath
;
161 #if DEPLOYMENT_TARGET_MACOSX
163 const char *path
= (char *)__CFgetenv("CFProcessPath");
165 __CFProcessPath
= strdup(path
);
166 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
167 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
168 return __CFProcessPath
;
172 uint32_t size
= CFMaxPathSize
;
174 if (0 == _NSGetExecutablePath(buffer
, &size
)) {
176 size_t len
= strlen(buffer
);
177 if (12 <= len
&& 0 == strcmp("LaunchCFMApp", buffer
+ len
- 12)) {
178 struct stat exec
, lcfm
;
179 const char *launchcfm
= "/System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp";
180 if (0 == stat(launchcfm
, &lcfm
) && 0 == stat(buffer
, &exec
) && (lcfm
.st_dev
== exec
.st_dev
) && (lcfm
.st_ino
== exec
.st_ino
)) {
181 // Executable is LaunchCFMApp, take special action
183 if ((*_NSGetArgv())[1] && '/' == *((*_NSGetArgv())[1])) {
184 strlcpy(buffer
, (*_NSGetArgv())[1], sizeof(buffer
));
189 __CFProcessPath
= strdup(buffer
);
190 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
191 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
193 if (!__CFProcessPath
) {
194 __CFProcessPath
= "";
195 __CFprogname
= __CFProcessPath
;
197 return __CFProcessPath
;
201 #if DEPLOYMENT_TARGET_LINUX
204 const char *_CFProcessPath(void) {
205 if (__CFProcessPath
) return __CFProcessPath
;
206 char buf
[CFMaxPathSize
+ 1];
208 ssize_t res
= readlink("/proc/self/exe", buf
, CFMaxPathSize
);
210 // null terminate, readlink does not
212 __CFProcessPath
= strdup(buf
);
213 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
214 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
216 __CFProcessPath
= "";
217 __CFprogname
= __CFProcessPath
;
219 return __CFProcessPath
;
223 __private_extern__ CFStringRef
_CFProcessNameString(void) {
224 static CFStringRef __CFProcessNameString
= NULL
;
225 if (!__CFProcessNameString
) {
226 const char *processName
= *_CFGetProgname();
227 if (!processName
) processName
= "";
228 CFStringRef newStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, processName
, kCFPlatformInterfaceStringEncoding
);
229 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *) newStr
, (void * volatile *)& __CFProcessNameString
)) {
230 CFRelease(newStr
); // someone else made the assignment, so just release the extra string.
233 return __CFProcessNameString
;
236 static CFStringRef __CFUserName
= NULL
;
237 static CFSpinLock_t __CFPlatformCacheLock
= CFSpinLockInit
;
239 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
243 static CFURLRef __CFHomeDirectory
= NULL
;
244 static uint32_t __CFEUID
= -1;
245 static uint32_t __CFUID
= -1;
247 static CFURLRef
_CFCopyHomeDirURLForUser(struct passwd
*upwd
) { // __CFPlatformCacheLock must be locked on entry and will be on exit
248 CFURLRef home
= NULL
;
250 const char *path
= __CFgetenv("CFFIXED_USER_HOME");
252 home
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)path
, strlen(path
), true);
256 if (upwd
&& upwd
->pw_dir
) {
257 home
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)upwd
->pw_dir
, strlen(upwd
->pw_dir
), true);
263 static void _CFUpdateUserInfo(void) { // __CFPlatformCacheLock must be locked on entry and will be on exit
266 __CFEUID
= geteuid();
268 if (__CFHomeDirectory
) CFRelease(__CFHomeDirectory
);
269 __CFHomeDirectory
= NULL
;
270 if (__CFUserName
) CFRelease(__CFUserName
);
273 upwd
= getpwuid(__CFEUID
? __CFEUID
: __CFUID
);
274 __CFHomeDirectory
= _CFCopyHomeDirURLForUser(upwd
);
275 if (!__CFHomeDirectory
) {
276 const char *cpath
= __CFgetenv("HOME");
278 __CFHomeDirectory
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)cpath
, strlen(cpath
), true);
282 // This implies that UserManager stores directory info in CString
283 // rather than FileSystemRep. Perhaps this is wrong & we should
284 // expect NeXTSTEP encodings. A great test of our localized system would
285 // be to have a user "O-umlat z e r". XXX
286 if (upwd
&& upwd
->pw_name
) {
287 __CFUserName
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, upwd
->pw_name
, kCFPlatformInterfaceStringEncoding
);
289 const char *cuser
= __CFgetenv("USER");
291 __CFUserName
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, cuser
, kCFPlatformInterfaceStringEncoding
);
296 static CFURLRef
_CFCreateHomeDirectoryURLForUser(CFStringRef uName
) { // __CFPlatformCacheLock must be locked on entry and will be on exit
297 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
299 if (geteuid() != __CFEUID
|| getuid() != __CFUID
|| !__CFHomeDirectory
)
301 if (__CFHomeDirectory
) CFRetain(__CFHomeDirectory
);
302 return __CFHomeDirectory
;
304 struct passwd
*upwd
= NULL
;
305 char buf
[128], *user
;
306 SInt32 len
= CFStringGetLength(uName
), size
= CFStringGetMaximumSizeForEncoding(len
, kCFPlatformInterfaceStringEncoding
);
311 user
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
+1, 0);
312 if (__CFOASafe
) __CFSetLastAllocationEventName(user
, "CFUtilities (temp)");
314 if (CFStringGetBytes(uName
, CFRangeMake(0, len
), kCFPlatformInterfaceStringEncoding
, 0, true, (uint8_t *)user
, size
, &usedSize
) == len
) {
315 user
[usedSize
] = '\0';
316 upwd
= getpwnam(user
);
319 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, user
);
321 return _CFCopyHomeDirURLForUser(upwd
);
323 #elif DEPLOYMENT_TARGET_WINDOWS
324 // This code can only get the directory for the current user
325 if (uName
&& !CFEqual(uName
, _CFUserName())) {
326 CFLog(kCFLogLevelError
, CFSTR("CFCopyHomeDirectoryURLForUser(): Unable to get home directory for other user"));
330 CFURLRef retVal
= NULL
;
332 CFStringRef str
= NULL
;
334 UniChar pathChars
[MAX_PATH
];
335 if (S_OK
== SHGetFolderPathW(NULL
, CSIDL_PROFILE
, NULL
, SHGFP_TYPE_CURRENT
, (wchar_t *)pathChars
)) {
336 len
= (CFIndex
)wcslen((wchar_t *)pathChars
);
337 str
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, pathChars
, len
);
338 retVal
= CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault
, str
, kCFURLWindowsPathStyle
, true);
343 // Fall back to environment variable, but this will not be unicode compatible
344 const char *cpath
= __CFgetenv("HOMEPATH");
345 const char *cdrive
= __CFgetenv("HOMEDRIVE");
346 if (cdrive
&& cpath
) {
347 char fullPath
[CFMaxPathSize
];
348 strlcpy(fullPath
, cdrive
, sizeof(fullPath
));
349 strlcat(fullPath
, cpath
, sizeof(fullPath
));
350 str
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, fullPath
, kCFPlatformInterfaceStringEncoding
);
351 retVal
= CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault
, str
, kCFURLWindowsPathStyle
, true);
357 // Last resort: We have to get "some" directory location, so fall-back to the processes current directory.
358 UniChar currDir
[MAX_PATH
];
359 DWORD dwChars
= GetCurrentDirectoryW(MAX_PATH
+ 1, (wchar_t *)currDir
);
361 len
= (CFIndex
)wcslen((wchar_t *)currDir
);
362 str
= CFStringCreateWithCharacters(kCFAllocatorDefault
, currDir
, len
);
363 retVal
= CFURLCreateWithFileSystemPath(NULL
, str
, kCFURLWindowsPathStyle
, true);
368 // We could do more here (as in KB Article Q101507). If that article is to be believed, we should only run into this case on Win95, or through user error.
369 CFStringRef testPath
= CFURLCopyFileSystemPath(retVal
, kCFURLWindowsPathStyle
);
370 if (CFStringGetLength(testPath
) == 0) {
374 if (testPath
) CFRelease(testPath
);
378 #error Dont know how to compute users home directories on this platform
382 static CFStringRef
_CFUserName(void) { // __CFPlatformCacheLock must be locked on entry and will be on exit
383 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
384 if (geteuid() != __CFEUID
|| getuid() != __CFUID
)
386 #elif DEPLOYMENT_TARGET_WINDOWS
388 wchar_t username
[1040];
391 if (GetUserNameW(username
, &size
)) {
392 // discount the extra NULL by decrementing the size
393 __CFUserName
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)username
, size
- 1);
395 const char *cname
= __CFgetenv("USERNAME");
397 __CFUserName
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, cname
, kCFPlatformInterfaceStringEncoding
);
401 #error Dont know how to compute user name on this platform
404 __CFUserName
= (CFStringRef
)CFRetain(CFSTR(""));
408 #define CFMaxHostNameLength 256
409 #define CFMaxHostNameSize (CFMaxHostNameLength+1)
411 __private_extern__ CFStringRef
_CFStringCreateHostName(void) {
412 char myName
[CFMaxHostNameSize
];
414 // return @"" instead of nil a la CFUserName() and Ali Ozer
415 if (0 != gethostname(myName
, CFMaxHostNameSize
)) myName
[0] = '\0';
416 return CFStringCreateWithCString(kCFAllocatorSystemDefault
, myName
, kCFPlatformInterfaceStringEncoding
);
419 /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
420 These can return NULL.
422 CF_EXPORT CFStringRef
CFGetUserName(void) {
423 CFStringRef result
= NULL
;
424 __CFSpinLock(&__CFPlatformCacheLock
);
425 result
= CFStringCreateCopy(kCFAllocatorSystemDefault
, _CFUserName());
426 __CFSpinUnlock(&__CFPlatformCacheLock
);
430 CF_EXPORT CFStringRef
CFCopyUserName(void) {
431 CFStringRef result
= NULL
;
432 __CFSpinLock(&__CFPlatformCacheLock
);
433 result
= CFStringCreateCopy(kCFAllocatorSystemDefault
, _CFUserName());
434 __CFSpinUnlock(&__CFPlatformCacheLock
);
438 CF_EXPORT CFURLRef
CFCopyHomeDirectoryURLForUser(CFStringRef uName
) {
439 CFURLRef result
= NULL
;
440 __CFSpinLock(&__CFPlatformCacheLock
);
441 result
= _CFCreateHomeDirectoryURLForUser(uName
);
442 __CFSpinUnlock(&__CFPlatformCacheLock
);
446 #undef CFMaxHostNameLength
447 #undef CFMaxHostNameSize
449 #if DEPLOYMENT_TARGET_WINDOWS
450 CF_INLINE CFIndex
strlen_UniChar(const UniChar
* p
) {
457 //#include <shfolder.h>
459 * _CFCreateApplicationRepositoryPath returns the path to the application's
460 * repository in a CFMutableStringRef. The path returned will be:
461 * <nFolder_path>\Apple Computer\<bundle_name>\
462 * or if the bundle name cannot be obtained:
463 * <nFolder_path>\Apple Computer\
464 * where nFolder_path is obtained by calling SHGetFolderPath with nFolder
465 * (for example, with CSIDL_APPDATA or CSIDL_LOCAL_APPDATA).
467 * The CFMutableStringRef result must be released by the caller.
469 * If anything fails along the way, the result will be NULL.
471 CF_EXPORT CFMutableStringRef
_CFCreateApplicationRepositoryPath(CFAllocatorRef alloc
, int nFolder
) {
472 CFMutableStringRef result
= NULL
;
473 UniChar szPath
[MAX_PATH
];
475 // get the current path to the data repository: CSIDL_APPDATA (roaming) or CSIDL_LOCAL_APPDATA (nonroaming)
476 if (S_OK
== SHGetFolderPathW(NULL
, nFolder
, NULL
, 0, (wchar_t *) szPath
)) {
477 CFStringRef directoryPath
;
479 // make it a CFString
480 directoryPath
= CFStringCreateWithCharacters(alloc
, szPath
, strlen_UniChar(szPath
));
483 CFStringRef bundleName
;
484 CFStringRef completePath
;
486 // attempt to get the bundle name
487 bundle
= CFBundleGetMainBundle();
489 bundleName
= (CFStringRef
)CFBundleGetValueForInfoDictionaryKey(bundle
, kCFBundleNameKey
);
496 // the path will be "<directoryPath>\Apple Computer\<bundleName>\" if there is a bundle name
497 completePath
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@\\Apple Computer\\%@\\"), directoryPath
, bundleName
);
500 // or "<directoryPath>\Apple Computer\" if there is no bundle name.
501 completePath
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@\\Apple Computer\\"), directoryPath
);
504 CFRelease(directoryPath
);
506 // make a mutable copy to return
508 result
= CFStringCreateMutableCopy(alloc
, 0, completePath
);
509 CFRelease(completePath
);
519 #pragma mark Thread Functions
521 #if DEPLOYMENT_TARGET_WINDOWS
523 // This code from here:
524 // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
526 const DWORD MS_VC_EXCEPTION
=0x406D1388;
528 typedef struct tagTHREADNAME_INFO
530 DWORD dwType
; // Must be 0x1000.
531 LPCSTR szName
; // Pointer to name (in user addr space).
532 DWORD dwThreadID
; // Thread ID (-1=caller thread).
533 DWORD dwFlags
; // Reserved for future use, must be zero.
537 CF_EXPORT
void _NS_pthread_setname_np(const char *name
) {
538 THREADNAME_INFO info
;
539 info
.dwType
= 0x1000;
541 info
.dwThreadID
= GetCurrentThreadId();
546 RaiseException( MS_VC_EXCEPTION
, 0, sizeof(info
)/sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
548 __except(EXCEPTION_EXECUTE_HANDLER
)
553 static pthread_t __initialPthread
= { NULL
, 0 };
555 CF_EXPORT
int _NS_pthread_main_np() {
556 pthread_t me
= pthread_self();
557 if (NULL
== __initialPthread
.p
) {
558 __initialPthread
.p
= me
.p
;
559 __initialPthread
.x
= me
.x
;
561 return (pthread_equal(__initialPthread
, me
));
567 #pragma mark Thread Local Data
569 // If slot >= CF_TSD_MAX_SLOTS, the SPI functions will crash at NULL + slot address.
570 // If thread data has been torn down, these functions should crash on CF_TSD_BAD_PTR + slot address.
571 #define CF_TSD_MAX_SLOTS 70
573 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
574 #define CF_TSD_KEY 55
577 // Windows and Linux, not sure how many times the destructor could get called; CF_TSD_MAX_DESTRUCTOR_CALLS could be 1
579 #define CF_TSD_BAD_PTR ((void *)0x1000)
581 typedef void (*tsdDestructor
)(void *);
583 // Data structure to hold TSD data, cleanup functions for each
584 typedef struct __CFTSDTable
{
585 uint32_t destructorCount
;
586 uintptr_t data
[CF_TSD_MAX_SLOTS
];
587 tsdDestructor destructors
[CF_TSD_MAX_SLOTS
];
590 static void __CFTSDFinalize(void *arg
);
592 #if DEPLOYMENT_TARGET_WINDOWS
594 static DWORD __CFTSDIndexKey
= 0xFFFFFFFF;
596 // Called from CFRuntime's startup code, on Windows only
597 __private_extern__
void __CFTSDWindowsInitialize() {
598 __CFTSDIndexKey
= TlsAlloc();
601 // Called from CFRuntime's cleanup code, on Windows only
602 __private_extern__
void __CFTSDWindowsCleanup() {
603 TlsFree(__CFTSDIndexKey
);
606 // Called for each thread as it exits, on Windows only
607 __private_extern__
void __CFFinalizeWindowsThreadData() {
608 // Normally, this should call the finalizer several times to emulate the behavior of pthreads on Windows. However, a few bugs keep us from doing this:
609 // <rdar://problem/8989063> REGRESSION(CF-610-CF-611): Crash closing Safari in BonjourDB destructor (Windows)
610 // <rdar://problem/9326814> SyncUIHandler crashes after conflict is resolved and we do SyncNow
611 // and a bug in dispatch keeps us from using pthreadsWin32 directly, because it does not deal with the case of a dispatch_async happening during process exit (it attempts to create a thread, but that is illegal on Win32 and causes a hang).
612 // So instead we just finalize once, which is the behavior pre-Airwolf anyway
613 __CFTSDFinalize(TlsGetValue(__CFTSDIndexKey
));
618 #if DEPLOYMENT_TARGET_LINUX
620 static pthread_key_t __CFTSDIndexKey
;
622 // Called from CFRuntime's startup code, on Linux only
623 __private_extern__
void __CFTSDLinuxInitialize() {
624 (void)pthread_key_create(&__CFTSDIndexKey
, __CFTSDFinalize
);
629 static void __CFTSDSetSpecific(void *arg
) {
630 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
631 pthread_setspecific(CF_TSD_KEY
, arg
);
632 #elif DEPLOYMENT_TARGET_LINUX
633 pthread_setspecific(__CFTSDIndexKey
, arg
);
634 #elif DEPLOYMENT_TARGET_WINDOWS
635 TlsSetValue(__CFTSDIndexKey
, arg
);
639 static void *__CFTSDGetSpecific() {
640 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
641 return pthread_getspecific(CF_TSD_KEY
);
642 #elif DEPLOYMENT_TARGET_LINUX
643 return pthread_getspecific(__CFTSDIndexKey
);
644 #elif DEPLOYMENT_TARGET_WINDOWS
645 return TlsGetValue(__CFTSDIndexKey
);
649 static void __CFTSDFinalize(void *arg
) {
650 // Set our TSD so we're called again by pthreads. It will call the destructor 5 times as long as a value is set in the thread specific data. We handle each case below.
651 __CFTSDSetSpecific(arg
);
653 if (!arg
|| arg
== CF_TSD_BAD_PTR
) {
654 // We've already been destroyed. The call above set the bad pointer again. Now we just return.
658 __CFTSDTable
*table
= (__CFTSDTable
*)arg
;
659 table
->destructorCount
++;
661 // On 1st, 2nd, 3rd, 4th calls, invoke destructor
662 // Note that invocation of the destructor may cause a value to be set again in the per-thread data slots. The destructor count and destructors are preserved.
663 // This logic is basically the same as what pthreads does. We just skip the 'created' flag.
665 uintptr_t pool
= _CFAutoreleasePoolPush();
667 for (int32_t i
= 0; i
< CF_TSD_MAX_SLOTS
; i
++) {
668 if (table
->data
[i
] && table
->destructors
[i
]) {
669 uintptr_t old
= table
->data
[i
];
670 table
->data
[i
] = (uintptr_t)NULL
;
671 table
->destructors
[i
]((void *)(old
));
675 _CFAutoreleasePoolPop(pool
);
678 if (table
->destructorCount
== PTHREAD_DESTRUCTOR_ITERATIONS
- 1) { // On 4th call, destroy our data
681 // Now if the destructor is called again we will take the shortcut at the beginning of this function.
682 __CFTSDSetSpecific(CF_TSD_BAD_PTR
);
687 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
688 extern int pthread_key_init_np(int, void (*)(void *));
691 // Get or initialize a thread local storage. It is created on demand.
692 static __CFTSDTable
*__CFTSDGetTable() {
693 __CFTSDTable
*table
= (__CFTSDTable
*)__CFTSDGetSpecific();
694 // Make sure we're not setting data again after destruction.
695 if (table
== CF_TSD_BAD_PTR
) {
698 // Create table on demand
700 // This memory is freed in the finalize function
701 table
= (__CFTSDTable
*)calloc(1, sizeof(__CFTSDTable
));
702 // Windows and Linux have created the table already, we need to initialize it here for other platforms. On Windows, the cleanup function is called by DllMain when a thread exits. On Linux the destructor is set at init time.
703 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
704 pthread_key_init_np(CF_TSD_KEY
, __CFTSDFinalize
);
706 __CFTSDSetSpecific(table
);
713 // For the use of CF and Foundation only
714 CF_EXPORT
void *_CFGetTSD(uint32_t slot
) {
715 if (slot
> CF_TSD_MAX_SLOTS
) {
716 _CFLogSimple(kCFLogLevelError
, "Error: TSD slot %d out of range (get)", slot
);
719 __CFTSDTable
*table
= __CFTSDGetTable();
721 // Someone is getting TSD during thread destruction. The table is gone, so we can't get any data anymore.
722 _CFLogSimple(kCFLogLevelWarning
, "Warning: TSD slot %d retrieved but the thread data has already been torn down.", slot
);
725 uintptr_t *slots
= (uintptr_t *)(table
->data
);
726 return (void *)slots
[slot
];
729 // For the use of CF and Foundation only
730 CF_EXPORT
void *_CFSetTSD(uint32_t slot
, void *newVal
, tsdDestructor destructor
) {
731 if (slot
> CF_TSD_MAX_SLOTS
) {
732 _CFLogSimple(kCFLogLevelError
, "Error: TSD slot %d out of range (set)", slot
);
735 __CFTSDTable
*table
= __CFTSDGetTable();
737 // Someone is setting TSD during thread destruction. The table is gone, so we can't get any data anymore.
738 _CFLogSimple(kCFLogLevelWarning
, "Warning: TSD slot %d set but the thread data has already been torn down.", slot
);
742 void *oldVal
= (void *)table
->data
[slot
];
744 table
->data
[slot
] = (uintptr_t)newVal
;
745 table
->destructors
[slot
] = destructor
;
751 #pragma mark Windows Wide to UTF8 and UTF8 to Wide
753 #if DEPLOYMENT_TARGET_WINDOWS
754 /* On Windows, we want to use UTF-16LE for path names to get full unicode support. Internally, however, everything remains in UTF-8 representation. These helper functions stand between CF and the Microsoft CRT to ensure that we are using the right representation on both sides. */
756 #include <sys/stat.h>
759 // Creates a buffer of wchar_t to hold a UTF16LE version of the UTF8 str passed in. Caller must free the buffer when done. If resultLen is non-NULL, it is filled out with the number of characters in the string.
760 static wchar_t *createWideFileSystemRepresentation(const char *str
, CFIndex
*resultLen
) {
761 // Get the real length of the string in UTF16 characters
762 CFStringRef cfStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
);
763 CFIndex strLen
= CFStringGetLength(cfStr
);
765 // Allocate a wide buffer to hold the converted string, including space for a NULL terminator
766 wchar_t *wideBuf
= (wchar_t *)malloc((strLen
+ 1) * sizeof(wchar_t));
768 // Copy the string into the buffer and terminate
769 CFStringGetCharacters(cfStr
, CFRangeMake(0, strLen
), (UniChar
*)wideBuf
);
773 if (resultLen
) *resultLen
= strLen
;
777 // Copies a UTF16 buffer into a supplied UTF8 buffer.
778 static void copyToNarrowFileSystemRepresentation(const wchar_t *wide
, CFIndex dstBufSize
, char *dstbuf
) {
779 // Get the real length of the wide string in UTF8 characters
780 CFStringRef cfStr
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)wide
, wcslen(wide
));
781 CFIndex strLen
= CFStringGetLength(cfStr
);
784 // Copy the wide string into the buffer and terminate
785 CFStringGetBytes(cfStr
, CFRangeMake(0, strLen
), kCFStringEncodingUTF8
, 0, false, (uint8_t *)dstbuf
, dstBufSize
, &bytesUsed
);
786 dstbuf
[bytesUsed
] = 0;
791 CF_EXPORT
int _NS_stat(const char *name
, struct _stat
*st
) {
792 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
793 int res
= _wstat(wide
, st
);
798 CF_EXPORT
int _NS_mkdir(const char *name
) {
799 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
800 int res
= _wmkdir(wide
);
805 CF_EXPORT
int _NS_rmdir(const char *name
) {
806 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
807 int res
= _wrmdir(wide
);
812 CF_EXPORT
int _NS_chmod(const char *name
, int mode
) {
813 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
817 if (mode
| 0400) newMode
|= _S_IREAD
;
818 if (mode
| 0200) newMode
|= _S_IWRITE
;
819 if (mode
| 0100) newMode
|= _S_IEXEC
;
821 int res
= _wchmod(wide
, newMode
);
826 CF_EXPORT
int _NS_unlink(const char *name
) {
827 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
828 int res
= _wunlink(wide
);
833 // Warning: this doesn't support dstbuf as null even though 'getcwd' does
834 CF_EXPORT
char *_NS_getcwd(char *dstbuf
, size_t size
) {
836 CFLog(kCFLogLevelWarning
, CFSTR("CFPlatform: getcwd called with null buffer"));
840 wchar_t *buf
= _wgetcwd(NULL
, 0);
845 // Convert result to UTF8
846 copyToNarrowFileSystemRepresentation(buf
, (CFIndex
)size
, dstbuf
);
851 CF_EXPORT
char *_NS_getenv(const char *name
) {
853 // We have to be careful what happens here, because getenv is called during cf initialization, and things like cfstring may not be working yet
857 CF_EXPORT
int _NS_rename(const char *oldName
, const char *newName
) {
858 wchar_t *oldWide
= createWideFileSystemRepresentation(oldName
, NULL
);
859 wchar_t *newWide
= createWideFileSystemRepresentation(newName
, NULL
);
860 // _wrename on Windows does not behave exactly as rename() on Mac OS -- if the file exists, the Windows one will fail whereas the Mac OS version will replace
861 // To simulate the Mac OS behavior, we use the Win32 API then fill out errno if something goes wrong
862 BOOL winRes
= MoveFileExW(oldWide
, newWide
, MOVEFILE_REPLACE_EXISTING
);
863 DWORD error
= GetLastError();
869 case ERROR_FILE_NOT_FOUND
:
870 case ERROR_PATH_NOT_FOUND
:
871 case ERROR_OPEN_FAILED
:
874 case ERROR_ACCESS_DENIED
:
883 return (winRes
? 0 : -1);
886 CF_EXPORT
int _NS_open(const char *name
, int oflag
, int pmode
) {
887 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
889 _wsopen_s(&fd
, wide
, oflag
, _SH_DENYNO
, _S_IREAD
| _S_IWRITE
);
894 CF_EXPORT
int _NS_chdir(const char *name
) {
895 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
896 int res
= _wchdir(wide
);
901 CF_EXPORT
int _NS_access(const char *name
, int amode
) {
902 // execute is always true
903 if (amode
== 1) return 0;
905 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
906 // we only care about the read-only (04) and write-only (02) bits, so mask octal 06
907 int res
= _waccess(wide
, amode
& 06);
912 // This is a bit different than the standard 'mkstemp', because the size parameter is needed so we know the size of the UTF8 buffer
913 // Also, we don't avoid the race between creating a temporary file name and opening it on Windows like we do on Mac
914 CF_EXPORT
int _NS_mkstemp(char *name
, int bufSize
) {
916 wchar_t *wide
= createWideFileSystemRepresentation(name
, &nameLen
);
918 // First check to see if the directory that this new temporary file will be created in exists. If not, set errno to ENOTDIR. This mimics the behavior of mkstemp on MacOS more closely.
919 // Look for the last '\' in the path
920 wchar_t *lastSlash
= wcsrchr(wide
, '\\');
926 // Set the last slash to NULL temporarily and use it for _wstat
928 struct _stat dirInfo
;
929 int res
= _wstat(wide
, &dirInfo
);
931 if (errno
== ENOENT
) {
937 // Restore the last slash
940 errno_t err
= _wmktemp_s(wide
, nameLen
+ 1);
947 _wsopen_s(&fd
, wide
, _O_RDWR
| _O_CREAT
| CF_OPENFLGS
, _SH_DENYNO
, _S_IREAD
| _S_IWRITE
);
949 // Convert the wide name back into the UTF8 buffer the caller supplied
950 copyToNarrowFileSystemRepresentation(wide
, bufSize
, name
);
957 #if DEPLOYMENT_TARGET_WINDOWS
958 // Utilities to convert from a volume name to a drive letter
960 Boolean
_isAFloppy(char driveLetter
)
964 Boolean retval
= false;
967 if (driveLetter
>= 'a' && driveLetter
<= 'z') {
968 driveLetter
= driveLetter
- 'a' + 'A';
971 if ((driveLetter
< 'A') || (driveLetter
> 'Z')) {
972 // invalid driveLetter; I guess it's not a floppy...
976 iDrive
= driveLetter
- 'A' + 1;
978 // On Windows NT, use the technique described in the Knowledge Base article Q115828 and in the "FLOPPY" SDK sample.
979 wsprintf(tsz
, TEXT("\\\\.\\%c:"), TEXT('@') + iDrive
);
980 h
= CreateFile(tsz
, 0, FILE_SHARE_WRITE
, 0, OPEN_EXISTING
, 0, 0);
981 if (h
!= INVALID_HANDLE_VALUE
)
983 DISK_GEOMETRY Geom
[20];
986 if (DeviceIoControl (h
, IOCTL_DISK_GET_MEDIA_TYPES
, 0, 0,
987 Geom
, sizeof(Geom
), &cb
, 0)
990 switch (Geom
[0].MediaType
)
992 case F5_1Pt2_512
: // 5.25 1.2MB floppy
993 case F5_360_512
: // 5.25 360K floppy
994 case F5_320_512
: // 5.25 320K floppy
995 case F5_320_1024
: // 5.25 320K floppy
996 case F5_180_512
: // 5.25 180K floppy
997 case F5_160_512
: // 5.25 160K floppy
998 case F3_1Pt44_512
: // 3.5 1.44MB floppy
999 case F3_2Pt88_512
: // 3.5 2.88MB floppy
1000 case F3_20Pt8_512
: // 3.5 20.8MB floppy
1001 case F3_720_512
: // 3.5 720K floppy
1014 extern CFStringRef
CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr
) {
1015 if (!volNameStr
) return NULL
;
1017 // This code is designed to match as closely as possible code from QuickTime's library
1018 CFIndex strLen
= CFStringGetLength(volNameStr
);
1024 long length
, result
;
1025 wchar_t *driveNames
= NULL
;
1027 // Get the size of the buffer to store the list of drives
1028 length
= GetLogicalDriveStringsW(0, 0);
1033 driveNames
= (wchar_t *)malloc((length
+ 1) * sizeof(wchar_t));
1034 result
= GetLogicalDriveStringsW(length
, driveNames
);
1036 if (!result
|| result
> length
) {
1041 // Get the volume name string into a wide buffer
1042 wchar_t *theVolumeName
= (wchar_t *)malloc((strLen
+ 1) * sizeof(wchar_t));
1043 CFStringGetCharacters(volNameStr
, CFRangeMake(0, strLen
), (UniChar
*)theVolumeName
);
1044 theVolumeName
[strLen
] = 0;
1046 // lowercase volume name
1047 _wcslwr(theVolumeName
);
1049 // Iterate through the drive names, looking for something that matches
1050 wchar_t *drivePtr
= driveNames
;
1051 CFStringRef drivePathResult
= NULL
;
1056 if (!_isAFloppy((char)*drivePtr
)) {
1058 DWORD whoCares1
, whoCares2
;
1059 BOOL getVolInfoSucceeded
;
1060 UniChar thisVolumeName
[MAX_PATH
];
1062 // Convert this drive string into a volume name
1063 oldErrorMode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
1064 getVolInfoSucceeded
= GetVolumeInformationW(drivePtr
, (LPWSTR
)thisVolumeName
, sizeof(thisVolumeName
), NULL
, &whoCares1
, &whoCares2
, NULL
, 0);
1065 SetErrorMode(oldErrorMode
);
1067 if (getVolInfoSucceeded
) {
1068 _wcslwr((wchar_t *)thisVolumeName
);
1070 // If the volume corresponding to this drive matches the input volume
1071 // then this drive is the winner.
1072 if (!wcscmp((const wchar_t *)thisVolumeName
, theVolumeName
) ||
1073 (*thisVolumeName
== 0x00 && (CFStringCompare(volNameStr
, CFSTR("NONAME"), 0) == kCFCompareEqualTo
))) {
1074 drivePathResult
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)drivePtr
, wcslen(drivePtr
));
1080 drivePtr
+= wcslen(drivePtr
) + 1;
1085 free(theVolumeName
);
1086 return drivePathResult
;
1089 #endif // DEPLOYMENT_TARGET_WINDOWS
1092 #pragma mark Linux OSAtomic
1094 #if DEPLOYMENT_TARGET_LINUX
1096 bool OSAtomicCompareAndSwapPtr(void *oldp
, void *newp
, void *volatile *dst
)
1098 return __sync_bool_compare_and_swap(dst
, oldp
, newp
);
1101 bool OSAtomicCompareAndSwapLong(long oldl
, long newl
, long volatile *dst
)
1103 return __sync_val_compare_and_swap(dst
, oldl
, newl
);
1106 bool OSAtomicCompareAndSwapPtrBarrier(void *oldp
, void *newp
, void *volatile *dst
)
1108 return __sync_bool_compare_and_swap(dst
, oldp
, newp
);
1111 int32_t OSAtomicAdd32Barrier( int32_t theAmount
, volatile int32_t *theValue
) {
1112 return __sync_fetch_and_add(theValue
, theAmount
) + theAmount
;
1115 bool OSAtomicCompareAndSwap32Barrier(int32_t oldValue
, int32_t newValue
, volatile int32_t *theValue
) {
1116 return __sync_bool_compare_and_swap(theValue
, oldValue
, newValue
);
1119 int32_t OSAtomicDecrement32Barrier(volatile int32_t *dst
)
1121 return OSAtomicAdd32Barrier(-1, dst
);
1124 int32_t OSAtomicIncrement32Barrier(volatile int32_t *dst
)
1126 return OSAtomicAdd32Barrier(1, dst
);
1129 int32_t OSAtomicAdd32( int32_t theAmount
, volatile int32_t *theValue
) {
1130 return OSAtomicAdd32Barrier(theAmount
, theValue
);
1133 int32_t OSAtomicIncrement32(volatile int32_t *theValue
) {
1134 return OSAtomicIncrement32Barrier(theValue
);
1137 int32_t OSAtomicDecrement32(volatile int32_t *theValue
) {
1138 return OSAtomicDecrement32Barrier(theValue
);
1141 void OSMemoryBarrier() {
1142 __sync_synchronize();
1145 #endif // DEPLOYMENT_TARGET_LINUX
1148 #pragma mark Windows and Linux Helpers
1150 #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
1154 __private_extern__
int asprintf(char **ret
, const char *format
, ...) {
1157 *ret
= (char *) malloc(sz
* sizeof(char));
1158 if (!*ret
) return -1;
1159 va_start(args
, format
);
1160 int cnt
= vsnprintf(*ret
, sz
, format
, args
);
1162 if (cnt
< sz
- 1) return cnt
;
1164 char *oldret
= *ret
;
1165 *ret
= (char *) realloc(*ret
, sz
* sizeof(char));
1166 if (!*ret
&& oldret
) free(oldret
);
1167 if (!*ret
) return -1;
1168 va_start(args
, format
);
1169 cnt
= vsnprintf(*ret
, sz
, format
, args
);
1171 if (cnt
< sz
- 1) return cnt
;