2 * Copyright (c) 2012 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-2012, 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 || DEPLOYMENT_TARGET_EMBEDDED_MINI
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_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
52 #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
54 #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
57 extern void __CFGetUGIDs(uid_t
*euid
, gid_t
*egid
);
59 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
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
;
70 #if DEPLOYMENT_TARGET_WINDOWS
71 // Returns the path to the CF DLL, which we can then use to find resources like char sets
72 bool bDllPathCached
= false;
73 __private_extern__
const wchar_t *_CFDLLPath(void) {
74 static wchar_t cachedPath
[MAX_PATH
+1];
76 if (!bDllPathCached
) {
78 // might be nice to get this from the project file at some point
79 wchar_t *DLLFileName
= L
"CoreFoundation_debug.dll";
81 wchar_t *DLLFileName
= L
"CoreFoundation.dll";
83 HMODULE ourModule
= GetModuleHandleW(DLLFileName
);
85 CFAssert(ourModule
, __kCFLogAssertion
, "GetModuleHandle failed");
87 DWORD wResult
= GetModuleFileNameW(ourModule
, cachedPath
, MAX_PATH
+1);
88 CFAssert1(wResult
> 0, __kCFLogAssertion
, "GetModuleFileName failed: %d", GetLastError());
89 CFAssert1(wResult
< MAX_PATH
+1, __kCFLogAssertion
, "GetModuleFileName result truncated: %s", cachedPath
);
91 // strip off last component, the DLL name
93 for (idx
= wResult
- 1; idx
; idx
--) {
94 if ('\\' == cachedPath
[idx
]) {
95 cachedPath
[idx
] = '\0';
99 bDllPathCached
= true;
105 static const char *__CFProcessPath
= NULL
;
106 static const char *__CFprogname
= NULL
;
108 const char **_CFGetProgname(void) {
110 _CFProcessPath(); // sets up __CFprogname as a side-effect
111 return &__CFprogname
;
114 const char **_CFGetProcessPath(void) {
115 if (!__CFProcessPath
)
116 _CFProcessPath(); // sets up __CFProcessPath as a side-effect
117 return &__CFProcessPath
;
120 #if DEPLOYMENT_TARGET_WINDOWS
121 const char *_CFProcessPath(void) {
122 if (__CFProcessPath
) return __CFProcessPath
;
123 wchar_t buf
[CFMaxPathSize
] = {0};
124 DWORD rlen
= GetModuleFileNameW(NULL
, buf
, sizeof(buf
) / sizeof(buf
[0]));
126 char asciiBuf
[CFMaxPathSize
] = {0};
127 int res
= WideCharToMultiByte(CP_UTF8
, 0, buf
, rlen
, asciiBuf
, sizeof(asciiBuf
) / sizeof(asciiBuf
[0]), NULL
, NULL
);
129 __CFProcessPath
= strdup(asciiBuf
);
130 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
131 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
134 if (!__CFProcessPath
) {
135 __CFProcessPath
= "";
136 __CFprogname
= __CFProcessPath
;
138 return __CFProcessPath
;
142 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
143 const char *_CFProcessPath(void) {
144 if (__CFProcessPath
) return __CFProcessPath
;
145 #if DEPLOYMENT_TARGET_MACOSX
147 const char *path
= (char *)__CFgetenv("CFProcessPath");
149 __CFProcessPath
= strdup(path
);
150 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
151 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
152 return __CFProcessPath
;
156 uint32_t size
= CFMaxPathSize
;
158 if (0 == _NSGetExecutablePath(buffer
, &size
)) {
159 __CFProcessPath
= strdup(buffer
);
160 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
161 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
163 if (!__CFProcessPath
) {
164 __CFProcessPath
= "";
165 __CFprogname
= __CFProcessPath
;
167 return __CFProcessPath
;
171 #if DEPLOYMENT_TARGET_LINUX
174 const char *_CFProcessPath(void) {
175 if (__CFProcessPath
) return __CFProcessPath
;
176 char buf
[CFMaxPathSize
+ 1];
178 ssize_t res
= readlink("/proc/self/exe", buf
, CFMaxPathSize
);
180 // null terminate, readlink does not
182 __CFProcessPath
= strdup(buf
);
183 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
184 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
186 __CFProcessPath
= "";
187 __CFprogname
= __CFProcessPath
;
189 return __CFProcessPath
;
193 __private_extern__ CFStringRef
_CFProcessNameString(void) {
194 static CFStringRef __CFProcessNameString
= NULL
;
195 if (!__CFProcessNameString
) {
196 const char *processName
= *_CFGetProgname();
197 if (!processName
) processName
= "";
198 CFStringRef newStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, processName
, kCFPlatformInterfaceStringEncoding
);
199 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *) newStr
, (void * volatile *)& __CFProcessNameString
)) {
200 CFRelease(newStr
); // someone else made the assignment, so just release the extra string.
203 return __CFProcessNameString
;
207 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
210 #include <sys/param.h>
212 // Set the fallBackToHome parameter to true if we should fall back to the HOME environment variable if all else fails. Otherwise return NULL.
213 static CFURLRef
_CFCopyHomeDirURLForUser(struct passwd
*upwd
, bool fallBackToHome
) {
214 const char *fixedHomePath
= issetugid() ? NULL
: __CFgetenv("CFFIXED_USER_HOME");
215 const char *homePath
= NULL
;
217 // Calculate the home directory we will use
218 // First try CFFIXED_USER_HOME (only if not setugid), then fall back to the upwd, then fall back to HOME environment variable
219 CFURLRef home
= NULL
;
220 if (!issetugid() && fixedHomePath
) home
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)fixedHomePath
, strlen(fixedHomePath
), true);
221 if (!home
&& upwd
&& upwd
->pw_dir
) home
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)upwd
->pw_dir
, strlen(upwd
->pw_dir
), true);
222 if (fallBackToHome
&& !home
) homePath
= __CFgetenv("HOME");
223 if (fallBackToHome
&& !home
&& homePath
) home
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)homePath
, strlen(homePath
), true);
231 #define CFMaxHostNameLength 256
232 #define CFMaxHostNameSize (CFMaxHostNameLength+1)
234 __private_extern__ CFStringRef
_CFStringCreateHostName(void) {
235 char myName
[CFMaxHostNameSize
];
237 // return @"" instead of nil a la CFUserName() and Ali Ozer
238 if (0 != gethostname(myName
, CFMaxHostNameSize
)) myName
[0] = '\0';
239 return CFStringCreateWithCString(kCFAllocatorSystemDefault
, myName
, kCFPlatformInterfaceStringEncoding
);
242 /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
243 These can return NULL.
245 CF_EXPORT CFStringRef
CFGetUserName(void) {
246 return CFCopyUserName();
249 CF_EXPORT CFStringRef
CFCopyUserName(void) {
250 CFStringRef result
= NULL
;
251 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
253 __CFGetUGIDs(&euid
, NULL
);
254 struct passwd
*upwd
= getpwuid(euid
? euid
: getuid());
255 if (upwd
&& upwd
->pw_name
) {
256 result
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, upwd
->pw_name
, kCFPlatformInterfaceStringEncoding
);
258 const char *cuser
= __CFgetenv("USER");
260 result
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, cuser
, kCFPlatformInterfaceStringEncoding
);
263 #elif DEPLOYMENT_TARGET_WINDOWS
264 wchar_t username
[1040];
267 if (GetUserNameW(username
, &size
)) {
268 // discount the extra NULL by decrementing the size
269 result
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)username
, size
- 1);
271 const char *cname
= __CFgetenv("USERNAME");
273 result
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, cname
, kCFPlatformInterfaceStringEncoding
);
277 #error Dont know how to compute user name on this platform
280 result
= (CFStringRef
)CFRetain(CFSTR(""));
284 CFURLRef
CFCopyHomeDirectoryURL(void) {
285 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
287 __CFGetUGIDs(&euid
, NULL
);
288 struct passwd
*upwd
= getpwuid(euid
? euid
: getuid());
289 return _CFCopyHomeDirURLForUser(upwd
, true);
290 #elif DEPLOYMENT_TARGET_WINDOWS
291 CFURLRef retVal
= NULL
;
293 CFStringRef str
= NULL
;
295 UniChar pathChars
[MAX_PATH
];
296 if (S_OK
== SHGetFolderPathW(NULL
, CSIDL_PROFILE
, NULL
, SHGFP_TYPE_CURRENT
, (wchar_t *)pathChars
)) {
297 len
= (CFIndex
)wcslen((wchar_t *)pathChars
);
298 str
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, pathChars
, len
);
299 retVal
= CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault
, str
, kCFURLWindowsPathStyle
, true);
304 // Fall back to environment variable, but this will not be unicode compatible
305 const char *cpath
= __CFgetenv("HOMEPATH");
306 const char *cdrive
= __CFgetenv("HOMEDRIVE");
307 if (cdrive
&& cpath
) {
308 char fullPath
[CFMaxPathSize
];
309 strlcpy(fullPath
, cdrive
, sizeof(fullPath
));
310 strlcat(fullPath
, cpath
, sizeof(fullPath
));
311 str
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, fullPath
, kCFPlatformInterfaceStringEncoding
);
312 retVal
= CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault
, str
, kCFURLWindowsPathStyle
, true);
318 // Last resort: We have to get "some" directory location, so fall-back to the processes current directory.
319 UniChar currDir
[MAX_PATH
];
320 DWORD dwChars
= GetCurrentDirectoryW(MAX_PATH
+ 1, (wchar_t *)currDir
);
322 len
= (CFIndex
)wcslen((wchar_t *)currDir
);
323 str
= CFStringCreateWithCharacters(kCFAllocatorDefault
, currDir
, len
);
324 retVal
= CFURLCreateWithFileSystemPath(NULL
, str
, kCFURLWindowsPathStyle
, true);
329 // 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.
330 CFStringRef testPath
= CFURLCopyFileSystemPath(retVal
, kCFURLWindowsPathStyle
);
331 if (CFStringGetLength(testPath
) == 0) {
335 if (testPath
) CFRelease(testPath
);
339 #error Dont know how to compute users home directories on this platform
343 CF_EXPORT CFURLRef
CFCopyHomeDirectoryURLForUser(CFStringRef uName
) {
344 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
347 __CFGetUGIDs(&euid
, NULL
);
348 struct passwd
*upwd
= getpwuid(euid
? euid
: getuid());
349 return _CFCopyHomeDirURLForUser(upwd
, true);
351 struct passwd
*upwd
= NULL
;
352 char buf
[128], *user
;
353 SInt32 len
= CFStringGetLength(uName
), size
= CFStringGetMaximumSizeForEncoding(len
, kCFPlatformInterfaceStringEncoding
);
358 user
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
+1, 0);
360 if (CFStringGetBytes(uName
, CFRangeMake(0, len
), kCFPlatformInterfaceStringEncoding
, 0, true, (uint8_t *)user
, size
, &usedSize
) == len
) {
361 user
[usedSize
] = '\0';
362 upwd
= getpwnam(user
);
365 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, user
);
367 return _CFCopyHomeDirURLForUser(upwd
, false);
369 #elif DEPLOYMENT_TARGET_WINDOWS
370 // This code can only get the directory for the current user
371 CFStringRef userName
= uName
? CFCopyUserName() : NULL
;
372 if (uName
&& !CFEqual(uName
, userName
)) {
373 CFLog(kCFLogLevelError
, CFSTR("CFCopyHomeDirectoryURLForUser(): Unable to get home directory for other user"));
374 if (userName
) CFRelease(userName
);
377 if (userName
) CFRelease(userName
);
378 return CFCopyHomeDirectoryURL();
380 #error Dont know how to compute users home directories on this platform
385 #undef CFMaxHostNameLength
386 #undef CFMaxHostNameSize
388 #if DEPLOYMENT_TARGET_WINDOWS
389 CF_INLINE CFIndex
strlen_UniChar(const UniChar
* p
) {
396 //#include <shfolder.h>
398 * _CFCreateApplicationRepositoryPath returns the path to the application's
399 * repository in a CFMutableStringRef. The path returned will be:
400 * <nFolder_path>\Apple Computer\<bundle_name>\
401 * or if the bundle name cannot be obtained:
402 * <nFolder_path>\Apple Computer\
403 * where nFolder_path is obtained by calling SHGetFolderPath with nFolder
404 * (for example, with CSIDL_APPDATA or CSIDL_LOCAL_APPDATA).
406 * The CFMutableStringRef result must be released by the caller.
408 * If anything fails along the way, the result will be NULL.
410 CF_EXPORT CFMutableStringRef
_CFCreateApplicationRepositoryPath(CFAllocatorRef alloc
, int nFolder
) {
411 CFMutableStringRef result
= NULL
;
412 UniChar szPath
[MAX_PATH
];
414 // get the current path to the data repository: CSIDL_APPDATA (roaming) or CSIDL_LOCAL_APPDATA (nonroaming)
415 if (S_OK
== SHGetFolderPathW(NULL
, nFolder
, NULL
, 0, (wchar_t *) szPath
)) {
416 CFStringRef directoryPath
;
418 // make it a CFString
419 directoryPath
= CFStringCreateWithCharacters(alloc
, szPath
, strlen_UniChar(szPath
));
422 CFStringRef bundleName
;
423 CFStringRef completePath
;
425 // attempt to get the bundle name
426 bundle
= CFBundleGetMainBundle();
428 bundleName
= (CFStringRef
)CFBundleGetValueForInfoDictionaryKey(bundle
, kCFBundleNameKey
);
435 // the path will be "<directoryPath>\Apple Computer\<bundleName>\" if there is a bundle name
436 completePath
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@\\Apple Computer\\%@\\"), directoryPath
, bundleName
);
439 // or "<directoryPath>\Apple Computer\" if there is no bundle name.
440 completePath
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@\\Apple Computer\\"), directoryPath
);
443 CFRelease(directoryPath
);
445 // make a mutable copy to return
447 result
= CFStringCreateMutableCopy(alloc
, 0, completePath
);
448 CFRelease(completePath
);
458 #pragma mark Thread Functions
460 #if DEPLOYMENT_TARGET_WINDOWS
462 // This code from here:
463 // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
465 const DWORD MS_VC_EXCEPTION
=0x406D1388;
467 typedef struct tagTHREADNAME_INFO
469 DWORD dwType
; // Must be 0x1000.
470 LPCSTR szName
; // Pointer to name (in user addr space).
471 DWORD dwThreadID
; // Thread ID (-1=caller thread).
472 DWORD dwFlags
; // Reserved for future use, must be zero.
476 CF_EXPORT
void _NS_pthread_setname_np(const char *name
) {
477 THREADNAME_INFO info
;
478 info
.dwType
= 0x1000;
480 info
.dwThreadID
= GetCurrentThreadId();
485 RaiseException( MS_VC_EXCEPTION
, 0, sizeof(info
)/sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
487 __except(EXCEPTION_EXECUTE_HANDLER
)
492 static pthread_t __initialPthread
= { NULL
, 0 };
494 CF_EXPORT
int _NS_pthread_main_np() {
495 pthread_t me
= pthread_self();
496 if (NULL
== __initialPthread
.p
) {
497 __initialPthread
.p
= me
.p
;
498 __initialPthread
.x
= me
.x
;
500 return (pthread_equal(__initialPthread
, me
));
506 #pragma mark Thread Local Data
508 // If slot >= CF_TSD_MAX_SLOTS, the SPI functions will crash at NULL + slot address.
509 // If thread data has been torn down, these functions should crash on CF_TSD_BAD_PTR + slot address.
510 #define CF_TSD_MAX_SLOTS 70
512 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
513 #define CF_TSD_KEY 55
516 // Windows and Linux, not sure how many times the destructor could get called; CF_TSD_MAX_DESTRUCTOR_CALLS could be 1
518 #define CF_TSD_BAD_PTR ((void *)0x1000)
520 typedef void (*tsdDestructor
)(void *);
522 // Data structure to hold TSD data, cleanup functions for each
523 typedef struct __CFTSDTable
{
524 uint32_t destructorCount
;
525 uintptr_t data
[CF_TSD_MAX_SLOTS
];
526 tsdDestructor destructors
[CF_TSD_MAX_SLOTS
];
529 static void __CFTSDFinalize(void *arg
);
531 #if DEPLOYMENT_TARGET_WINDOWS
533 static DWORD __CFTSDIndexKey
= 0xFFFFFFFF;
535 // Called from CFRuntime's startup code, on Windows only
536 __private_extern__
void __CFTSDWindowsInitialize() {
537 __CFTSDIndexKey
= TlsAlloc();
540 // Called from CFRuntime's cleanup code, on Windows only
541 __private_extern__
void __CFTSDWindowsCleanup() {
542 TlsFree(__CFTSDIndexKey
);
545 // Called for each thread as it exits, on Windows only
546 __private_extern__
void __CFFinalizeWindowsThreadData() {
547 // 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:
548 // <rdar://problem/8989063> REGRESSION(CF-610-CF-611): Crash closing Safari in BonjourDB destructor (Windows)
549 // <rdar://problem/9326814> SyncUIHandler crashes after conflict is resolved and we do SyncNow
550 // 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).
551 // So instead we just finalize once, which is the behavior pre-Airwolf anyway
552 __CFTSDFinalize(TlsGetValue(__CFTSDIndexKey
));
557 #if DEPLOYMENT_TARGET_LINUX
559 static pthread_key_t __CFTSDIndexKey
;
561 // Called from CFRuntime's startup code, on Linux only
562 __private_extern__
void __CFTSDLinuxInitialize() {
563 (void)pthread_key_create(&__CFTSDIndexKey
, __CFTSDFinalize
);
568 static void __CFTSDSetSpecific(void *arg
) {
569 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
570 pthread_setspecific(CF_TSD_KEY
, arg
);
571 #elif DEPLOYMENT_TARGET_LINUX
572 pthread_setspecific(__CFTSDIndexKey
, arg
);
573 #elif DEPLOYMENT_TARGET_WINDOWS
574 TlsSetValue(__CFTSDIndexKey
, arg
);
578 static void *__CFTSDGetSpecific() {
579 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
580 return pthread_getspecific(CF_TSD_KEY
);
581 #elif DEPLOYMENT_TARGET_LINUX
582 return pthread_getspecific(__CFTSDIndexKey
);
583 #elif DEPLOYMENT_TARGET_WINDOWS
584 return TlsGetValue(__CFTSDIndexKey
);
588 static void __CFTSDFinalize(void *arg
) {
589 // Set our TSD so we're called again by pthreads. It will call the destructor PTHREAD_DESTRUCTOR_ITERATIONS times as long as a value is set in the thread specific data. We handle each case below.
590 __CFTSDSetSpecific(arg
);
592 if (!arg
|| arg
== CF_TSD_BAD_PTR
) {
593 // We've already been destroyed. The call above set the bad pointer again. Now we just return.
597 __CFTSDTable
*table
= (__CFTSDTable
*)arg
;
598 table
->destructorCount
++;
600 // On first calls invoke destructor. Later we destroy the data.
601 // 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.
602 // This logic is basically the same as what pthreads does. We just skip the 'created' flag.
603 for (int32_t i
= 0; i
< CF_TSD_MAX_SLOTS
; i
++) {
604 if (table
->data
[i
] && table
->destructors
[i
]) {
605 uintptr_t old
= table
->data
[i
];
606 table
->data
[i
] = (uintptr_t)NULL
;
607 table
->destructors
[i
]((void *)(old
));
611 if (table
->destructorCount
== PTHREAD_DESTRUCTOR_ITERATIONS
- 1) { // On PTHREAD_DESTRUCTOR_ITERATIONS-1 call, destroy our data
614 // Now if the destructor is called again we will take the shortcut at the beginning of this function.
615 __CFTSDSetSpecific(CF_TSD_BAD_PTR
);
620 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
621 extern int pthread_key_init_np(int, void (*)(void *));
624 // Get or initialize a thread local storage. It is created on demand.
625 static __CFTSDTable
*__CFTSDGetTable() {
626 __CFTSDTable
*table
= (__CFTSDTable
*)__CFTSDGetSpecific();
627 // Make sure we're not setting data again after destruction.
628 if (table
== CF_TSD_BAD_PTR
) {
631 // Create table on demand
633 // This memory is freed in the finalize function
634 table
= (__CFTSDTable
*)calloc(1, sizeof(__CFTSDTable
));
635 // 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.
636 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
637 pthread_key_init_np(CF_TSD_KEY
, __CFTSDFinalize
);
639 __CFTSDSetSpecific(table
);
646 // For the use of CF and Foundation only
647 CF_EXPORT
void *_CFGetTSD(uint32_t slot
) {
648 if (slot
> CF_TSD_MAX_SLOTS
) {
649 _CFLogSimple(kCFLogLevelError
, "Error: TSD slot %d out of range (get)", slot
);
652 __CFTSDTable
*table
= __CFTSDGetTable();
654 // Someone is getting TSD during thread destruction. The table is gone, so we can't get any data anymore.
655 _CFLogSimple(kCFLogLevelWarning
, "Warning: TSD slot %d retrieved but the thread data has already been torn down.", slot
);
658 uintptr_t *slots
= (uintptr_t *)(table
->data
);
659 return (void *)slots
[slot
];
662 // For the use of CF and Foundation only
663 CF_EXPORT
void *_CFSetTSD(uint32_t slot
, void *newVal
, tsdDestructor destructor
) {
664 if (slot
> CF_TSD_MAX_SLOTS
) {
665 _CFLogSimple(kCFLogLevelError
, "Error: TSD slot %d out of range (set)", slot
);
668 __CFTSDTable
*table
= __CFTSDGetTable();
670 // Someone is setting TSD during thread destruction. The table is gone, so we can't get any data anymore.
671 _CFLogSimple(kCFLogLevelWarning
, "Warning: TSD slot %d set but the thread data has already been torn down.", slot
);
675 void *oldVal
= (void *)table
->data
[slot
];
677 table
->data
[slot
] = (uintptr_t)newVal
;
678 table
->destructors
[slot
] = destructor
;
685 #pragma mark Windows Wide to UTF8 and UTF8 to Wide
687 #if DEPLOYMENT_TARGET_WINDOWS
688 /* 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. */
690 #include <sys/stat.h>
693 // 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.
694 static wchar_t *createWideFileSystemRepresentation(const char *str
, CFIndex
*resultLen
) {
695 // Get the real length of the string in UTF16 characters
696 CFStringRef cfStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
);
697 CFIndex strLen
= CFStringGetLength(cfStr
);
699 // Allocate a wide buffer to hold the converted string, including space for a NULL terminator
700 wchar_t *wideBuf
= (wchar_t *)malloc((strLen
+ 1) * sizeof(wchar_t));
702 // Copy the string into the buffer and terminate
703 CFStringGetCharacters(cfStr
, CFRangeMake(0, strLen
), (UniChar
*)wideBuf
);
707 if (resultLen
) *resultLen
= strLen
;
711 // Copies a UTF16 buffer into a supplied UTF8 buffer.
712 static void copyToNarrowFileSystemRepresentation(const wchar_t *wide
, CFIndex dstBufSize
, char *dstbuf
) {
713 // Get the real length of the wide string in UTF8 characters
714 CFStringRef cfStr
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)wide
, wcslen(wide
));
715 CFIndex strLen
= CFStringGetLength(cfStr
);
718 // Copy the wide string into the buffer and terminate
719 CFStringGetBytes(cfStr
, CFRangeMake(0, strLen
), kCFStringEncodingUTF8
, 0, false, (uint8_t *)dstbuf
, dstBufSize
, &bytesUsed
);
720 dstbuf
[bytesUsed
] = 0;
725 CF_EXPORT
int _NS_stat(const char *name
, struct _stat
*st
) {
726 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
727 int res
= _wstat(wide
, st
);
732 CF_EXPORT
int _NS_mkdir(const char *name
) {
733 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
734 int res
= _wmkdir(wide
);
739 CF_EXPORT
int _NS_rmdir(const char *name
) {
740 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
741 int res
= _wrmdir(wide
);
746 CF_EXPORT
int _NS_chmod(const char *name
, int mode
) {
747 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
751 if (mode
| 0400) newMode
|= _S_IREAD
;
752 if (mode
| 0200) newMode
|= _S_IWRITE
;
753 if (mode
| 0100) newMode
|= _S_IEXEC
;
755 int res
= _wchmod(wide
, newMode
);
760 CF_EXPORT
int _NS_unlink(const char *name
) {
761 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
762 int res
= _wunlink(wide
);
767 // Warning: this doesn't support dstbuf as null even though 'getcwd' does
768 CF_EXPORT
char *_NS_getcwd(char *dstbuf
, size_t size
) {
770 CFLog(kCFLogLevelWarning
, CFSTR("CFPlatform: getcwd called with null buffer"));
774 wchar_t *buf
= _wgetcwd(NULL
, 0);
779 // Convert result to UTF8
780 copyToNarrowFileSystemRepresentation(buf
, (CFIndex
)size
, dstbuf
);
785 CF_EXPORT
char *_NS_getenv(const char *name
) {
787 // We have to be careful what happens here, because getenv is called during cf initialization, and things like cfstring may not be working yet
791 CF_EXPORT
int _NS_rename(const char *oldName
, const char *newName
) {
792 wchar_t *oldWide
= createWideFileSystemRepresentation(oldName
, NULL
);
793 wchar_t *newWide
= createWideFileSystemRepresentation(newName
, NULL
);
794 // _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
795 // To simulate the Mac OS behavior, we use the Win32 API then fill out errno if something goes wrong
796 BOOL winRes
= MoveFileExW(oldWide
, newWide
, MOVEFILE_REPLACE_EXISTING
);
797 DWORD error
= GetLastError();
803 case ERROR_FILE_NOT_FOUND
:
804 case ERROR_PATH_NOT_FOUND
:
805 case ERROR_OPEN_FAILED
:
808 case ERROR_ACCESS_DENIED
:
817 return (winRes
? 0 : -1);
820 CF_EXPORT
int _NS_open(const char *name
, int oflag
, int pmode
) {
821 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
823 _wsopen_s(&fd
, wide
, oflag
, _SH_DENYNO
, _S_IREAD
| _S_IWRITE
);
828 CF_EXPORT
int _NS_chdir(const char *name
) {
829 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
830 int res
= _wchdir(wide
);
835 CF_EXPORT
int _NS_access(const char *name
, int amode
) {
836 // execute is always true
837 if (amode
== 1) return 0;
839 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
840 // we only care about the read-only (04) and write-only (02) bits, so mask octal 06
841 int res
= _waccess(wide
, amode
& 06);
846 // This is a bit different than the standard 'mkstemp', because the size parameter is needed so we know the size of the UTF8 buffer
847 // Also, we don't avoid the race between creating a temporary file name and opening it on Windows like we do on Mac
848 CF_EXPORT
int _NS_mkstemp(char *name
, int bufSize
) {
850 wchar_t *wide
= createWideFileSystemRepresentation(name
, &nameLen
);
852 // 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.
853 // Look for the last '\' in the path
854 wchar_t *lastSlash
= wcsrchr(wide
, '\\');
860 // Set the last slash to NULL temporarily and use it for _wstat
862 struct _stat dirInfo
;
863 int res
= _wstat(wide
, &dirInfo
);
865 if (errno
== ENOENT
) {
871 // Restore the last slash
874 errno_t err
= _wmktemp_s(wide
, nameLen
+ 1);
881 _wsopen_s(&fd
, wide
, _O_RDWR
| _O_CREAT
| CF_OPENFLGS
, _SH_DENYNO
, _S_IREAD
| _S_IWRITE
);
883 // Convert the wide name back into the UTF8 buffer the caller supplied
884 copyToNarrowFileSystemRepresentation(wide
, bufSize
, name
);
890 // Utilities to convert from a volume name to a drive letter
892 Boolean
_isAFloppy(char driveLetter
)
896 Boolean retval
= false;
899 if (driveLetter
>= 'a' && driveLetter
<= 'z') {
900 driveLetter
= driveLetter
- 'a' + 'A';
903 if ((driveLetter
< 'A') || (driveLetter
> 'Z')) {
904 // invalid driveLetter; I guess it's not a floppy...
908 iDrive
= driveLetter
- 'A' + 1;
910 // On Windows NT, use the technique described in the Knowledge Base article Q115828 and in the "FLOPPY" SDK sample.
911 wsprintf(tsz
, TEXT("\\\\.\\%c:"), TEXT('@') + iDrive
);
912 h
= CreateFile(tsz
, 0, FILE_SHARE_WRITE
, 0, OPEN_EXISTING
, 0, 0);
913 if (h
!= INVALID_HANDLE_VALUE
)
915 DISK_GEOMETRY Geom
[20];
918 if (DeviceIoControl (h
, IOCTL_DISK_GET_MEDIA_TYPES
, 0, 0,
919 Geom
, sizeof(Geom
), &cb
, 0)
922 switch (Geom
[0].MediaType
)
924 case F5_1Pt2_512
: // 5.25 1.2MB floppy
925 case F5_360_512
: // 5.25 360K floppy
926 case F5_320_512
: // 5.25 320K floppy
927 case F5_320_1024
: // 5.25 320K floppy
928 case F5_180_512
: // 5.25 180K floppy
929 case F5_160_512
: // 5.25 160K floppy
930 case F3_1Pt44_512
: // 3.5 1.44MB floppy
931 case F3_2Pt88_512
: // 3.5 2.88MB floppy
932 case F3_20Pt8_512
: // 3.5 20.8MB floppy
933 case F3_720_512
: // 3.5 720K floppy
946 extern CFStringRef
CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr
) {
947 if (!volNameStr
) return NULL
;
949 // This code is designed to match as closely as possible code from QuickTime's library
950 CFIndex strLen
= CFStringGetLength(volNameStr
);
957 wchar_t *driveNames
= NULL
;
959 // Get the size of the buffer to store the list of drives
960 length
= GetLogicalDriveStringsW(0, 0);
965 driveNames
= (wchar_t *)malloc((length
+ 1) * sizeof(wchar_t));
966 result
= GetLogicalDriveStringsW(length
, driveNames
);
968 if (!result
|| result
> length
) {
973 // Get the volume name string into a wide buffer
974 wchar_t *theVolumeName
= (wchar_t *)malloc((strLen
+ 1) * sizeof(wchar_t));
975 CFStringGetCharacters(volNameStr
, CFRangeMake(0, strLen
), (UniChar
*)theVolumeName
);
976 theVolumeName
[strLen
] = 0;
978 // lowercase volume name
979 _wcslwr(theVolumeName
);
981 // Iterate through the drive names, looking for something that matches
982 wchar_t *drivePtr
= driveNames
;
983 CFStringRef drivePathResult
= NULL
;
988 if (!_isAFloppy((char)*drivePtr
)) {
990 DWORD whoCares1
, whoCares2
;
991 BOOL getVolInfoSucceeded
;
992 UniChar thisVolumeName
[MAX_PATH
];
994 // Convert this drive string into a volume name
995 oldErrorMode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
996 getVolInfoSucceeded
= GetVolumeInformationW(drivePtr
, (LPWSTR
)thisVolumeName
, sizeof(thisVolumeName
), NULL
, &whoCares1
, &whoCares2
, NULL
, 0);
997 SetErrorMode(oldErrorMode
);
999 if (getVolInfoSucceeded
) {
1000 _wcslwr((wchar_t *)thisVolumeName
);
1002 // If the volume corresponding to this drive matches the input volume
1003 // then this drive is the winner.
1004 if (!wcscmp((const wchar_t *)thisVolumeName
, theVolumeName
) ||
1005 (*thisVolumeName
== 0x00 && (CFStringCompare(volNameStr
, CFSTR("NONAME"), 0) == kCFCompareEqualTo
))) {
1006 drivePathResult
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)drivePtr
, wcslen(drivePtr
));
1012 drivePtr
+= wcslen(drivePtr
) + 1;
1017 free(theVolumeName
);
1018 return drivePathResult
;
1022 int tz_minuteswest
; /* minutes west of Greenwich */
1023 int tz_dsttime
; /* type of dst correction */
1026 __private_extern__
int _NS_gettimeofday(struct timeval
*tv
, struct timezone
*tz
) {
1029 GetSystemTimeAsFileTime(&ft
);
1030 unsigned __int64 t
= 0;
1031 t
|= ft
.dwHighDateTime
;
1033 t
|= ft
.dwLowDateTime
;
1035 // Convert to microseconds
1038 // Difference between 1/1/1970 and 1/1/1601
1039 t
-= 11644473600000000Ui
64;
1041 // Convert microseconds to seconds
1042 tv
->tv_sec
= (long)(t
/ 1000000UL);
1043 tv
->tv_usec
= (long)(t
% 1000000UL);
1046 // We don't support tz
1050 #endif // DEPLOYMENT_TARGET_WINDOWS
1053 #pragma mark Linux OSAtomic
1055 #if DEPLOYMENT_TARGET_LINUX
1057 bool OSAtomicCompareAndSwapPtr(void *oldp
, void *newp
, void *volatile *dst
)
1059 return __sync_bool_compare_and_swap(dst
, oldp
, newp
);
1062 bool OSAtomicCompareAndSwapLong(long oldl
, long newl
, long volatile *dst
)
1064 return __sync_val_compare_and_swap(dst
, oldl
, newl
);
1067 bool OSAtomicCompareAndSwapPtrBarrier(void *oldp
, void *newp
, void *volatile *dst
)
1069 return __sync_bool_compare_and_swap(dst
, oldp
, newp
);
1072 int32_t OSAtomicAdd32Barrier( int32_t theAmount
, volatile int32_t *theValue
) {
1073 return __sync_fetch_and_add(theValue
, theAmount
) + theAmount
;
1076 bool OSAtomicCompareAndSwap32Barrier(int32_t oldValue
, int32_t newValue
, volatile int32_t *theValue
) {
1077 return __sync_bool_compare_and_swap(theValue
, oldValue
, newValue
);
1080 int32_t OSAtomicDecrement32Barrier(volatile int32_t *dst
)
1082 return OSAtomicAdd32Barrier(-1, dst
);
1085 int32_t OSAtomicIncrement32Barrier(volatile int32_t *dst
)
1087 return OSAtomicAdd32Barrier(1, dst
);
1090 int32_t OSAtomicAdd32( int32_t theAmount
, volatile int32_t *theValue
) {
1091 return OSAtomicAdd32Barrier(theAmount
, theValue
);
1094 int32_t OSAtomicIncrement32(volatile int32_t *theValue
) {
1095 return OSAtomicIncrement32Barrier(theValue
);
1098 int32_t OSAtomicDecrement32(volatile int32_t *theValue
) {
1099 return OSAtomicDecrement32Barrier(theValue
);
1102 void OSMemoryBarrier() {
1103 __sync_synchronize();
1106 #endif // DEPLOYMENT_TARGET_LINUX
1109 #pragma mark Windows and Linux Helpers
1111 #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
1115 __private_extern__
int asprintf(char **ret
, const char *format
, ...) {
1118 *ret
= (char *) malloc(sz
* sizeof(char));
1119 if (!*ret
) return -1;
1120 va_start(args
, format
);
1121 int cnt
= vsnprintf(*ret
, sz
, format
, args
);
1123 if (cnt
< sz
- 1) return cnt
;
1125 char *oldret
= *ret
;
1126 *ret
= (char *) realloc(*ret
, sz
* sizeof(char));
1127 if (!*ret
&& oldret
) free(oldret
);
1128 if (!*ret
) return -1;
1129 va_start(args
, format
);
1130 cnt
= vsnprintf(*ret
, sz
, format
, args
);
1132 if (cnt
< sz
- 1) return cnt
;