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