1/*
2 * Copyright (c) 2014 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-2013, 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
58extern 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
62char **_CFArgv(void) { return *_NSGetArgv(); }
63int _CFArgc(void) { return *_NSGetArgc(); }
64#endif
65
66
67CF_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
73bool bDllPathCached = false;
74CF_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
106static const char *__CFProcessPath = NULL;
107static const char *__CFprogname = NULL;
108
109const char **_CFGetProgname(void) {
110    if (!__CFprogname)
111        _CFProcessPath();		// sets up __CFprogname as a side-effect
112    return &__CFprogname;
113}
114
115const 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
122const 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
144const 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
175const 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
194CF_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.
214static 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
235CF_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*/
246CF_EXPORT CFStringRef CFGetUserName(void) {
247    return CFCopyUserName();
248}
249
250CF_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
285CFURLRef 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
344CF_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
390CF_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 */
411CF_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
466const DWORD MS_VC_EXCEPTION=0x406D1388;
467#pragma pack(push,8)
468typedef 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
477CF_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
493static pthread_t __initialPthread = { NULL, 0 };
494
495CF_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
514static 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
521typedef void (*tsdDestructor)(void *);
522
523// Data structure to hold TSD data, cleanup functions for each
524typedef struct __CFTSDTable {
525    uint32_t destructorCount;
526    uintptr_t data[CF_TSD_MAX_SLOTS];
527    tsdDestructor destructors[CF_TSD_MAX_SLOTS];
528} __CFTSDTable;
529
530static void __CFTSDFinalize(void *arg);
531
532#if DEPLOYMENT_TARGET_WINDOWS
533
534static DWORD __CFTSDIndexKey = 0xFFFFFFFF;
535
536// Called from CFRuntime's startup code, on Windows only
537CF_PRIVATE void __CFTSDWindowsInitialize() {
538    __CFTSDIndexKey = TlsAlloc();
539}
540
541// Called from CFRuntime's cleanup code, on Windows only
542CF_PRIVATE void __CFTSDWindowsCleanup() {
543    TlsFree(__CFTSDIndexKey);
544}
545
546// Called for each thread as it exits, on Windows only
547CF_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
560static pthread_key_t __CFTSDIndexKey;
561
562// Called from CFRuntime's startup code, on Linux only
563CF_PRIVATE void __CFTSDLinuxInitialize() {
564    (void)pthread_key_create(&__CFTSDIndexKey, __CFTSDFinalize);
565}
566
567#endif
568
569static 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
579static 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
589static 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
622extern int pthread_key_init_np(int, void (*)(void *));
623#endif
624
625// Get or initialize a thread local storage. It is created on demand.
626static __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
648CF_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
664CF_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.
695static 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.
713static 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
726CF_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
733CF_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
740CF_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
747CF_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
761CF_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
769CF_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
786CF_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
792CF_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
821CF_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
829CF_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
836CF_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
849CF_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
893Boolean _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
947extern 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
1022struct timezone {
1023    int	tz_minuteswest;	/* minutes west of Greenwich */
1024    int	tz_dsttime;	/* type of dst correction */
1025};
1026
1027CF_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
1058bool OSAtomicCompareAndSwapPtr(void *oldp, void *newp, void *volatile *dst)
1059{
1060    return __sync_bool_compare_and_swap(dst, oldp, newp);
1061}
1062
1063bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst)
1064{
1065    return __sync_val_compare_and_swap(dst, oldl, newl);
1066}
1067
1068bool OSAtomicCompareAndSwapPtrBarrier(void *oldp, void *newp, void *volatile *dst)
1069{
1070    return __sync_bool_compare_and_swap(dst, oldp, newp);
1071}
1072
1073int32_t OSAtomicAdd32Barrier( int32_t theAmount, volatile int32_t *theValue ) {
1074    return __sync_fetch_and_add(theValue, theAmount) + theAmount;
1075}
1076
1077bool OSAtomicCompareAndSwap32Barrier(int32_t oldValue, int32_t newValue, volatile int32_t *theValue) {
1078    return __sync_bool_compare_and_swap(theValue, oldValue, newValue);
1079}
1080
1081bool OSAtomicCompareAndSwap64Barrier(int64_t oldValue, int64_t newValue, volatile int64_t *theValue) {
1082    return __sync_bool_compare_and_swap(theValue, oldValue, newValue);
1083}
1084
1085int32_t OSAtomicDecrement32Barrier(volatile int32_t *dst)
1086{
1087    return OSAtomicAdd32Barrier(-1, dst);
1088}
1089
1090int32_t OSAtomicIncrement32Barrier(volatile int32_t *dst)
1091{
1092    return OSAtomicAdd32Barrier(1, dst);
1093}
1094
1095int32_t OSAtomicAdd32( int32_t theAmount, volatile int32_t *theValue ) {
1096    return OSAtomicAdd32Barrier(theAmount, theValue);
1097}
1098
1099int32_t OSAtomicIncrement32(volatile int32_t *theValue) {
1100    return OSAtomicIncrement32Barrier(theValue);
1101}
1102
1103int32_t OSAtomicDecrement32(volatile int32_t *theValue) {
1104    return OSAtomicDecrement32Barrier(theValue);
1105}
1106
1107void OSMemoryBarrier() {
1108    __sync_synchronize();
1109}
1110
1111#include <Block_private.h>
1112
1113void 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
1127CF_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
1152