]> git.saurik.com Git - apple/cf.git/blob - CFPlatform.c
CF-744.12.tar.gz
[apple/cf.git] / CFPlatform.c
1 /*
2 * Copyright (c) 2012 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-2012, 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 #endif
41
42 #if DEPLOYMENT_TARGET_WINDOWS
43 #include <shellapi.h>
44 #include <shlobj.h>
45 #include <WinIoCtl.h>
46
47 #define getcwd _NS_getcwd
48
49 #endif
50
51 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS
52 #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
53 #else
54 #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
55 #endif
56
57 extern void __CFGetUGIDs(uid_t *euid, gid_t *egid);
58
59 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
60 // CoreGraphics and LaunchServices are only projects (1 Dec 2006) that use these
61 char **_CFArgv(void) { return *_NSGetArgv(); }
62 int _CFArgc(void) { return *_NSGetArgc(); }
63 #endif
64
65
66 __private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) {
67 return getcwd(path, maxlen) != NULL;
68 }
69
70 #if DEPLOYMENT_TARGET_WINDOWS
71 // Returns the path to the CF DLL, which we can then use to find resources like char sets
72 bool bDllPathCached = false;
73 __private_extern__ const wchar_t *_CFDLLPath(void) {
74 static wchar_t cachedPath[MAX_PATH+1];
75
76 if (!bDllPathCached) {
77 #ifdef _DEBUG
78 // might be nice to get this from the project file at some point
79 wchar_t *DLLFileName = L"CoreFoundation_debug.dll";
80 #else
81 wchar_t *DLLFileName = L"CoreFoundation.dll";
82 #endif
83 HMODULE ourModule = GetModuleHandleW(DLLFileName);
84
85 CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed");
86
87 DWORD wResult = GetModuleFileNameW(ourModule, cachedPath, MAX_PATH+1);
88 CFAssert1(wResult > 0, __kCFLogAssertion, "GetModuleFileName failed: %d", GetLastError());
89 CFAssert1(wResult < MAX_PATH+1, __kCFLogAssertion, "GetModuleFileName result truncated: %s", cachedPath);
90
91 // strip off last component, the DLL name
92 CFIndex idx;
93 for (idx = wResult - 1; idx; idx--) {
94 if ('\\' == cachedPath[idx]) {
95 cachedPath[idx] = '\0';
96 break;
97 }
98 }
99 bDllPathCached = true;
100 }
101 return cachedPath;
102 }
103 #endif
104
105 static const char *__CFProcessPath = NULL;
106 static const char *__CFprogname = NULL;
107
108 const char **_CFGetProgname(void) {
109 if (!__CFprogname)
110 _CFProcessPath(); // sets up __CFprogname as a side-effect
111 return &__CFprogname;
112 }
113
114 const char **_CFGetProcessPath(void) {
115 if (!__CFProcessPath)
116 _CFProcessPath(); // sets up __CFProcessPath as a side-effect
117 return &__CFProcessPath;
118 }
119
120 #if DEPLOYMENT_TARGET_WINDOWS
121 const char *_CFProcessPath(void) {
122 if (__CFProcessPath) return __CFProcessPath;
123 wchar_t buf[CFMaxPathSize] = {0};
124 DWORD rlen = GetModuleFileNameW(NULL, buf, sizeof(buf) / sizeof(buf[0]));
125 if (0 < rlen) {
126 char asciiBuf[CFMaxPathSize] = {0};
127 int res = WideCharToMultiByte(CP_UTF8, 0, buf, rlen, asciiBuf, sizeof(asciiBuf) / sizeof(asciiBuf[0]), NULL, NULL);
128 if (0 < res) {
129 __CFProcessPath = strdup(asciiBuf);
130 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
131 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
132 }
133 }
134 if (!__CFProcessPath) {
135 __CFProcessPath = "";
136 __CFprogname = __CFProcessPath;
137 }
138 return __CFProcessPath;
139 }
140 #endif
141
142 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
143 const char *_CFProcessPath(void) {
144 if (__CFProcessPath) return __CFProcessPath;
145 #if DEPLOYMENT_TARGET_MACOSX
146 if (!issetugid()) {
147 const char *path = (char *)__CFgetenv("CFProcessPath");
148 if (path) {
149 __CFProcessPath = strdup(path);
150 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
151 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
152 return __CFProcessPath;
153 }
154 }
155 #endif
156 uint32_t size = CFMaxPathSize;
157 char buffer[size];
158 if (0 == _NSGetExecutablePath(buffer, &size)) {
159 __CFProcessPath = strdup(buffer);
160 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
161 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
162 }
163 if (!__CFProcessPath) {
164 __CFProcessPath = "";
165 __CFprogname = __CFProcessPath;
166 }
167 return __CFProcessPath;
168 }
169 #endif
170
171 #if DEPLOYMENT_TARGET_LINUX
172 #include <unistd.h>
173
174 const char *_CFProcessPath(void) {
175 if (__CFProcessPath) return __CFProcessPath;
176 char buf[CFMaxPathSize + 1];
177
178 ssize_t res = readlink("/proc/self/exe", buf, CFMaxPathSize);
179 if (res > 0) {
180 // null terminate, readlink does not
181 buf[res] = 0;
182 __CFProcessPath = strdup(buf);
183 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
184 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
185 } else {
186 __CFProcessPath = "";
187 __CFprogname = __CFProcessPath;
188 }
189 return __CFProcessPath;
190 }
191 #endif
192
193 __private_extern__ CFStringRef _CFProcessNameString(void) {
194 static CFStringRef __CFProcessNameString = NULL;
195 if (!__CFProcessNameString) {
196 const char *processName = *_CFGetProgname();
197 if (!processName) processName = "";
198 CFStringRef newStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, processName, kCFPlatformInterfaceStringEncoding);
199 if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *) newStr, (void * volatile *)& __CFProcessNameString)) {
200 CFRelease(newStr); // someone else made the assignment, so just release the extra string.
201 }
202 }
203 return __CFProcessNameString;
204 }
205
206
207 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
208
209 #include <pwd.h>
210 #include <sys/param.h>
211
212 // Set the fallBackToHome parameter to true if we should fall back to the HOME environment variable if all else fails. Otherwise return NULL.
213 static CFURLRef _CFCopyHomeDirURLForUser(struct passwd *upwd, bool fallBackToHome) {
214 const char *fixedHomePath = issetugid() ? NULL : __CFgetenv("CFFIXED_USER_HOME");
215 const char *homePath = NULL;
216
217 // Calculate the home directory we will use
218 // First try CFFIXED_USER_HOME (only if not setugid), then fall back to the upwd, then fall back to HOME environment variable
219 CFURLRef home = NULL;
220 if (!issetugid() && fixedHomePath) home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)fixedHomePath, strlen(fixedHomePath), true);
221 if (!home && upwd && upwd->pw_dir) home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)upwd->pw_dir, strlen(upwd->pw_dir), true);
222 if (fallBackToHome && !home) homePath = __CFgetenv("HOME");
223 if (fallBackToHome && !home && homePath) home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)homePath, strlen(homePath), true);
224
225 return home;
226 }
227
228 #endif
229
230
231 #define CFMaxHostNameLength 256
232 #define CFMaxHostNameSize (CFMaxHostNameLength+1)
233
234 __private_extern__ CFStringRef _CFStringCreateHostName(void) {
235 char myName[CFMaxHostNameSize];
236
237 // return @"" instead of nil a la CFUserName() and Ali Ozer
238 if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0';
239 return CFStringCreateWithCString(kCFAllocatorSystemDefault, myName, kCFPlatformInterfaceStringEncoding);
240 }
241
242 /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
243 These can return NULL.
244 */
245 CF_EXPORT CFStringRef CFGetUserName(void) {
246 return CFCopyUserName();
247 }
248
249 CF_EXPORT CFStringRef CFCopyUserName(void) {
250 CFStringRef result = NULL;
251 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
252 uid_t euid;
253 __CFGetUGIDs(&euid, NULL);
254 struct passwd *upwd = getpwuid(euid ? euid : getuid());
255 if (upwd && upwd->pw_name) {
256 result = CFStringCreateWithCString(kCFAllocatorSystemDefault, upwd->pw_name, kCFPlatformInterfaceStringEncoding);
257 } else {
258 const char *cuser = __CFgetenv("USER");
259 if (cuser) {
260 result = CFStringCreateWithCString(kCFAllocatorSystemDefault, cuser, kCFPlatformInterfaceStringEncoding);
261 }
262 }
263 #elif DEPLOYMENT_TARGET_WINDOWS
264 wchar_t username[1040];
265 DWORD size = 1040;
266 username[0] = 0;
267 if (GetUserNameW(username, &size)) {
268 // discount the extra NULL by decrementing the size
269 result = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)username, size - 1);
270 } else {
271 const char *cname = __CFgetenv("USERNAME");
272 if (cname) {
273 result = CFStringCreateWithCString(kCFAllocatorSystemDefault, cname, kCFPlatformInterfaceStringEncoding);
274 }
275 }
276 #else
277 #error Dont know how to compute user name on this platform
278 #endif
279 if (!result)
280 result = (CFStringRef)CFRetain(CFSTR(""));
281 return result;
282 }
283
284 CFURLRef CFCopyHomeDirectoryURL(void) {
285 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
286 uid_t euid;
287 __CFGetUGIDs(&euid, NULL);
288 struct passwd *upwd = getpwuid(euid ? euid : getuid());
289 return _CFCopyHomeDirURLForUser(upwd, true);
290 #elif DEPLOYMENT_TARGET_WINDOWS
291 CFURLRef retVal = NULL;
292 CFIndex len = 0;
293 CFStringRef str = NULL;
294
295 UniChar pathChars[MAX_PATH];
296 if (S_OK == SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, (wchar_t *)pathChars)) {
297 len = (CFIndex)wcslen((wchar_t *)pathChars);
298 str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathChars, len);
299 retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
300 CFRelease(str);
301 }
302
303 if (!retVal) {
304 // Fall back to environment variable, but this will not be unicode compatible
305 const char *cpath = __CFgetenv("HOMEPATH");
306 const char *cdrive = __CFgetenv("HOMEDRIVE");
307 if (cdrive && cpath) {
308 char fullPath[CFMaxPathSize];
309 strlcpy(fullPath, cdrive, sizeof(fullPath));
310 strlcat(fullPath, cpath, sizeof(fullPath));
311 str = CFStringCreateWithCString(kCFAllocatorSystemDefault, fullPath, kCFPlatformInterfaceStringEncoding);
312 retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
313 CFRelease(str);
314 }
315 }
316
317 if (!retVal) {
318 // Last resort: We have to get "some" directory location, so fall-back to the processes current directory.
319 UniChar currDir[MAX_PATH];
320 DWORD dwChars = GetCurrentDirectoryW(MAX_PATH + 1, (wchar_t *)currDir);
321 if (dwChars > 0) {
322 len = (CFIndex)wcslen((wchar_t *)currDir);
323 str = CFStringCreateWithCharacters(kCFAllocatorDefault, currDir, len);
324 retVal = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true);
325 CFRelease(str);
326 }
327 }
328
329 // We could do more here (as in KB Article Q101507). If that article is to be believed, we should only run into this case on Win95, or through user error.
330 CFStringRef testPath = CFURLCopyFileSystemPath(retVal, kCFURLWindowsPathStyle);
331 if (CFStringGetLength(testPath) == 0) {
332 CFRelease(retVal);
333 retVal = NULL;
334 }
335 if (testPath) CFRelease(testPath);
336
337 return retVal;
338 #else
339 #error Dont know how to compute users home directories on this platform
340 #endif
341 }
342
343 CF_EXPORT CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName) {
344 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
345 if (!uName) {
346 uid_t euid;
347 __CFGetUGIDs(&euid, NULL);
348 struct passwd *upwd = getpwuid(euid ? euid : getuid());
349 return _CFCopyHomeDirURLForUser(upwd, true);
350 } else {
351 struct passwd *upwd = NULL;
352 char buf[128], *user;
353 SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding);
354 CFIndex usedSize;
355 if (size < 127) {
356 user = buf;
357 } else {
358 user = CFAllocatorAllocate(kCFAllocatorSystemDefault, size+1, 0);
359 }
360 if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, (uint8_t *)user, size, &usedSize) == len) {
361 user[usedSize] = '\0';
362 upwd = getpwnam(user);
363 }
364 if (buf != user) {
365 CFAllocatorDeallocate(kCFAllocatorSystemDefault, user);
366 }
367 return _CFCopyHomeDirURLForUser(upwd, false);
368 }
369 #elif DEPLOYMENT_TARGET_WINDOWS
370 // This code can only get the directory for the current user
371 CFStringRef userName = uName ? CFCopyUserName() : NULL;
372 if (uName && !CFEqual(uName, userName)) {
373 CFLog(kCFLogLevelError, CFSTR("CFCopyHomeDirectoryURLForUser(): Unable to get home directory for other user"));
374 if (userName) CFRelease(userName);
375 return NULL;
376 }
377 if (userName) CFRelease(userName);
378 return CFCopyHomeDirectoryURL();
379 #else
380 #error Dont know how to compute users home directories on this platform
381 #endif
382 }
383
384
385 #undef CFMaxHostNameLength
386 #undef CFMaxHostNameSize
387
388 #if DEPLOYMENT_TARGET_WINDOWS
389 CF_INLINE CFIndex strlen_UniChar(const UniChar* p) {
390 CFIndex result = 0;
391 while ((*p++) != 0)
392 ++result;
393 return result;
394 }
395
396 //#include <shfolder.h>
397 /*
398 * _CFCreateApplicationRepositoryPath returns the path to the application's
399 * repository in a CFMutableStringRef. The path returned will be:
400 * <nFolder_path>\Apple Computer\<bundle_name>\
401 * or if the bundle name cannot be obtained:
402 * <nFolder_path>\Apple Computer\
403 * where nFolder_path is obtained by calling SHGetFolderPath with nFolder
404 * (for example, with CSIDL_APPDATA or CSIDL_LOCAL_APPDATA).
405 *
406 * The CFMutableStringRef result must be released by the caller.
407 *
408 * If anything fails along the way, the result will be NULL.
409 */
410 CF_EXPORT CFMutableStringRef _CFCreateApplicationRepositoryPath(CFAllocatorRef alloc, int nFolder) {
411 CFMutableStringRef result = NULL;
412 UniChar szPath[MAX_PATH];
413
414 // get the current path to the data repository: CSIDL_APPDATA (roaming) or CSIDL_LOCAL_APPDATA (nonroaming)
415 if (S_OK == SHGetFolderPathW(NULL, nFolder, NULL, 0, (wchar_t *) szPath)) {
416 CFStringRef directoryPath;
417
418 // make it a CFString
419 directoryPath = CFStringCreateWithCharacters(alloc, szPath, strlen_UniChar(szPath));
420 if (directoryPath) {
421 CFBundleRef bundle;
422 CFStringRef bundleName;
423 CFStringRef completePath;
424
425 // attempt to get the bundle name
426 bundle = CFBundleGetMainBundle();
427 if (bundle) {
428 bundleName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey);
429 }
430 else {
431 bundleName = NULL;
432 }
433
434 if (bundleName) {
435 // the path will be "<directoryPath>\Apple Computer\<bundleName>\" if there is a bundle name
436 completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\%@\\"), directoryPath, bundleName);
437 }
438 else {
439 // or "<directoryPath>\Apple Computer\" if there is no bundle name.
440 completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\"), directoryPath);
441 }
442
443 CFRelease(directoryPath);
444
445 // make a mutable copy to return
446 if (completePath) {
447 result = CFStringCreateMutableCopy(alloc, 0, completePath);
448 CFRelease(completePath);
449 }
450 }
451 }
452
453 return ( result );
454 }
455 #endif
456
457 #pragma mark -
458 #pragma mark Thread Functions
459
460 #if DEPLOYMENT_TARGET_WINDOWS
461
462 // This code from here:
463 // http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
464
465 const DWORD MS_VC_EXCEPTION=0x406D1388;
466 #pragma pack(push,8)
467 typedef struct tagTHREADNAME_INFO
468 {
469 DWORD dwType; // Must be 0x1000.
470 LPCSTR szName; // Pointer to name (in user addr space).
471 DWORD dwThreadID; // Thread ID (-1=caller thread).
472 DWORD dwFlags; // Reserved for future use, must be zero.
473 } THREADNAME_INFO;
474 #pragma pack(pop)
475
476 CF_EXPORT void _NS_pthread_setname_np(const char *name) {
477 THREADNAME_INFO info;
478 info.dwType = 0x1000;
479 info.szName = name;
480 info.dwThreadID = GetCurrentThreadId();
481 info.dwFlags = 0;
482
483 __try
484 {
485 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
486 }
487 __except(EXCEPTION_EXECUTE_HANDLER)
488 {
489 }
490 }
491
492 static pthread_t __initialPthread = { NULL, 0 };
493
494 CF_EXPORT int _NS_pthread_main_np() {
495 pthread_t me = pthread_self();
496 if (NULL == __initialPthread.p) {
497 __initialPthread.p = me.p;
498 __initialPthread.x = me.x;
499 }
500 return (pthread_equal(__initialPthread, me));
501 }
502
503 #endif
504
505 #pragma mark -
506 #pragma mark Thread Local Data
507
508 // If slot >= CF_TSD_MAX_SLOTS, the SPI functions will crash at NULL + slot address.
509 // If thread data has been torn down, these functions should crash on CF_TSD_BAD_PTR + slot address.
510 #define CF_TSD_MAX_SLOTS 70
511
512 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
513 #define CF_TSD_KEY 55
514 #endif
515
516 // Windows and Linux, not sure how many times the destructor could get called; CF_TSD_MAX_DESTRUCTOR_CALLS could be 1
517
518 #define CF_TSD_BAD_PTR ((void *)0x1000)
519
520 typedef void (*tsdDestructor)(void *);
521
522 // Data structure to hold TSD data, cleanup functions for each
523 typedef struct __CFTSDTable {
524 uint32_t destructorCount;
525 uintptr_t data[CF_TSD_MAX_SLOTS];
526 tsdDestructor destructors[CF_TSD_MAX_SLOTS];
527 } __CFTSDTable;
528
529 static void __CFTSDFinalize(void *arg);
530
531 #if DEPLOYMENT_TARGET_WINDOWS
532
533 static DWORD __CFTSDIndexKey = 0xFFFFFFFF;
534
535 // Called from CFRuntime's startup code, on Windows only
536 __private_extern__ void __CFTSDWindowsInitialize() {
537 __CFTSDIndexKey = TlsAlloc();
538 }
539
540 // Called from CFRuntime's cleanup code, on Windows only
541 __private_extern__ void __CFTSDWindowsCleanup() {
542 TlsFree(__CFTSDIndexKey);
543 }
544
545 // Called for each thread as it exits, on Windows only
546 __private_extern__ void __CFFinalizeWindowsThreadData() {
547 // Normally, this should call the finalizer several times to emulate the behavior of pthreads on Windows. However, a few bugs keep us from doing this:
548 // <rdar://problem/8989063> REGRESSION(CF-610-CF-611): Crash closing Safari in BonjourDB destructor (Windows)
549 // <rdar://problem/9326814> SyncUIHandler crashes after conflict is resolved and we do SyncNow
550 // and a bug in dispatch keeps us from using pthreadsWin32 directly, because it does not deal with the case of a dispatch_async happening during process exit (it attempts to create a thread, but that is illegal on Win32 and causes a hang).
551 // So instead we just finalize once, which is the behavior pre-Airwolf anyway
552 __CFTSDFinalize(TlsGetValue(__CFTSDIndexKey));
553 }
554
555 #endif
556
557 #if DEPLOYMENT_TARGET_LINUX
558
559 static pthread_key_t __CFTSDIndexKey;
560
561 // Called from CFRuntime's startup code, on Linux only
562 __private_extern__ void __CFTSDLinuxInitialize() {
563 (void)pthread_key_create(&__CFTSDIndexKey, __CFTSDFinalize);
564 }
565
566 #endif
567
568 static void __CFTSDSetSpecific(void *arg) {
569 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
570 pthread_setspecific(CF_TSD_KEY, arg);
571 #elif DEPLOYMENT_TARGET_LINUX
572 pthread_setspecific(__CFTSDIndexKey, arg);
573 #elif DEPLOYMENT_TARGET_WINDOWS
574 TlsSetValue(__CFTSDIndexKey, arg);
575 #endif
576 }
577
578 static void *__CFTSDGetSpecific() {
579 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
580 return pthread_getspecific(CF_TSD_KEY);
581 #elif DEPLOYMENT_TARGET_LINUX
582 return pthread_getspecific(__CFTSDIndexKey);
583 #elif DEPLOYMENT_TARGET_WINDOWS
584 return TlsGetValue(__CFTSDIndexKey);
585 #endif
586 }
587
588 static void __CFTSDFinalize(void *arg) {
589 // Set our TSD so we're called again by pthreads. It will call the destructor PTHREAD_DESTRUCTOR_ITERATIONS times as long as a value is set in the thread specific data. We handle each case below.
590 __CFTSDSetSpecific(arg);
591
592 if (!arg || arg == CF_TSD_BAD_PTR) {
593 // We've already been destroyed. The call above set the bad pointer again. Now we just return.
594 return;
595 }
596
597 __CFTSDTable *table = (__CFTSDTable *)arg;
598 table->destructorCount++;
599
600 // On first calls invoke destructor. Later we destroy the data.
601 // Note that invocation of the destructor may cause a value to be set again in the per-thread data slots. The destructor count and destructors are preserved.
602 // This logic is basically the same as what pthreads does. We just skip the 'created' flag.
603 for (int32_t i = 0; i < CF_TSD_MAX_SLOTS; i++) {
604 if (table->data[i] && table->destructors[i]) {
605 uintptr_t old = table->data[i];
606 table->data[i] = (uintptr_t)NULL;
607 table->destructors[i]((void *)(old));
608 }
609 }
610
611 if (table->destructorCount == PTHREAD_DESTRUCTOR_ITERATIONS - 1) { // On PTHREAD_DESTRUCTOR_ITERATIONS-1 call, destroy our data
612 free(table);
613
614 // Now if the destructor is called again we will take the shortcut at the beginning of this function.
615 __CFTSDSetSpecific(CF_TSD_BAD_PTR);
616 return;
617 }
618 }
619
620 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
621 extern int pthread_key_init_np(int, void (*)(void *));
622 #endif
623
624 // Get or initialize a thread local storage. It is created on demand.
625 static __CFTSDTable *__CFTSDGetTable() {
626 __CFTSDTable *table = (__CFTSDTable *)__CFTSDGetSpecific();
627 // Make sure we're not setting data again after destruction.
628 if (table == CF_TSD_BAD_PTR) {
629 return NULL;
630 }
631 // Create table on demand
632 if (!table) {
633 // This memory is freed in the finalize function
634 table = (__CFTSDTable *)calloc(1, sizeof(__CFTSDTable));
635 // Windows and Linux have created the table already, we need to initialize it here for other platforms. On Windows, the cleanup function is called by DllMain when a thread exits. On Linux the destructor is set at init time.
636 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
637 pthread_key_init_np(CF_TSD_KEY, __CFTSDFinalize);
638 #endif
639 __CFTSDSetSpecific(table);
640 }
641
642 return table;
643 }
644
645
646 // For the use of CF and Foundation only
647 CF_EXPORT void *_CFGetTSD(uint32_t slot) {
648 if (slot > CF_TSD_MAX_SLOTS) {
649 _CFLogSimple(kCFLogLevelError, "Error: TSD slot %d out of range (get)", slot);
650 HALT;
651 }
652 __CFTSDTable *table = __CFTSDGetTable();
653 if (!table) {
654 // Someone is getting TSD during thread destruction. The table is gone, so we can't get any data anymore.
655 _CFLogSimple(kCFLogLevelWarning, "Warning: TSD slot %d retrieved but the thread data has already been torn down.", slot);
656 return NULL;
657 }
658 uintptr_t *slots = (uintptr_t *)(table->data);
659 return (void *)slots[slot];
660 }
661
662 // For the use of CF and Foundation only
663 CF_EXPORT void *_CFSetTSD(uint32_t slot, void *newVal, tsdDestructor destructor) {
664 if (slot > CF_TSD_MAX_SLOTS) {
665 _CFLogSimple(kCFLogLevelError, "Error: TSD slot %d out of range (set)", slot);
666 HALT;
667 }
668 __CFTSDTable *table = __CFTSDGetTable();
669 if (!table) {
670 // Someone is setting TSD during thread destruction. The table is gone, so we can't get any data anymore.
671 _CFLogSimple(kCFLogLevelWarning, "Warning: TSD slot %d set but the thread data has already been torn down.", slot);
672 return NULL;
673 }
674
675 void *oldVal = (void *)table->data[slot];
676
677 table->data[slot] = (uintptr_t)newVal;
678 table->destructors[slot] = destructor;
679
680 return oldVal;
681 }
682
683
684 #pragma mark -
685 #pragma mark Windows Wide to UTF8 and UTF8 to Wide
686
687 #if DEPLOYMENT_TARGET_WINDOWS
688 /* On Windows, we want to use UTF-16LE for path names to get full unicode support. Internally, however, everything remains in UTF-8 representation. These helper functions stand between CF and the Microsoft CRT to ensure that we are using the right representation on both sides. */
689
690 #include <sys/stat.h>
691 #include <share.h>
692
693 // Creates a buffer of wchar_t to hold a UTF16LE version of the UTF8 str passed in. Caller must free the buffer when done. If resultLen is non-NULL, it is filled out with the number of characters in the string.
694 static wchar_t *createWideFileSystemRepresentation(const char *str, CFIndex *resultLen) {
695 // Get the real length of the string in UTF16 characters
696 CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8);
697 CFIndex strLen = CFStringGetLength(cfStr);
698
699 // Allocate a wide buffer to hold the converted string, including space for a NULL terminator
700 wchar_t *wideBuf = (wchar_t *)malloc((strLen + 1) * sizeof(wchar_t));
701
702 // Copy the string into the buffer and terminate
703 CFStringGetCharacters(cfStr, CFRangeMake(0, strLen), (UniChar *)wideBuf);
704 wideBuf[strLen] = 0;
705
706 CFRelease(cfStr);
707 if (resultLen) *resultLen = strLen;
708 return wideBuf;
709 }
710
711 // Copies a UTF16 buffer into a supplied UTF8 buffer.
712 static void copyToNarrowFileSystemRepresentation(const wchar_t *wide, CFIndex dstBufSize, char *dstbuf) {
713 // Get the real length of the wide string in UTF8 characters
714 CFStringRef cfStr = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)wide, wcslen(wide));
715 CFIndex strLen = CFStringGetLength(cfStr);
716 CFIndex bytesUsed;
717
718 // Copy the wide string into the buffer and terminate
719 CFStringGetBytes(cfStr, CFRangeMake(0, strLen), kCFStringEncodingUTF8, 0, false, (uint8_t *)dstbuf, dstBufSize, &bytesUsed);
720 dstbuf[bytesUsed] = 0;
721
722 CFRelease(cfStr);
723 }
724
725 CF_EXPORT int _NS_stat(const char *name, struct _stat *st) {
726 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
727 int res = _wstat(wide, st);
728 free(wide);
729 return res;
730 }
731
732 CF_EXPORT int _NS_mkdir(const char *name) {
733 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
734 int res = _wmkdir(wide);
735 free(wide);
736 return res;
737 }
738
739 CF_EXPORT int _NS_rmdir(const char *name) {
740 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
741 int res = _wrmdir(wide);
742 free(wide);
743 return res;
744 }
745
746 CF_EXPORT int _NS_chmod(const char *name, int mode) {
747 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
748
749 // Convert mode
750 int newMode = 0;
751 if (mode | 0400) newMode |= _S_IREAD;
752 if (mode | 0200) newMode |= _S_IWRITE;
753 if (mode | 0100) newMode |= _S_IEXEC;
754
755 int res = _wchmod(wide, newMode);
756 free(wide);
757 return res;
758 }
759
760 CF_EXPORT int _NS_unlink(const char *name) {
761 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
762 int res = _wunlink(wide);
763 free(wide);
764 return res;
765 }
766
767 // Warning: this doesn't support dstbuf as null even though 'getcwd' does
768 CF_EXPORT char *_NS_getcwd(char *dstbuf, size_t size) {
769 if (!dstbuf) {
770 CFLog(kCFLogLevelWarning, CFSTR("CFPlatform: getcwd called with null buffer"));
771 return 0;
772 }
773
774 wchar_t *buf = _wgetcwd(NULL, 0);
775 if (!buf) {
776 return NULL;
777 }
778
779 // Convert result to UTF8
780 copyToNarrowFileSystemRepresentation(buf, (CFIndex)size, dstbuf);
781 free(buf);
782 return dstbuf;
783 }
784
785 CF_EXPORT char *_NS_getenv(const char *name) {
786 // todo: wide getenv
787 // We have to be careful what happens here, because getenv is called during cf initialization, and things like cfstring may not be working yet
788 return getenv(name);
789 }
790
791 CF_EXPORT int _NS_rename(const char *oldName, const char *newName) {
792 wchar_t *oldWide = createWideFileSystemRepresentation(oldName, NULL);
793 wchar_t *newWide = createWideFileSystemRepresentation(newName, NULL);
794 // _wrename on Windows does not behave exactly as rename() on Mac OS -- if the file exists, the Windows one will fail whereas the Mac OS version will replace
795 // To simulate the Mac OS behavior, we use the Win32 API then fill out errno if something goes wrong
796 BOOL winRes = MoveFileExW(oldWide, newWide, MOVEFILE_REPLACE_EXISTING);
797 DWORD error = GetLastError();
798 if (!winRes) {
799 switch (error) {
800 case ERROR_SUCCESS:
801 errno = 0;
802 break;
803 case ERROR_FILE_NOT_FOUND:
804 case ERROR_PATH_NOT_FOUND:
805 case ERROR_OPEN_FAILED:
806 errno = ENOENT;
807 break;
808 case ERROR_ACCESS_DENIED:
809 errno = EACCES;
810 break;
811 default:
812 errno = error;
813 }
814 }
815 free(oldWide);
816 free(newWide);
817 return (winRes ? 0 : -1);
818 }
819
820 CF_EXPORT int _NS_open(const char *name, int oflag, int pmode) {
821 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
822 int fd;
823 _wsopen_s(&fd, wide, oflag, _SH_DENYNO, _S_IREAD | _S_IWRITE);
824 free(wide);
825 return fd;
826 }
827
828 CF_EXPORT int _NS_chdir(const char *name) {
829 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
830 int res = _wchdir(wide);
831 free(wide);
832 return res;
833 }
834
835 CF_EXPORT int _NS_access(const char *name, int amode) {
836 // execute is always true
837 if (amode == 1) return 0;
838
839 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
840 // we only care about the read-only (04) and write-only (02) bits, so mask octal 06
841 int res = _waccess(wide, amode & 06);
842 free(wide);
843 return res;
844 }
845
846 // This is a bit different than the standard 'mkstemp', because the size parameter is needed so we know the size of the UTF8 buffer
847 // Also, we don't avoid the race between creating a temporary file name and opening it on Windows like we do on Mac
848 CF_EXPORT int _NS_mkstemp(char *name, int bufSize) {
849 CFIndex nameLen;
850 wchar_t *wide = createWideFileSystemRepresentation(name, &nameLen);
851
852 // First check to see if the directory that this new temporary file will be created in exists. If not, set errno to ENOTDIR. This mimics the behavior of mkstemp on MacOS more closely.
853 // Look for the last '\' in the path
854 wchar_t *lastSlash = wcsrchr(wide, '\\');
855 if (!lastSlash) {
856 free(wide);
857 return -1;
858 }
859
860 // Set the last slash to NULL temporarily and use it for _wstat
861 *lastSlash = 0;
862 struct _stat dirInfo;
863 int res = _wstat(wide, &dirInfo);
864 if (res < 0) {
865 if (errno == ENOENT) {
866 errno = ENOTDIR;
867 }
868 free(wide);
869 return -1;
870 }
871 // Restore the last slash
872 *lastSlash = '\\';
873
874 errno_t err = _wmktemp_s(wide, nameLen + 1);
875 if (err != 0) {
876 free(wide);
877 return 0;
878 }
879
880 int fd;
881 _wsopen_s(&fd, wide, _O_RDWR | _O_CREAT | CF_OPENFLGS, _SH_DENYNO, _S_IREAD | _S_IWRITE);
882
883 // Convert the wide name back into the UTF8 buffer the caller supplied
884 copyToNarrowFileSystemRepresentation(wide, bufSize, name);
885 free(wide);
886 return fd;
887 }
888
889
890 // Utilities to convert from a volume name to a drive letter
891
892 Boolean _isAFloppy(char driveLetter)
893 {
894 HANDLE h;
895 TCHAR tsz[8];
896 Boolean retval = false;
897 int iDrive;
898
899 if (driveLetter >= 'a' && driveLetter <= 'z') {
900 driveLetter = driveLetter - 'a' + 'A';
901 }
902
903 if ((driveLetter < 'A') || (driveLetter > 'Z')) {
904 // invalid driveLetter; I guess it's not a floppy...
905 return false;
906 }
907
908 iDrive = driveLetter - 'A' + 1;
909
910 // On Windows NT, use the technique described in the Knowledge Base article Q115828 and in the "FLOPPY" SDK sample.
911 wsprintf(tsz, TEXT("\\\\.\\%c:"), TEXT('@') + iDrive);
912 h = CreateFile(tsz, 0, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
913 if (h != INVALID_HANDLE_VALUE)
914 {
915 DISK_GEOMETRY Geom[20];
916 DWORD cb;
917
918 if (DeviceIoControl (h, IOCTL_DISK_GET_MEDIA_TYPES, 0, 0,
919 Geom, sizeof(Geom), &cb, 0)
920 && cb > 0)
921 {
922 switch (Geom[0].MediaType)
923 {
924 case F5_1Pt2_512: // 5.25 1.2MB floppy
925 case F5_360_512: // 5.25 360K floppy
926 case F5_320_512: // 5.25 320K floppy
927 case F5_320_1024: // 5.25 320K floppy
928 case F5_180_512: // 5.25 180K floppy
929 case F5_160_512: // 5.25 160K floppy
930 case F3_1Pt44_512: // 3.5 1.44MB floppy
931 case F3_2Pt88_512: // 3.5 2.88MB floppy
932 case F3_20Pt8_512: // 3.5 20.8MB floppy
933 case F3_720_512: // 3.5 720K floppy
934 retval = true;
935 break;
936 }
937 }
938
939 CloseHandle(h);
940 }
941
942 return retval;
943 }
944
945
946 extern CFStringRef CFCreateWindowsDrivePathFromVolumeName(CFStringRef volNameStr) {
947 if (!volNameStr) return NULL;
948
949 // This code is designed to match as closely as possible code from QuickTime's library
950 CFIndex strLen = CFStringGetLength(volNameStr);
951 if (strLen == 0) {
952 return NULL;
953 }
954
955 // Get drive names
956 long length, result;
957 wchar_t *driveNames = NULL;
958
959 // Get the size of the buffer to store the list of drives
960 length = GetLogicalDriveStringsW(0, 0);
961 if (!length) {
962 return NULL;
963 }
964
965 driveNames = (wchar_t *)malloc((length + 1) * sizeof(wchar_t));
966 result = GetLogicalDriveStringsW(length, driveNames);
967
968 if (!result || result > length) {
969 free(driveNames);
970 return NULL;
971 }
972
973 // Get the volume name string into a wide buffer
974 wchar_t *theVolumeName = (wchar_t *)malloc((strLen + 1) * sizeof(wchar_t));
975 CFStringGetCharacters(volNameStr, CFRangeMake(0, strLen), (UniChar *)theVolumeName);
976 theVolumeName[strLen] = 0;
977
978 // lowercase volume name
979 _wcslwr(theVolumeName);
980
981 // Iterate through the drive names, looking for something that matches
982 wchar_t *drivePtr = driveNames;
983 CFStringRef drivePathResult = NULL;
984
985 while (*drivePtr) {
986 _wcslwr(drivePtr);
987
988 if (!_isAFloppy((char)*drivePtr)) {
989 UINT oldErrorMode;
990 DWORD whoCares1, whoCares2;
991 BOOL getVolInfoSucceeded;
992 UniChar thisVolumeName[MAX_PATH];
993
994 // Convert this drive string into a volume name
995 oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
996 getVolInfoSucceeded = GetVolumeInformationW(drivePtr, (LPWSTR)thisVolumeName, sizeof(thisVolumeName), NULL, &whoCares1, &whoCares2, NULL, 0);
997 SetErrorMode(oldErrorMode);
998
999 if (getVolInfoSucceeded) {
1000 _wcslwr((wchar_t *)thisVolumeName);
1001
1002 // If the volume corresponding to this drive matches the input volume
1003 // then this drive is the winner.
1004 if (!wcscmp((const wchar_t *)thisVolumeName, theVolumeName) ||
1005 (*thisVolumeName == 0x00 && (CFStringCompare(volNameStr, CFSTR("NONAME"), 0) == kCFCompareEqualTo))) {
1006 drivePathResult = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)drivePtr, wcslen(drivePtr));
1007 break;
1008 }
1009 }
1010 }
1011
1012 drivePtr += wcslen(drivePtr) + 1;
1013 }
1014
1015
1016 free(driveNames);
1017 free(theVolumeName);
1018 return drivePathResult;
1019 }
1020
1021 struct timezone {
1022 int tz_minuteswest; /* minutes west of Greenwich */
1023 int tz_dsttime; /* type of dst correction */
1024 };
1025
1026 __private_extern__ int _NS_gettimeofday(struct timeval *tv, struct timezone *tz) {
1027 if (tv) {
1028 FILETIME ft;
1029 GetSystemTimeAsFileTime(&ft);
1030 unsigned __int64 t = 0;
1031 t |= ft.dwHighDateTime;
1032 t <<= 32;
1033 t |= ft.dwLowDateTime;
1034
1035 // Convert to microseconds
1036 t /= 10;
1037
1038 // Difference between 1/1/1970 and 1/1/1601
1039 t -= 11644473600000000Ui64;
1040
1041 // Convert microseconds to seconds
1042 tv->tv_sec = (long)(t / 1000000UL);
1043 tv->tv_usec = (long)(t % 1000000UL);
1044 }
1045
1046 // We don't support tz
1047 return 0;
1048 }
1049
1050 #endif // DEPLOYMENT_TARGET_WINDOWS
1051
1052 #pragma mark -
1053 #pragma mark Linux OSAtomic
1054
1055 #if DEPLOYMENT_TARGET_LINUX
1056
1057 bool OSAtomicCompareAndSwapPtr(void *oldp, void *newp, void *volatile *dst)
1058 {
1059 return __sync_bool_compare_and_swap(dst, oldp, newp);
1060 }
1061
1062 bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst)
1063 {
1064 return __sync_val_compare_and_swap(dst, oldl, newl);
1065 }
1066
1067 bool OSAtomicCompareAndSwapPtrBarrier(void *oldp, void *newp, void *volatile *dst)
1068 {
1069 return __sync_bool_compare_and_swap(dst, oldp, newp);
1070 }
1071
1072 int32_t OSAtomicAdd32Barrier( int32_t theAmount, volatile int32_t *theValue ) {
1073 return __sync_fetch_and_add(theValue, theAmount) + theAmount;
1074 }
1075
1076 bool OSAtomicCompareAndSwap32Barrier(int32_t oldValue, int32_t newValue, volatile int32_t *theValue) {
1077 return __sync_bool_compare_and_swap(theValue, oldValue, newValue);
1078 }
1079
1080 int32_t OSAtomicDecrement32Barrier(volatile int32_t *dst)
1081 {
1082 return OSAtomicAdd32Barrier(-1, dst);
1083 }
1084
1085 int32_t OSAtomicIncrement32Barrier(volatile int32_t *dst)
1086 {
1087 return OSAtomicAdd32Barrier(1, dst);
1088 }
1089
1090 int32_t OSAtomicAdd32( int32_t theAmount, volatile int32_t *theValue ) {
1091 return OSAtomicAdd32Barrier(theAmount, theValue);
1092 }
1093
1094 int32_t OSAtomicIncrement32(volatile int32_t *theValue) {
1095 return OSAtomicIncrement32Barrier(theValue);
1096 }
1097
1098 int32_t OSAtomicDecrement32(volatile int32_t *theValue) {
1099 return OSAtomicDecrement32Barrier(theValue);
1100 }
1101
1102 void OSMemoryBarrier() {
1103 __sync_synchronize();
1104 }
1105
1106 #endif // DEPLOYMENT_TARGET_LINUX
1107
1108 #pragma mark -
1109 #pragma mark Windows and Linux Helpers
1110
1111 #if DEPLOYMENT_TARGET_WINDOWS || DEPLOYMENT_TARGET_LINUX
1112
1113 #include <stdio.h>
1114
1115 __private_extern__ int asprintf(char **ret, const char *format, ...) {
1116 va_list args;
1117 size_t sz = 1024;
1118 *ret = (char *) malloc(sz * sizeof(char));
1119 if (!*ret) return -1;
1120 va_start(args, format);
1121 int cnt = vsnprintf(*ret, sz, format, args);
1122 va_end(args);
1123 if (cnt < sz - 1) return cnt;
1124 sz = cnt + 8;
1125 char *oldret = *ret;
1126 *ret = (char *) realloc(*ret, sz * sizeof(char));
1127 if (!*ret && oldret) free(oldret);
1128 if (!*ret) return -1;
1129 va_start(args, format);
1130 cnt = vsnprintf(*ret, sz, format, args);
1131 va_end(args);
1132 if (cnt < sz - 1) return cnt;
1133 free(*ret);
1134 *ret = NULL;
1135 return -1;
1136 }
1137
1138 #endif
1139