]> git.saurik.com Git - apple/cf.git/blob - CFPlatform.c
CF-1153.18.tar.gz
[apple/cf.git] / CFPlatform.c
1 /*
2 * Copyright (c) 2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFPlatform.c
25 Copyright (c) 1999-2014, Apple Inc. All rights reserved.
26 Responsibility: Tony Parker
27 */
28
29 #include "CFInternal.h"
30 #include <CoreFoundation/CFPriv.h>
31 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
32 #include <stdlib.h>
33 #include <sys/stat.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <pwd.h>
38 #include <crt_externs.h>
39 #include <mach-o/dyld.h>
40 #include <pthread/tsd_private.h>
41 #endif
42
43 #if DEPLOYMENT_TARGET_WINDOWS
44 #include <shellapi.h>
45 #include <shlobj.h>
46 #include <WinIoCtl.h>
47
48 #define getcwd _NS_getcwd
49
50 #endif
51
52 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
53 #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
54 #else
55 #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
56 #endif
57
58 extern void __CFGetUGIDs(uid_t *euid, gid_t *egid);
59
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(); }
64 #endif
65
66
67 CF_PRIVATE Boolean _CFGetCurrentDirectory(char *path, int maxlen) {
68 return getcwd(path, maxlen) != NULL;
69 }
70
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];
76
77 if (!bDllPathCached) {
78 #ifdef _DEBUG
79 // might be nice to get this from the project file at some point
80 wchar_t *DLLFileName = L"CoreFoundation_debug.dll";
81 #else
82 wchar_t *DLLFileName = L"CoreFoundation.dll";
83 #endif
84 HMODULE ourModule = GetModuleHandleW(DLLFileName);
85
86 CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed");
87
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);
91
92 // strip off last component, the DLL name
93 CFIndex idx;
94 for (idx = wResult - 1; idx; idx--) {
95 if ('\\' == cachedPath[idx]) {
96 cachedPath[idx] = '\0';
97 break;
98 }
99 }
100 bDllPathCached = true;
101 }
102 return cachedPath;
103 }
104 #endif
105
106 static const char *__CFProcessPath = NULL;
107 static const char *__CFprogname = NULL;
108
109 const char **_CFGetProgname(void) {
110 if (!__CFprogname)
111 _CFProcessPath(); // sets up __CFprogname as a side-effect
112 return &__CFprogname;
113 }
114
115 const char **_CFGetProcessPath(void) {
116 if (!__CFProcessPath)
117 _CFProcessPath(); // sets up __CFProcessPath as a side-effect
118 return &__CFProcessPath;
119 }
120
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]));
126 if (0 < rlen) {
127 char asciiBuf[CFMaxPathSize] = {0};
128 int res = WideCharToMultiByte(CP_UTF8, 0, buf, rlen, asciiBuf, sizeof(asciiBuf) / sizeof(asciiBuf[0]), NULL, NULL);
129 if (0 < res) {
130 __CFProcessPath = strdup(asciiBuf);
131 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
132 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
133 }
134 }
135 if (!__CFProcessPath) {
136 __CFProcessPath = "";
137 __CFprogname = __CFProcessPath;
138 }
139 return __CFProcessPath;
140 }
141 #endif
142
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
147 if (!issetugid()) {
148 const char *path = (char *)__CFgetenv("CFProcessPath");
149 if (path) {
150 __CFProcessPath = strdup(path);
151 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
152 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
153 return __CFProcessPath;
154 }
155 }
156 #endif
157 uint32_t size = CFMaxPathSize;
158 char buffer[size];
159 if (0 == _NSGetExecutablePath(buffer, &size)) {
160 __CFProcessPath = strdup(buffer);
161 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
162 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
163 }
164 if (!__CFProcessPath) {
165 __CFProcessPath = "";
166 __CFprogname = __CFProcessPath;
167 }
168 return __CFProcessPath;
169 }
170 #endif
171
172 #if DEPLOYMENT_TARGET_LINUX
173 #include <unistd.h>
174
175 const char *_CFProcessPath(void) {
176 if (__CFProcessPath) return __CFProcessPath;
177 char buf[CFMaxPathSize + 1];
178
179 ssize_t res = readlink("/proc/self/exe", buf, CFMaxPathSize);
180 if (res > 0) {
181 // null terminate, readlink does not
182 buf[res] = 0;
183 __CFProcessPath = strdup(buf);
184 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
185 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
186 } else {
187 __CFProcessPath = "";
188 __CFprogname = __CFProcessPath;
189 }
190 return __CFProcessPath;
191 }
192 #endif
193
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.
202 }
203 }
204 return __CFProcessNameString;
205 }
206
207
208 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
209
210 #include <pwd.h>
211 #include <sys/param.h>
212
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;
217
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);
225
226 return home;
227 }
228
229 #endif
230
231
232 #define CFMaxHostNameLength 256
233 #define CFMaxHostNameSize (CFMaxHostNameLength+1)
234
235 CF_PRIVATE CFStringRef _CFStringCreateHostName(void) {
236 char myName[CFMaxHostNameSize];
237
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);
241 }
242
243 /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
244 These can return NULL.
245 */
246 CF_EXPORT CFStringRef CFGetUserName(void) {
247 return CFCopyUserName();
248 }
249
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
253 uid_t euid;
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);
258 } else {
259 const char *cuser = __CFgetenv("USER");
260 if (cuser) {
261 result = CFStringCreateWithCString(kCFAllocatorSystemDefault, cuser, kCFPlatformInterfaceStringEncoding);
262 }
263 }
264 #elif DEPLOYMENT_TARGET_WINDOWS
265 wchar_t username[1040];
266 DWORD size = 1040;
267 username[0] = 0;
268 if (GetUserNameW(username, &size)) {
269 // discount the extra NULL by decrementing the size
270 result = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)username, size - 1);
271 } else {
272 const char *cname = __CFgetenv("USERNAME");
273 if (cname) {
274 result = CFStringCreateWithCString(kCFAllocatorSystemDefault, cname, kCFPlatformInterfaceStringEncoding);
275 }
276 }
277 #else
278 #error Dont know how to compute user name on this platform
279 #endif
280 if (!result)
281 result = (CFStringRef)CFRetain(CFSTR(""));
282 return result;
283 }
284
285 CFURLRef CFCopyHomeDirectoryURL(void) {
286 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
287 uid_t euid;
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;
293 CFIndex len = 0;
294 CFStringRef str = NULL;
295
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);
301 CFRelease(str);
302 }
303
304 if (!retVal) {
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);
314 CFRelease(str);
315 }
316 }
317
318 if (!retVal) {
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);
322 if (dwChars > 0) {
323 len = (CFIndex)wcslen((wchar_t *)currDir);
324 str = CFStringCreateWithCharacters(kCFAllocatorDefault, currDir, len);
325 retVal = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true);
326 CFRelease(str);
327 }
328 }
329
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) {
333 CFRelease(retVal);
334 retVal = NULL;
335 }
336 if (testPath) CFRelease(testPath);
337
338 return retVal;
339 #else
340 #error Dont know how to compute users home directories on this platform
341 #endif
342 }
343
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
346 if (!uName) {
347 uid_t euid;
348 __CFGetUGIDs(&euid, NULL);
349 struct passwd *upwd = getpwuid(euid ? euid : getuid());
350 return _CFCopyHomeDirURLForUser(upwd, true);
351 } else {
352 struct passwd *upwd = NULL;
353 char buf[128], *user;
354 SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding);
355 CFIndex usedSize;
356 if (size < 127) {
357 user = buf;
358 } else {
359 user = CFAllocatorAllocate(kCFAllocatorSystemDefault, size+1, 0);
360 }
361 if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, (uint8_t *)user, size, &usedSize) == len) {
362 user[usedSize] = '\0';
363 upwd = getpwnam(user);
364 }
365 if (buf != user) {
366 CFAllocatorDeallocate(kCFAllocatorSystemDefault, user);
367 }
368 return _CFCopyHomeDirURLForUser(upwd, false);
369 }
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);
376 return NULL;
377 }
378 if (userName) CFRelease(userName);
379 return CFCopyHomeDirectoryURL();
380 #else
381 #error Dont know how to compute users home directories on this platform
382 #endif
383 }
384
385
386 #undef CFMaxHostNameLength
387 #undef CFMaxHostNameSize
388
389 #if DEPLOYMENT_TARGET_WINDOWS
390 CF_INLINE CFIndex strlen_UniChar(const UniChar* p) {
391 CFIndex result = 0;
392 while ((*p++) != 0)
393 ++result;
394 return result;
395 }
396
397 //#include <shfolder.h>
398 /*
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).
406 *
407 * The CFMutableStringRef result must be released by the caller.
408 *
409 * If anything fails along the way, the result will be NULL.
410 */
411 CF_EXPORT CFMutableStringRef _CFCreateApplicationRepositoryPath(CFAllocatorRef alloc, int nFolder) {
412 CFMutableStringRef result = NULL;
413 UniChar szPath[MAX_PATH];
414
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;
418
419 // make it a CFString
420 directoryPath = CFStringCreateWithCharacters(alloc, szPath, strlen_UniChar(szPath));
421 if (directoryPath) {
422 CFBundleRef bundle;
423 CFStringRef bundleName;
424 CFStringRef completePath;
425
426 // attempt to get the bundle name
427 bundle = CFBundleGetMainBundle();
428 if (bundle) {
429 bundleName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey);
430 }
431 else {
432 bundleName = NULL;
433 }
434
435 if (bundleName) {
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);
438 }
439 else {
440 // or "<directoryPath>\Apple Computer\" if there is no bundle name.
441 completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\"), directoryPath);
442 }
443
444 CFRelease(directoryPath);
445
446 // make a mutable copy to return
447 if (completePath) {
448 result = CFStringCreateMutableCopy(alloc, 0, completePath);
449 CFRelease(completePath);
450 }
451 }
452 }
453
454 return ( result );
455 }
456 #endif
457
458 #pragma mark -
459 #pragma mark Thread Functions
460
461 #if DEPLOYMENT_TARGET_WINDOWS
462
463 // This code from here:
464 // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
465
466 const DWORD MS_VC_EXCEPTION=0x406D1388;
467 #pragma pack(push,8)
468 typedef struct tagTHREADNAME_INFO
469 {
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.
474 } THREADNAME_INFO;
475 #pragma pack(pop)
476
477 CF_EXPORT void _NS_pthread_setname_np(const char *name) {
478 THREADNAME_INFO info;
479 info.dwType = 0x1000;
480 info.szName = name;
481 info.dwThreadID = GetCurrentThreadId();
482 info.dwFlags = 0;
483
484 __try
485 {
486 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
487 }
488 __except(EXCEPTION_EXECUTE_HANDLER)
489 {
490 }
491 }
492
493 static pthread_t __initialPthread = { NULL, 0 };
494
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;
500 }
501 return (pthread_equal(__initialPthread, me));
502 }
503
504 #endif
505
506 #pragma mark -
507 #pragma mark Thread Local Data
508
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
512
513 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
514 static const unsigned long CF_TSD_KEY = __PTK_FRAMEWORK_COREFOUNDATION_KEY5;
515 #endif
516
517 // Windows and Linux, not sure how many times the destructor could get called; CF_TSD_MAX_DESTRUCTOR_CALLS could be 1
518
519 #define CF_TSD_BAD_PTR ((void *)0x1000)
520
521 typedef void (*tsdDestructor)(void *);
522
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];
528 } __CFTSDTable;
529
530 static void __CFTSDFinalize(void *arg);
531
532 #if DEPLOYMENT_TARGET_WINDOWS
533
534 static DWORD __CFTSDIndexKey = 0xFFFFFFFF;
535
536 // Called from CFRuntime's startup code, on Windows only
537 CF_PRIVATE void __CFTSDWindowsInitialize() {
538 __CFTSDIndexKey = TlsAlloc();
539 }
540
541 // Called from CFRuntime's cleanup code, on Windows only
542 CF_PRIVATE void __CFTSDWindowsCleanup() {
543 TlsFree(__CFTSDIndexKey);
544 }
545
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));
554 }
555
556 #endif
557
558 #if DEPLOYMENT_TARGET_LINUX
559
560 static pthread_key_t __CFTSDIndexKey;
561
562 // Called from CFRuntime's startup code, on Linux only
563 CF_PRIVATE void __CFTSDLinuxInitialize() {
564 (void)pthread_key_create(&__CFTSDIndexKey, __CFTSDFinalize);
565 }
566
567 #endif
568
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);
576 #endif
577 }
578
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);
586 #endif
587 }
588
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);
592
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.
595 return;
596 }
597
598 __CFTSDTable *table = (__CFTSDTable *)arg;
599 table->destructorCount++;
600
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));
609 }
610 }
611
612 if (table->destructorCount == PTHREAD_DESTRUCTOR_ITERATIONS - 1) { // On PTHREAD_DESTRUCTOR_ITERATIONS-1 call, destroy our data
613 free(table);
614
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);
617 return;
618 }
619 }
620
621 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
622 extern int pthread_key_init_np(int, void (*)(void *));
623 #endif
624
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) {
630 return NULL;
631 }
632 // Create table on demand
633 if (!table) {
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);
639 #endif
640 __CFTSDSetSpecific(table);
641 }
642
643 return table;
644 }
645
646
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);
651 HALT;
652 }
653 __CFTSDTable *table = __CFTSDGetTable();
654 if (!table) {
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);
657 return NULL;
658 }
659 uintptr_t *slots = (uintptr_t *)(table->data);
660 return (void *)slots[slot];
661 }
662
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);
667 HALT;
668 }
669 __CFTSDTable *table = __CFTSDGetTable();
670 if (!table) {
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);
673 return NULL;
674 }
675
676 void *oldVal = (void *)table->data[slot];
677
678 table->data[slot] = (uintptr_t)newVal;
679 table->destructors[slot] = destructor;
680
681 return oldVal;
682 }
683
684
685 #pragma mark -
686 #pragma mark Windows Wide to UTF8 and UTF8 to Wide
687
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. */
690
691 #include <sys/stat.h>
692 #include <share.h>
693
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);
699
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));
702
703 // Copy the string into the buffer and terminate
704 CFStringGetCharacters(cfStr, CFRangeMake(0, strLen), (UniChar *)wideBuf);
705 wideBuf[strLen] = 0;
706
707 CFRelease(cfStr);
708 if (resultLen) *resultLen = strLen;
709 return wideBuf;
710 }
711
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);
717 CFIndex bytesUsed;
718
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;
722
723 CFRelease(cfStr);
724 }
725
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);
729 free(wide);
730 return res;
731 }
732
733 CF_EXPORT int _NS_mkdir(const char *name) {
734 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
735 int res = _wmkdir(wide);
736 free(wide);
737 return res;
738 }
739
740 CF_EXPORT int _NS_rmdir(const char *name) {
741 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
742 int res = _wrmdir(wide);
743 free(wide);
744 return res;
745 }
746
747 CF_EXPORT int _NS_chmod(const char *name, int mode) {
748 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
749
750 // Convert mode
751 int newMode = 0;
752 if (mode | 0400) newMode |= _S_IREAD;
753 if (mode | 0200) newMode |= _S_IWRITE;
754 if (mode | 0100) newMode |= _S_IEXEC;
755
756 int res = _wchmod(wide, newMode);
757 free(wide);
758 return res;
759 }
760
761 CF_EXPORT int _NS_unlink(const char *name) {
762 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
763 int res = _wunlink(wide);
764 free(wide);
765 return res;
766 }
767
768 // Warning: this doesn't support dstbuf as null even though 'getcwd' does
769 CF_EXPORT char *_NS_getcwd(char *dstbuf, size_t size) {
770 if (!dstbuf) {
771 CFLog(kCFLogLevelWarning, CFSTR("CFPlatform: getcwd called with null buffer"));
772 return 0;
773 }
774
775 wchar_t *buf = _wgetcwd(NULL, 0);
776 if (!buf) {
777 return NULL;
778 }
779
780 // Convert result to UTF8
781 copyToNarrowFileSystemRepresentation(buf, (CFIndex)size, dstbuf);
782 free(buf);
783 return dstbuf;
784 }
785
786 CF_EXPORT char *_NS_getenv(const char *name) {
787 // todo: wide getenv
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
789 return getenv(name);
790 }
791
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();
799 if (!winRes) {
800 switch (error) {
801 case ERROR_SUCCESS:
802 errno = 0;
803 break;
804 case ERROR_FILE_NOT_FOUND:
805 case ERROR_PATH_NOT_FOUND:
806 case ERROR_OPEN_FAILED:
807 errno = ENOENT;
808 break;
809 case ERROR_ACCESS_DENIED:
810 errno = EACCES;
811 break;
812 default:
813 errno = error;
814 }
815 }
816 free(oldWide);
817 free(newWide);
818 return (winRes ? 0 : -1);
819 }
820
821 CF_EXPORT int _NS_open(const char *name, int oflag, int pmode) {
822 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
823 int fd;
824 _wsopen_s(&fd, wide, oflag, _SH_DENYNO, _S_IREAD | _S_IWRITE);
825 free(wide);
826 return fd;
827 }
828
829 CF_EXPORT int _NS_chdir(const char *name) {
830 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
831 int res = _wchdir(wide);
832 free(wide);
833 return res;
834 }
835
836 CF_EXPORT int _NS_access(const char *name, int amode) {
837 // execute is always true
838 if (amode == 1) return 0;
839
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);
843 free(wide);
844 return res;
845 }
846
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) {
850 CFIndex nameLen;
851 wchar_t *wide = createWideFileSystemRepresentation(name, &nameLen);
852
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, '\\');
856 if (!lastSlash) {
857 free(wide);
858 return -1;
859 }
860
861 // Set the last slash to NULL temporarily and use it for _wstat
862 *lastSlash = 0;
863 struct _stat dirInfo;
864 int res = _wstat(wide, &dirInfo);
865 if (res < 0) {
866 if (errno == ENOENT) {
867 errno = ENOTDIR;
868 }
869 free(wide);
870 return -1;
871 }
872 // Restore the last slash
873 *lastSlash = '\\';
874
875 errno_t err = _wmktemp_s(wide, nameLen + 1);
876 if (err != 0) {
877 free(wide);
878 return 0;
879 }
880
881 int fd;
882 _wsopen_s(&fd, wide, _O_RDWR | _O_CREAT | CF_OPENFLGS, _SH_DENYNO, _S_IREAD | _S_IWRITE);
883
884 // Convert the wide name back into the UTF8 buffer the caller supplied
885 copyToNarrowFileSystemRepresentation(wide, bufSize, name);
886 free(wide);
887 return fd;
888 }
889
890
891 // Utilities to convert from a volume name to a drive letter
892
893 Boolean _isAFloppy(char driveLetter)
894 {
895 HANDLE h;
896 TCHAR tsz[8];
897 Boolean retval = false;
898 int iDrive;
899
900 if (driveLetter >= 'a' && driveLetter <= 'z') {
901 driveLetter = driveLetter - 'a' + 'A';
902 }
903
904 if ((driveLetter < 'A') || (driveLetter > 'Z')) {
905 // invalid driveLetter; I guess it's not a floppy...
906 return false;
907 }
908
909 iDrive = driveLetter - 'A' + 1;
910
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)
915 {
916 DISK_GEOMETRY Geom[20];
917 DWORD cb;
918
919 if (DeviceIoControl (h, IOCTL_DISK_GET_MEDIA_TYPES, 0, 0,
920 Geom, sizeof(Geom), &cb, 0)
921 && cb > 0)
922 {
923 switch (Geom[0].MediaType)
924 {
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
935 retval = true;
936 break;
937 }
938 }
939
940 CloseHandle(h);
941 }
942
943 return retval;
944 }
945
946
947 extern CFStringRef CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr) {
948 if (!volNameStr) return NULL;
949
950 // This code is designed to match as closely as possible code from QuickTime's library
951 CFIndex strLen = CFStringGetLength(volNameStr);
952 if (strLen == 0) {
953 return NULL;
954 }
955
956 // Get drive names
957 long length, result;
958 wchar_t *driveNames = NULL;
959
960 // Get the size of the buffer to store the list of drives
961 length = GetLogicalDriveStringsW(0, 0);
962 if (!length) {
963 return NULL;
964 }
965
966 driveNames = (wchar_t *)malloc((length + 1) * sizeof(wchar_t));
967 result = GetLogicalDriveStringsW(length, driveNames);
968
969 if (!result || result > length) {
970 free(driveNames);
971 return NULL;
972 }
973
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;
978
979 // lowercase volume name
980 _wcslwr(theVolumeName);
981
982 // Iterate through the drive names, looking for something that matches
983 wchar_t *drivePtr = driveNames;
984 CFStringRef drivePathResult = NULL;
985
986 while (*drivePtr) {
987 _wcslwr(drivePtr);
988
989 if (!_isAFloppy((char)*drivePtr)) {
990 UINT oldErrorMode;
991 DWORD whoCares1, whoCares2;
992 BOOL getVolInfoSucceeded;
993 UniChar thisVolumeName[MAX_PATH];
994
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);
999
1000 if (getVolInfoSucceeded) {
1001 _wcslwr((wchar_t *)thisVolumeName);
1002
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));
1008 break;
1009 }
1010 }
1011 }
1012
1013 drivePtr += wcslen(drivePtr) + 1;
1014 }
1015
1016
1017 free(driveNames);
1018 free(theVolumeName);
1019 return drivePathResult;
1020 }
1021
1022 struct timezone {
1023 int tz_minuteswest; /* minutes west of Greenwich */
1024 int tz_dsttime; /* type of dst correction */
1025 };
1026
1027 CF_PRIVATE int _NS_gettimeofday(struct timeval *tv, struct timezone *tz) {
1028 if (tv) {
1029 FILETIME ft;
1030 GetSystemTimeAsFileTime(&ft);
1031 unsigned __int64 t = 0;
1032 t |= ft.dwHighDateTime;
1033 t <<= 32;
1034 t |= ft.dwLowDateTime;
1035
1036 // Convert to microseconds
1037 t /= 10;
1038
1039 // Difference between 1/1/1970 and 1/1/1601
1040 t -= 11644473600000000Ui64;
1041
1042 // Convert microseconds to seconds
1043 tv->tv_sec = (long)(t / 1000000UL);
1044 tv->tv_usec = (long)(t % 1000000UL);
1045 }
1046
1047 // We don't support tz
1048 return 0;
1049 }
1050
1051 #endif // DEPLOYMENT_TARGET_WINDOWS
1052
1053 #pragma mark -
1054 #pragma mark Linux OSAtomic
1055
1056 #if DEPLOYMENT_TARGET_LINUX
1057
1058 bool OSAtomicCompareAndSwapPtr(void *oldp, void *newp, void *volatile *dst)
1059 {
1060 return __sync_bool_compare_and_swap(dst, oldp, newp);
1061 }
1062
1063 bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst)
1064 {
1065 return __sync_val_compare_and_swap(dst, oldl, newl);
1066 }
1067
1068 bool OSAtomicCompareAndSwapPtrBarrier(void *oldp, void *newp, void *volatile *dst)
1069 {
1070 return __sync_bool_compare_and_swap(dst, oldp, newp);
1071 }
1072
1073 int32_t OSAtomicAdd32Barrier( int32_t theAmount, volatile int32_t *theValue ) {
1074 return __sync_fetch_and_add(theValue, theAmount) + theAmount;
1075 }
1076
1077 bool OSAtomicCompareAndSwap32Barrier(int32_t oldValue, int32_t newValue, volatile int32_t *theValue) {
1078 return __sync_bool_compare_and_swap(theValue, oldValue, newValue);
1079 }
1080
1081 bool OSAtomicCompareAndSwap64Barrier(int64_t oldValue, int64_t newValue, volatile int64_t *theValue) {
1082 return __sync_bool_compare_and_swap(theValue, oldValue, newValue);
1083 }
1084
1085 int32_t OSAtomicDecrement32Barrier(volatile int32_t *dst)
1086 {
1087 return OSAtomicAdd32Barrier(-1, dst);
1088 }
1089
1090 int32_t OSAtomicIncrement32Barrier(volatile int32_t *dst)
1091 {
1092 return OSAtomicAdd32Barrier(1, dst);
1093 }
1094
1095 int32_t OSAtomicAdd32( int32_t theAmount, volatile int32_t *theValue ) {
1096 return OSAtomicAdd32Barrier(theAmount, theValue);
1097 }
1098
1099 int32_t OSAtomicIncrement32(volatile int32_t *theValue) {
1100 return OSAtomicIncrement32Barrier(theValue);
1101 }
1102
1103 int32_t OSAtomicDecrement32(volatile int32_t *theValue) {
1104 return OSAtomicDecrement32Barrier(theValue);
1105 }
1106
1107 void OSMemoryBarrier() {
1108 __sync_synchronize();
1109 }
1110
1111 #include <Block_private.h>
1112
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);
1116 }
1117
1118 #endif // DEPLOYMENT_TARGET_LINUX
1119
1120 #pragma mark -
1121 #pragma mark Windows and Linux Helpers
1122
1123 #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
1124
1125 #include <stdio.h>
1126
1127 CF_PRIVATE int asprintf(char **ret, const char *format, ...) {
1128 va_list args;
1129 size_t sz = 1024;
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);
1134 va_end(args);
1135 if (cnt < sz - 1) return cnt;
1136 sz = cnt + 8;
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);
1143 va_end(args);
1144 if (cnt < sz - 1) return cnt;
1145 free(*ret);
1146 *ret = NULL;
1147 return -1;
1148 }
1149
1150 #endif
1151