2 * Copyright (c) 2014 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-2013, 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>
40 #include <pthread/tsd_private.h>
43 #if DEPLOYMENT_TARGET_WINDOWS
48 #define getcwd _NS_getcwd
52 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
53 #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
55 #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
58 extern void __CFGetUGIDs(uid_t
*euid
, gid_t
*egid
);
60 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
61 // CoreGraphics and LaunchServices are only projects (1 Dec 2006) that use these
62 char **_CFArgv(void) { return *_NSGetArgv(); }
63 int _CFArgc(void) { return *_NSGetArgc(); }
67 CF_PRIVATE Boolean
_CFGetCurrentDirectory(char *path
, int maxlen
) {
68 return getcwd(path
, maxlen
) != NULL
;
71 #if DEPLOYMENT_TARGET_WINDOWS
72 // Returns the path to the CF DLL, which we can then use to find resources like char sets
73 bool bDllPathCached
= false;
74 CF_PRIVATE
const wchar_t *_CFDLLPath(void) {
75 static wchar_t cachedPath
[MAX_PATH
+1];
77 if (!bDllPathCached
) {
79 // might be nice to get this from the project file at some point
80 wchar_t *DLLFileName
= L
"CoreFoundation_debug.dll";
82 wchar_t *DLLFileName
= L
"CoreFoundation.dll";
84 HMODULE ourModule
= GetModuleHandleW(DLLFileName
);
86 CFAssert(ourModule
, __kCFLogAssertion
, "GetModuleHandle failed");
88 DWORD wResult
= GetModuleFileNameW(ourModule
, cachedPath
, MAX_PATH
+1);
89 CFAssert1(wResult
> 0, __kCFLogAssertion
, "GetModuleFileName failed: %d", GetLastError());
90 CFAssert1(wResult
< MAX_PATH
+1, __kCFLogAssertion
, "GetModuleFileName result truncated: %s", cachedPath
);
92 // strip off last component, the DLL name
94 for (idx
= wResult
- 1; idx
; idx
--) {
95 if ('\\' == cachedPath
[idx
]) {
96 cachedPath
[idx
] = '\0';
100 bDllPathCached
= true;
106 static const char *__CFProcessPath
= NULL
;
107 static const char *__CFprogname
= NULL
;
109 const char **_CFGetProgname(void) {
111 _CFProcessPath(); // sets up __CFprogname as a side-effect
112 return &__CFprogname
;
115 const char **_CFGetProcessPath(void) {
116 if (!__CFProcessPath
)
117 _CFProcessPath(); // sets up __CFProcessPath as a side-effect
118 return &__CFProcessPath
;
121 #if DEPLOYMENT_TARGET_WINDOWS
122 const char *_CFProcessPath(void) {
123 if (__CFProcessPath
) return __CFProcessPath
;
124 wchar_t buf
[CFMaxPathSize
] = {0};
125 DWORD rlen
= GetModuleFileNameW(NULL
, buf
, sizeof(buf
) / sizeof(buf
[0]));
127 char asciiBuf
[CFMaxPathSize
] = {0};
128 int res
= WideCharToMultiByte(CP_UTF8
, 0, buf
, rlen
, asciiBuf
, sizeof(asciiBuf
) / sizeof(asciiBuf
[0]), NULL
, NULL
);
130 __CFProcessPath
= strdup(asciiBuf
);
131 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
132 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
135 if (!__CFProcessPath
) {
136 __CFProcessPath
= "";
137 __CFprogname
= __CFProcessPath
;
139 return __CFProcessPath
;
143 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
144 const char *_CFProcessPath(void) {
145 if (__CFProcessPath
) return __CFProcessPath
;
146 #if DEPLOYMENT_TARGET_MACOSX
148 const char *path
= (char *)__CFgetenv("CFProcessPath");
150 __CFProcessPath
= strdup(path
);
151 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
152 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
153 return __CFProcessPath
;
157 uint32_t size
= CFMaxPathSize
;
159 if (0 == _NSGetExecutablePath(buffer
, &size
)) {
160 __CFProcessPath
= strdup(buffer
);
161 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
162 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
164 if (!__CFProcessPath
) {
165 __CFProcessPath
= "";
166 __CFprogname
= __CFProcessPath
;
168 return __CFProcessPath
;
172 #if DEPLOYMENT_TARGET_LINUX
175 const char *_CFProcessPath(void) {
176 if (__CFProcessPath
) return __CFProcessPath
;
177 char buf
[CFMaxPathSize
+ 1];
179 ssize_t res
= readlink("/proc/self/exe", buf
, CFMaxPathSize
);
181 // null terminate, readlink does not
183 __CFProcessPath
= strdup(buf
);
184 __CFprogname
= strrchr(__CFProcessPath
, PATH_SEP
);
185 __CFprogname
= (__CFprogname
? __CFprogname
+ 1 : __CFProcessPath
);
187 __CFProcessPath
= "";
188 __CFprogname
= __CFProcessPath
;
190 return __CFProcessPath
;
194 CF_PRIVATE CFStringRef
_CFProcessNameString(void) {
195 static CFStringRef __CFProcessNameString
= NULL
;
196 if (!__CFProcessNameString
) {
197 const char *processName
= *_CFGetProgname();
198 if (!processName
) processName
= "";
199 CFStringRef newStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, processName
, kCFPlatformInterfaceStringEncoding
);
200 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *) newStr
, (void * volatile *)& __CFProcessNameString
)) {
201 CFRelease(newStr
); // someone else made the assignment, so just release the extra string.
204 return __CFProcessNameString
;
208 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
211 #include <sys/param.h>
213 // Set the fallBackToHome parameter to true if we should fall back to the HOME environment variable if all else fails. Otherwise return NULL.
214 static CFURLRef
_CFCopyHomeDirURLForUser(struct passwd
*upwd
, bool fallBackToHome
) {
215 const char *fixedHomePath
= issetugid() ? NULL
: __CFgetenv("CFFIXED_USER_HOME");
216 const char *homePath
= NULL
;
218 // Calculate the home directory we will use
219 // First try CFFIXED_USER_HOME (only if not setugid), then fall back to the upwd, then fall back to HOME environment variable
220 CFURLRef home
= NULL
;
221 if (!issetugid() && fixedHomePath
) home
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)fixedHomePath
, strlen(fixedHomePath
), true);
222 if (!home
&& upwd
&& upwd
->pw_dir
) home
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)upwd
->pw_dir
, strlen(upwd
->pw_dir
), true);
223 if (fallBackToHome
&& !home
) homePath
= __CFgetenv("HOME");
224 if (fallBackToHome
&& !home
&& homePath
) home
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault
, (uint8_t *)homePath
, strlen(homePath
), true);
232 #define CFMaxHostNameLength 256
233 #define CFMaxHostNameSize (CFMaxHostNameLength+1)
235 CF_PRIVATE CFStringRef
_CFStringCreateHostName(void) {
236 char myName
[CFMaxHostNameSize
];
238 // return @"" instead of nil a la CFUserName() and Ali Ozer
239 if (0 != gethostname(myName
, CFMaxHostNameSize
)) myName
[0] = '\0';
240 return CFStringCreateWithCString(kCFAllocatorSystemDefault
, myName
, kCFPlatformInterfaceStringEncoding
);
243 /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
244 These can return NULL.
246 CF_EXPORT CFStringRef
CFGetUserName(void) {
247 return CFCopyUserName();
250 CF_EXPORT CFStringRef
CFCopyUserName(void) {
251 CFStringRef result
= NULL
;
252 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
254 __CFGetUGIDs(&euid
, NULL
);
255 struct passwd
*upwd
= getpwuid(euid
? euid
: getuid());
256 if (upwd
&& upwd
->pw_name
) {
257 result
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, upwd
->pw_name
, kCFPlatformInterfaceStringEncoding
);
259 const char *cuser
= __CFgetenv("USER");
261 result
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, cuser
, kCFPlatformInterfaceStringEncoding
);
264 #elif DEPLOYMENT_TARGET_WINDOWS
265 wchar_t username
[1040];
268 if (GetUserNameW(username
, &size
)) {
269 // discount the extra NULL by decrementing the size
270 result
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)username
, size
- 1);
272 const char *cname
= __CFgetenv("USERNAME");
274 result
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, cname
, kCFPlatformInterfaceStringEncoding
);
278 #error Dont know how to compute user name on this platform
281 result
= (CFStringRef
)CFRetain(CFSTR(""));
285 CFURLRef
CFCopyHomeDirectoryURL(void) {
286 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
288 __CFGetUGIDs(&euid
, NULL
);
289 struct passwd
*upwd
= getpwuid(euid
? euid
: getuid());
290 return _CFCopyHomeDirURLForUser(upwd
, true);
291 #elif DEPLOYMENT_TARGET_WINDOWS
292 CFURLRef retVal
= NULL
;
294 CFStringRef str
= NULL
;
296 UniChar pathChars
[MAX_PATH
];
297 if (S_OK
== SHGetFolderPathW(NULL
, CSIDL_PROFILE
, NULL
, SHGFP_TYPE_CURRENT
, (wchar_t *)pathChars
)) {
298 len
= (CFIndex
)wcslen((wchar_t *)pathChars
);
299 str
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, pathChars
, len
);
300 retVal
= CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault
, str
, kCFURLWindowsPathStyle
, true);
305 // Fall back to environment variable, but this will not be unicode compatible
306 const char *cpath
= __CFgetenv("HOMEPATH");
307 const char *cdrive
= __CFgetenv("HOMEDRIVE");
308 if (cdrive
&& cpath
) {
309 char fullPath
[CFMaxPathSize
];
310 strlcpy(fullPath
, cdrive
, sizeof(fullPath
));
311 strlcat(fullPath
, cpath
, sizeof(fullPath
));
312 str
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, fullPath
, kCFPlatformInterfaceStringEncoding
);
313 retVal
= CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault
, str
, kCFURLWindowsPathStyle
, true);
319 // Last resort: We have to get "some" directory location, so fall-back to the processes current directory.
320 UniChar currDir
[MAX_PATH
];
321 DWORD dwChars
= GetCurrentDirectoryW(MAX_PATH
+ 1, (wchar_t *)currDir
);
323 len
= (CFIndex
)wcslen((wchar_t *)currDir
);
324 str
= CFStringCreateWithCharacters(kCFAllocatorDefault
, currDir
, len
);
325 retVal
= CFURLCreateWithFileSystemPath(NULL
, str
, kCFURLWindowsPathStyle
, true);
330 // 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.
331 CFStringRef testPath
= CFURLCopyFileSystemPath(retVal
, kCFURLWindowsPathStyle
);
332 if (CFStringGetLength(testPath
) == 0) {
336 if (testPath
) CFRelease(testPath
);
340 #error Dont know how to compute users home directories on this platform
344 CF_EXPORT CFURLRef
CFCopyHomeDirectoryURLForUser(CFStringRef uName
) {
345 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
348 __CFGetUGIDs(&euid
, NULL
);
349 struct passwd
*upwd
= getpwuid(euid
? euid
: getuid());
350 return _CFCopyHomeDirURLForUser(upwd
, true);
352 struct passwd
*upwd
= NULL
;
353 char buf
[128], *user
;
354 SInt32 len
= CFStringGetLength(uName
), size
= CFStringGetMaximumSizeForEncoding(len
, kCFPlatformInterfaceStringEncoding
);
359 user
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
+1, 0);
361 if (CFStringGetBytes(uName
, CFRangeMake(0, len
), kCFPlatformInterfaceStringEncoding
, 0, true, (uint8_t *)user
, size
, &usedSize
) == len
) {
362 user
[usedSize
] = '\0';
363 upwd
= getpwnam(user
);
366 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, user
);
368 return _CFCopyHomeDirURLForUser(upwd
, false);
370 #elif DEPLOYMENT_TARGET_WINDOWS
371 // This code can only get the directory for the current user
372 CFStringRef userName
= uName
? CFCopyUserName() : NULL
;
373 if (uName
&& !CFEqual(uName
, userName
)) {
374 CFLog(kCFLogLevelError
, CFSTR("CFCopyHomeDirectoryURLForUser(): Unable to get home directory for other user"));
375 if (userName
) CFRelease(userName
);
378 if (userName
) CFRelease(userName
);
379 return CFCopyHomeDirectoryURL();
381 #error Dont know how to compute users home directories on this platform
386 #undef CFMaxHostNameLength
387 #undef CFMaxHostNameSize
389 #if DEPLOYMENT_TARGET_WINDOWS
390 CF_INLINE CFIndex
strlen_UniChar(const UniChar
* p
) {
397 //#include <shfolder.h>
399 * _CFCreateApplicationRepositoryPath returns the path to the application's
400 * repository in a CFMutableStringRef. The path returned will be:
401 * <nFolder_path>\Apple Computer\<bundle_name>\
402 * or if the bundle name cannot be obtained:
403 * <nFolder_path>\Apple Computer\
404 * where nFolder_path is obtained by calling SHGetFolderPath with nFolder
405 * (for example, with CSIDL_APPDATA or CSIDL_LOCAL_APPDATA).
407 * The CFMutableStringRef result must be released by the caller.
409 * If anything fails along the way, the result will be NULL.
411 CF_EXPORT CFMutableStringRef
_CFCreateApplicationRepositoryPath(CFAllocatorRef alloc
, int nFolder
) {
412 CFMutableStringRef result
= NULL
;
413 UniChar szPath
[MAX_PATH
];
415 // get the current path to the data repository: CSIDL_APPDATA (roaming) or CSIDL_LOCAL_APPDATA (nonroaming)
416 if (S_OK
== SHGetFolderPathW(NULL
, nFolder
, NULL
, 0, (wchar_t *) szPath
)) {
417 CFStringRef directoryPath
;
419 // make it a CFString
420 directoryPath
= CFStringCreateWithCharacters(alloc
, szPath
, strlen_UniChar(szPath
));
423 CFStringRef bundleName
;
424 CFStringRef completePath
;
426 // attempt to get the bundle name
427 bundle
= CFBundleGetMainBundle();
429 bundleName
= (CFStringRef
)CFBundleGetValueForInfoDictionaryKey(bundle
, kCFBundleNameKey
);
436 // the path will be "<directoryPath>\Apple Computer\<bundleName>\" if there is a bundle name
437 completePath
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@\\Apple Computer\\%@\\"), directoryPath
, bundleName
);
440 // or "<directoryPath>\Apple Computer\" if there is no bundle name.
441 completePath
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@\\Apple Computer\\"), directoryPath
);
444 CFRelease(directoryPath
);
446 // make a mutable copy to return
448 result
= CFStringCreateMutableCopy(alloc
, 0, completePath
);
449 CFRelease(completePath
);
459 #pragma mark Thread Functions
461 #if DEPLOYMENT_TARGET_WINDOWS
463 // This code from here:
464 // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
466 const DWORD MS_VC_EXCEPTION
=0x406D1388;
468 typedef struct tagTHREADNAME_INFO
470 DWORD dwType
; // Must be 0x1000.
471 LPCSTR szName
; // Pointer to name (in user addr space).
472 DWORD dwThreadID
; // Thread ID (-1=caller thread).
473 DWORD dwFlags
; // Reserved for future use, must be zero.
477 CF_EXPORT
void _NS_pthread_setname_np(const char *name
) {
478 THREADNAME_INFO info
;
479 info
.dwType
= 0x1000;
481 info
.dwThreadID
= GetCurrentThreadId();
486 RaiseException( MS_VC_EXCEPTION
, 0, sizeof(info
)/sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
488 __except(EXCEPTION_EXECUTE_HANDLER
)
493 static pthread_t __initialPthread
= { NULL
, 0 };
495 CF_EXPORT
int _NS_pthread_main_np() {
496 pthread_t me
= pthread_self();
497 if (NULL
== __initialPthread
.p
) {
498 __initialPthread
.p
= me
.p
;
499 __initialPthread
.x
= me
.x
;
501 return (pthread_equal(__initialPthread
, me
));
507 #pragma mark Thread Local Data
509 // If slot >= CF_TSD_MAX_SLOTS, the SPI functions will crash at NULL + slot address.
510 // If thread data has been torn down, these functions should crash on CF_TSD_BAD_PTR + slot address.
511 #define CF_TSD_MAX_SLOTS 70
513 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
514 static const unsigned long CF_TSD_KEY
= __PTK_FRAMEWORK_COREFOUNDATION_KEY5
;
517 // Windows and Linux, not sure how many times the destructor could get called; CF_TSD_MAX_DESTRUCTOR_CALLS could be 1
519 #define CF_TSD_BAD_PTR ((void *)0x1000)
521 typedef void (*tsdDestructor
)(void *);
523 // Data structure to hold TSD data, cleanup functions for each
524 typedef struct __CFTSDTable
{
525 uint32_t destructorCount
;
526 uintptr_t data
[CF_TSD_MAX_SLOTS
];
527 tsdDestructor destructors
[CF_TSD_MAX_SLOTS
];
530 static void __CFTSDFinalize(void *arg
);
532 #if DEPLOYMENT_TARGET_WINDOWS
534 static DWORD __CFTSDIndexKey
= 0xFFFFFFFF;
536 // Called from CFRuntime's startup code, on Windows only
537 CF_PRIVATE
void __CFTSDWindowsInitialize() {
538 __CFTSDIndexKey
= TlsAlloc();
541 // Called from CFRuntime's cleanup code, on Windows only
542 CF_PRIVATE
void __CFTSDWindowsCleanup() {
543 TlsFree(__CFTSDIndexKey
);
546 // Called for each thread as it exits, on Windows only
547 CF_PRIVATE
void __CFFinalizeWindowsThreadData() {
548 // 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:
549 // <rdar://problem/8989063> REGRESSION(CF-610-CF-611): Crash closing Safari in BonjourDB destructor (Windows)
550 // <rdar://problem/9326814> SyncUIHandler crashes after conflict is resolved and we do SyncNow
551 // 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).
552 // So instead we just finalize once, which is the behavior pre-Airwolf anyway
553 __CFTSDFinalize(TlsGetValue(__CFTSDIndexKey
));
558 #if DEPLOYMENT_TARGET_LINUX
560 static pthread_key_t __CFTSDIndexKey
;
562 // Called from CFRuntime's startup code, on Linux only
563 CF_PRIVATE
void __CFTSDLinuxInitialize() {
564 (void)pthread_key_create(&__CFTSDIndexKey
, __CFTSDFinalize
);
569 static void __CFTSDSetSpecific(void *arg
) {
570 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
571 _pthread_setspecific_direct(CF_TSD_KEY
, arg
);
572 #elif DEPLOYMENT_TARGET_LINUX
573 pthread_setspecific(__CFTSDIndexKey
, arg
);
574 #elif DEPLOYMENT_TARGET_WINDOWS
575 TlsSetValue(__CFTSDIndexKey
, arg
);
579 static void *__CFTSDGetSpecific() {
580 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
581 return _pthread_getspecific_direct(CF_TSD_KEY
);
582 #elif DEPLOYMENT_TARGET_LINUX
583 return pthread_getspecific(__CFTSDIndexKey
);
584 #elif DEPLOYMENT_TARGET_WINDOWS
585 return TlsGetValue(__CFTSDIndexKey
);
589 static void __CFTSDFinalize(void *arg
) {
590 // 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.
591 __CFTSDSetSpecific(arg
);
593 if (!arg
|| arg
== CF_TSD_BAD_PTR
) {
594 // We've already been destroyed. The call above set the bad pointer again. Now we just return.
598 __CFTSDTable
*table
= (__CFTSDTable
*)arg
;
599 table
->destructorCount
++;
601 // On first calls invoke destructor. Later we destroy the data.
602 // 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.
603 // This logic is basically the same as what pthreads does. We just skip the 'created' flag.
604 for (int32_t i
= 0; i
< CF_TSD_MAX_SLOTS
; i
++) {
605 if (table
->data
[i
] && table
->destructors
[i
]) {
606 uintptr_t old
= table
->data
[i
];
607 table
->data
[i
] = (uintptr_t)NULL
;
608 table
->destructors
[i
]((void *)(old
));
612 if (table
->destructorCount
== PTHREAD_DESTRUCTOR_ITERATIONS
- 1) { // On PTHREAD_DESTRUCTOR_ITERATIONS-1 call, destroy our data
615 // Now if the destructor is called again we will take the shortcut at the beginning of this function.
616 __CFTSDSetSpecific(CF_TSD_BAD_PTR
);
621 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
622 extern int pthread_key_init_np(int, void (*)(void *));
625 // Get or initialize a thread local storage. It is created on demand.
626 static __CFTSDTable
*__CFTSDGetTable() {
627 __CFTSDTable
*table
= (__CFTSDTable
*)__CFTSDGetSpecific();
628 // Make sure we're not setting data again after destruction.
629 if (table
== CF_TSD_BAD_PTR
) {
632 // Create table on demand
634 // This memory is freed in the finalize function
635 table
= (__CFTSDTable
*)calloc(1, sizeof(__CFTSDTable
));
636 // 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.
637 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
638 pthread_key_init_np(CF_TSD_KEY
, __CFTSDFinalize
);
640 __CFTSDSetSpecific(table
);
647 // For the use of CF and Foundation only
648 CF_EXPORT
void *_CFGetTSD(uint32_t slot
) {
649 if (slot
> CF_TSD_MAX_SLOTS
) {
650 _CFLogSimple(kCFLogLevelError
, "Error: TSD slot %d out of range (get)", slot
);
653 __CFTSDTable
*table
= __CFTSDGetTable();
655 // Someone is getting TSD during thread destruction. The table is gone, so we can't get any data anymore.
656 _CFLogSimple(kCFLogLevelWarning
, "Warning: TSD slot %d retrieved but the thread data has already been torn down.", slot
);
659 uintptr_t *slots
= (uintptr_t *)(table
->data
);
660 return (void *)slots
[slot
];
663 // For the use of CF and Foundation only
664 CF_EXPORT
void *_CFSetTSD(uint32_t slot
, void *newVal
, tsdDestructor destructor
) {
665 if (slot
> CF_TSD_MAX_SLOTS
) {
666 _CFLogSimple(kCFLogLevelError
, "Error: TSD slot %d out of range (set)", slot
);
669 __CFTSDTable
*table
= __CFTSDGetTable();
671 // Someone is setting TSD during thread destruction. The table is gone, so we can't get any data anymore.
672 _CFLogSimple(kCFLogLevelWarning
, "Warning: TSD slot %d set but the thread data has already been torn down.", slot
);
676 void *oldVal
= (void *)table
->data
[slot
];
678 table
->data
[slot
] = (uintptr_t)newVal
;
679 table
->destructors
[slot
] = destructor
;
686 #pragma mark Windows Wide to UTF8 and UTF8 to Wide
688 #if DEPLOYMENT_TARGET_WINDOWS
689 /* 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. */
691 #include <sys/stat.h>
694 // 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.
695 static wchar_t *createWideFileSystemRepresentation(const char *str
, CFIndex
*resultLen
) {
696 // Get the real length of the string in UTF16 characters
697 CFStringRef cfStr
= CFStringCreateWithCString(kCFAllocatorSystemDefault
, str
, kCFStringEncodingUTF8
);
698 CFIndex strLen
= CFStringGetLength(cfStr
);
700 // Allocate a wide buffer to hold the converted string, including space for a NULL terminator
701 wchar_t *wideBuf
= (wchar_t *)malloc((strLen
+ 1) * sizeof(wchar_t));
703 // Copy the string into the buffer and terminate
704 CFStringGetCharacters(cfStr
, CFRangeMake(0, strLen
), (UniChar
*)wideBuf
);
708 if (resultLen
) *resultLen
= strLen
;
712 // Copies a UTF16 buffer into a supplied UTF8 buffer.
713 static void copyToNarrowFileSystemRepresentation(const wchar_t *wide
, CFIndex dstBufSize
, char *dstbuf
) {
714 // Get the real length of the wide string in UTF8 characters
715 CFStringRef cfStr
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)wide
, wcslen(wide
));
716 CFIndex strLen
= CFStringGetLength(cfStr
);
719 // Copy the wide string into the buffer and terminate
720 CFStringGetBytes(cfStr
, CFRangeMake(0, strLen
), kCFStringEncodingUTF8
, 0, false, (uint8_t *)dstbuf
, dstBufSize
, &bytesUsed
);
721 dstbuf
[bytesUsed
] = 0;
726 CF_EXPORT
int _NS_stat(const char *name
, struct _stat
*st
) {
727 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
728 int res
= _wstat(wide
, st
);
733 CF_EXPORT
int _NS_mkdir(const char *name
) {
734 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
735 int res
= _wmkdir(wide
);
740 CF_EXPORT
int _NS_rmdir(const char *name
) {
741 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
742 int res
= _wrmdir(wide
);
747 CF_EXPORT
int _NS_chmod(const char *name
, int mode
) {
748 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
752 if (mode
| 0400) newMode
|= _S_IREAD
;
753 if (mode
| 0200) newMode
|= _S_IWRITE
;
754 if (mode
| 0100) newMode
|= _S_IEXEC
;
756 int res
= _wchmod(wide
, newMode
);
761 CF_EXPORT
int _NS_unlink(const char *name
) {
762 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
763 int res
= _wunlink(wide
);
768 // Warning: this doesn't support dstbuf as null even though 'getcwd' does
769 CF_EXPORT
char *_NS_getcwd(char *dstbuf
, size_t size
) {
771 CFLog(kCFLogLevelWarning
, CFSTR("CFPlatform: getcwd called with null buffer"));
775 wchar_t *buf
= _wgetcwd(NULL
, 0);
780 // Convert result to UTF8
781 copyToNarrowFileSystemRepresentation(buf
, (CFIndex
)size
, dstbuf
);
786 CF_EXPORT
char *_NS_getenv(const char *name
) {
788 // We have to be careful what happens here, because getenv is called during cf initialization, and things like cfstring may not be working yet
792 CF_EXPORT
int _NS_rename(const char *oldName
, const char *newName
) {
793 wchar_t *oldWide
= createWideFileSystemRepresentation(oldName
, NULL
);
794 wchar_t *newWide
= createWideFileSystemRepresentation(newName
, NULL
);
795 // _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
796 // To simulate the Mac OS behavior, we use the Win32 API then fill out errno if something goes wrong
797 BOOL winRes
= MoveFileExW(oldWide
, newWide
, MOVEFILE_REPLACE_EXISTING
);
798 DWORD error
= GetLastError();
804 case ERROR_FILE_NOT_FOUND
:
805 case ERROR_PATH_NOT_FOUND
:
806 case ERROR_OPEN_FAILED
:
809 case ERROR_ACCESS_DENIED
:
818 return (winRes
? 0 : -1);
821 CF_EXPORT
int _NS_open(const char *name
, int oflag
, int pmode
) {
822 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
824 _wsopen_s(&fd
, wide
, oflag
, _SH_DENYNO
, _S_IREAD
| _S_IWRITE
);
829 CF_EXPORT
int _NS_chdir(const char *name
) {
830 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
831 int res
= _wchdir(wide
);
836 CF_EXPORT
int _NS_access(const char *name
, int amode
) {
837 // execute is always true
838 if (amode
== 1) return 0;
840 wchar_t *wide
= createWideFileSystemRepresentation(name
, NULL
);
841 // we only care about the read-only (04) and write-only (02) bits, so mask octal 06
842 int res
= _waccess(wide
, amode
& 06);
847 // This is a bit different than the standard 'mkstemp', because the size parameter is needed so we know the size of the UTF8 buffer
848 // Also, we don't avoid the race between creating a temporary file name and opening it on Windows like we do on Mac
849 CF_EXPORT
int _NS_mkstemp(char *name
, int bufSize
) {
851 wchar_t *wide
= createWideFileSystemRepresentation(name
, &nameLen
);
853 // 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.
854 // Look for the last '\' in the path
855 wchar_t *lastSlash
= wcsrchr(wide
, '\\');
861 // Set the last slash to NULL temporarily and use it for _wstat
863 struct _stat dirInfo
;
864 int res
= _wstat(wide
, &dirInfo
);
866 if (errno
== ENOENT
) {
872 // Restore the last slash
875 errno_t err
= _wmktemp_s(wide
, nameLen
+ 1);
882 _wsopen_s(&fd
, wide
, _O_RDWR
| _O_CREAT
| CF_OPENFLGS
, _SH_DENYNO
, _S_IREAD
| _S_IWRITE
);
884 // Convert the wide name back into the UTF8 buffer the caller supplied
885 copyToNarrowFileSystemRepresentation(wide
, bufSize
, name
);
891 // Utilities to convert from a volume name to a drive letter
893 Boolean
_isAFloppy(char driveLetter
)
897 Boolean retval
= false;
900 if (driveLetter
>= 'a' && driveLetter
<= 'z') {
901 driveLetter
= driveLetter
- 'a' + 'A';
904 if ((driveLetter
< 'A') || (driveLetter
> 'Z')) {
905 // invalid driveLetter; I guess it's not a floppy...
909 iDrive
= driveLetter
- 'A' + 1;
911 // On Windows NT, use the technique described in the Knowledge Base article Q115828 and in the "FLOPPY" SDK sample.
912 wsprintf(tsz
, TEXT("\\\\.\\%c:"), TEXT('@') + iDrive
);
913 h
= CreateFile(tsz
, 0, FILE_SHARE_WRITE
, 0, OPEN_EXISTING
, 0, 0);
914 if (h
!= INVALID_HANDLE_VALUE
)
916 DISK_GEOMETRY Geom
[20];
919 if (DeviceIoControl (h
, IOCTL_DISK_GET_MEDIA_TYPES
, 0, 0,
920 Geom
, sizeof(Geom
), &cb
, 0)
923 switch (Geom
[0].MediaType
)
925 case F5_1Pt2_512
: // 5.25 1.2MB floppy
926 case F5_360_512
: // 5.25 360K floppy
927 case F5_320_512
: // 5.25 320K floppy
928 case F5_320_1024
: // 5.25 320K floppy
929 case F5_180_512
: // 5.25 180K floppy
930 case F5_160_512
: // 5.25 160K floppy
931 case F3_1Pt44_512
: // 3.5 1.44MB floppy
932 case F3_2Pt88_512
: // 3.5 2.88MB floppy
933 case F3_20Pt8_512
: // 3.5 20.8MB floppy
934 case F3_720_512
: // 3.5 720K floppy
947 extern CFStringRef
CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr
) {
948 if (!volNameStr
) return NULL
;
950 // This code is designed to match as closely as possible code from QuickTime's library
951 CFIndex strLen
= CFStringGetLength(volNameStr
);
958 wchar_t *driveNames
= NULL
;
960 // Get the size of the buffer to store the list of drives
961 length
= GetLogicalDriveStringsW(0, 0);
966 driveNames
= (wchar_t *)malloc((length
+ 1) * sizeof(wchar_t));
967 result
= GetLogicalDriveStringsW(length
, driveNames
);
969 if (!result
|| result
> length
) {
974 // Get the volume name string into a wide buffer
975 wchar_t *theVolumeName
= (wchar_t *)malloc((strLen
+ 1) * sizeof(wchar_t));
976 CFStringGetCharacters(volNameStr
, CFRangeMake(0, strLen
), (UniChar
*)theVolumeName
);
977 theVolumeName
[strLen
] = 0;
979 // lowercase volume name
980 _wcslwr(theVolumeName
);
982 // Iterate through the drive names, looking for something that matches
983 wchar_t *drivePtr
= driveNames
;
984 CFStringRef drivePathResult
= NULL
;
989 if (!_isAFloppy((char)*drivePtr
)) {
991 DWORD whoCares1
, whoCares2
;
992 BOOL getVolInfoSucceeded
;
993 UniChar thisVolumeName
[MAX_PATH
];
995 // Convert this drive string into a volume name
996 oldErrorMode
= SetErrorMode(SEM_FAILCRITICALERRORS
);
997 getVolInfoSucceeded
= GetVolumeInformationW(drivePtr
, (LPWSTR
)thisVolumeName
, sizeof(thisVolumeName
), NULL
, &whoCares1
, &whoCares2
, NULL
, 0);
998 SetErrorMode(oldErrorMode
);
1000 if (getVolInfoSucceeded
) {
1001 _wcslwr((wchar_t *)thisVolumeName
);
1003 // If the volume corresponding to this drive matches the input volume
1004 // then this drive is the winner.
1005 if (!wcscmp((const wchar_t *)thisVolumeName
, theVolumeName
) ||
1006 (*thisVolumeName
== 0x00 && (CFStringCompare(volNameStr
, CFSTR("NONAME"), 0) == kCFCompareEqualTo
))) {
1007 drivePathResult
= CFStringCreateWithCharacters(kCFAllocatorSystemDefault
, (const UniChar
*)drivePtr
, wcslen(drivePtr
));
1013 drivePtr
+= wcslen(drivePtr
) + 1;
1018 free(theVolumeName
);
1019 return drivePathResult
;
1023 int tz_minuteswest
; /* minutes west of Greenwich */
1024 int tz_dsttime
; /* type of dst correction */
1027 CF_PRIVATE
int _NS_gettimeofday(struct timeval
*tv
, struct timezone
*tz
) {
1030 GetSystemTimeAsFileTime(&ft
);
1031 unsigned __int64 t
= 0;
1032 t
|= ft
.dwHighDateTime
;
1034 t
|= ft
.dwLowDateTime
;
1036 // Convert to microseconds
1039 // Difference between 1/1/1970 and 1/1/1601
1040 t
-= 11644473600000000Ui
64;
1042 // Convert microseconds to seconds
1043 tv
->tv_sec
= (long)(t
/ 1000000UL);
1044 tv
->tv_usec
= (long)(t
% 1000000UL);
1047 // We don't support tz
1051 #endif // DEPLOYMENT_TARGET_WINDOWS
1054 #pragma mark Linux OSAtomic
1056 #if DEPLOYMENT_TARGET_LINUX
1058 bool OSAtomicCompareAndSwapPtr(void *oldp
, void *newp
, void *volatile *dst
)
1060 return __sync_bool_compare_and_swap(dst
, oldp
, newp
);
1063 bool OSAtomicCompareAndSwapLong(long oldl
, long newl
, long volatile *dst
)
1065 return __sync_val_compare_and_swap(dst
, oldl
, newl
);
1068 bool OSAtomicCompareAndSwapPtrBarrier(void *oldp
, void *newp
, void *volatile *dst
)
1070 return __sync_bool_compare_and_swap(dst
, oldp
, newp
);
1073 int32_t OSAtomicAdd32Barrier( int32_t theAmount
, volatile int32_t *theValue
) {
1074 return __sync_fetch_and_add(theValue
, theAmount
) + theAmount
;
1077 bool OSAtomicCompareAndSwap32Barrier(int32_t oldValue
, int32_t newValue
, volatile int32_t *theValue
) {
1078 return __sync_bool_compare_and_swap(theValue
, oldValue
, newValue
);
1081 bool OSAtomicCompareAndSwap64Barrier(int64_t oldValue
, int64_t newValue
, volatile int64_t *theValue
) {
1082 return __sync_bool_compare_and_swap(theValue
, oldValue
, newValue
);
1085 int32_t OSAtomicDecrement32Barrier(volatile int32_t *dst
)
1087 return OSAtomicAdd32Barrier(-1, dst
);
1090 int32_t OSAtomicIncrement32Barrier(volatile int32_t *dst
)
1092 return OSAtomicAdd32Barrier(1, dst
);
1095 int32_t OSAtomicAdd32( int32_t theAmount
, volatile int32_t *theValue
) {
1096 return OSAtomicAdd32Barrier(theAmount
, theValue
);
1099 int32_t OSAtomicIncrement32(volatile int32_t *theValue
) {
1100 return OSAtomicIncrement32Barrier(theValue
);
1103 int32_t OSAtomicDecrement32(volatile int32_t *theValue
) {
1104 return OSAtomicDecrement32Barrier(theValue
);
1107 void OSMemoryBarrier() {
1108 __sync_synchronize();
1111 #include <Block_private.h>
1113 void dispatch_once(dispatch_once_t
*predicate
, dispatch_block_t block
) {
1114 struct Block_layout
*layout
= (struct Block_layout
*)block
;
1115 pthread_once(predicate
, (void (*)(void))layout
->invoke
);
1118 #endif // DEPLOYMENT_TARGET_LINUX
1121 #pragma mark Windows and Linux Helpers
1123 #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
1127 CF_PRIVATE
int asprintf(char **ret
, const char *format
, ...) {
1130 *ret
= (char *) malloc(sz
* sizeof(char));
1131 if (!*ret
) return -1;
1132 va_start(args
, format
);
1133 int cnt
= vsnprintf(*ret
, sz
, format
, args
);
1135 if (cnt
< sz
- 1) return cnt
;
1137 char *oldret
= *ret
;
1138 *ret
= (char *) realloc(*ret
, sz
* sizeof(char));
1139 if (!*ret
&& oldret
) free(oldret
);
1140 if (!*ret
) return -1;
1141 va_start(args
, format
);
1142 cnt
= vsnprintf(*ret
, sz
, format
, args
);
1144 if (cnt
< sz
- 1) return cnt
;