]> git.saurik.com Git - apple/cf.git/blob - CFPlatform.c
CF-550.19.tar.gz
[apple/cf.git] / CFPlatform.c
1 /*
2 * Copyright (c) 2010 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-2009, Apple Inc. All rights reserved.
26 Responsibility: Christopher Kane
27 */
28
29 #include "CFInternal.h"
30 #include <CoreFoundation/CFPriv.h>
31 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
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
46 #define getcwd _NS_getcwd
47
48 #endif
49
50 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS_SYNC
51 #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8
52 #else
53 #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding()
54 #endif
55
56 static CFStringRef _CFUserName(void);
57
58 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
59 // CoreGraphics and LaunchServices are only projects (1 Dec 2006) that use these
60 char **_CFArgv(void) { return *_NSGetArgv(); }
61 int _CFArgc(void) { return *_NSGetArgc(); }
62 #endif
63
64
65 __private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) {
66 return getcwd(path, maxlen) != NULL;
67 }
68
69 static Boolean __CFIsCFM = false;
70
71 // If called super early, we just return false
72 __private_extern__ Boolean _CFIsCFM(void) {
73 return __CFIsCFM;
74 }
75
76 #if DEPLOYMENT_TARGET_WINDOWS
77 #define PATH_SEP '\\'
78 #else
79 #define PATH_SEP '/'
80 #endif
81
82
83 #if DEPLOYMENT_TARGET_WINDOWS
84 // Returns the path to the CF DLL, which we can then use to find resources like char sets
85 bool bDllPathCached = false;
86 __private_extern__ const wchar_t *_CFDLLPath(void) {
87 static wchar_t cachedPath[MAX_PATH+1];
88
89 if (!bDllPathCached) {
90 #ifdef _DEBUG
91 // might be nice to get this from the project file at some point
92 wchar_t *DLLFileName = L"CoreFoundation_debug.dll";
93 #else
94 wchar_t *DLLFileName = L"CoreFoundation.dll";
95 #endif
96 HMODULE ourModule = GetModuleHandleW(DLLFileName);
97
98 CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed");
99
100 DWORD wResult = GetModuleFileNameW(ourModule, cachedPath, MAX_PATH+1);
101 CFAssert1(wResult > 0, __kCFLogAssertion, "GetModuleFileName failed: %d", GetLastError());
102 CFAssert1(wResult < MAX_PATH+1, __kCFLogAssertion, "GetModuleFileName result truncated: %s", cachedPath);
103
104 // strip off last component, the DLL name
105 CFIndex idx;
106 for (idx = wResult - 1; idx; idx--) {
107 if ('\\' == cachedPath[idx]) {
108 cachedPath[idx] = '\0';
109 break;
110 }
111 }
112 bDllPathCached = true;
113 }
114 return cachedPath;
115 }
116 #endif
117
118 static const char *__CFProcessPath = NULL;
119 static const char *__CFprogname = NULL;
120
121 const char **_CFGetProgname(void) {
122 if (!__CFprogname)
123 _CFProcessPath(); // sets up __CFprogname as a side-effect
124 return &__CFprogname;
125 }
126
127 const char **_CFGetProcessPath(void) {
128 if (!__CFProcessPath)
129 _CFProcessPath(); // sets up __CFProcessPath as a side-effect
130 return &__CFProcessPath;
131 }
132
133 #if DEPLOYMENT_TARGET_WINDOWS
134 const char *_CFProcessPath(void) {
135 if (__CFProcessPath) return __CFProcessPath;
136 wchar_t buf[CFMaxPathSize] = {0};
137 DWORD rlen = GetModuleFileNameW(NULL, buf, sizeof(buf) / sizeof(buf[0]));
138 if (0 < rlen) {
139 char asciiBuf[CFMaxPathSize] = {0};
140 int res = WideCharToMultiByte(CP_UTF8, 0, buf, rlen, asciiBuf, sizeof(asciiBuf) / sizeof(asciiBuf[0]), NULL, NULL);
141 if (0 < res) {
142 __CFProcessPath = strdup(asciiBuf);
143 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
144 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
145 }
146 }
147 if (!__CFProcessPath) {
148 __CFProcessPath = "";
149 __CFprogname = __CFProcessPath;
150 }
151 return __CFProcessPath;
152 }
153 #endif
154
155 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
156 const char *_CFProcessPath(void) {
157 if (__CFProcessPath) return __CFProcessPath;
158 #if DEPLOYMENT_TARGET_MACOSX
159 if (!issetugid()) {
160 const char *path = (char *)__CFgetenv("CFProcessPath");
161 if (path) {
162 __CFProcessPath = strdup(path);
163 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
164 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
165 return __CFProcessPath;
166 }
167 }
168 #endif
169 uint32_t size = CFMaxPathSize;
170 char buffer[size];
171 if (0 == _NSGetExecutablePath(buffer, &size)) {
172 #if DEPLOYMENT_TARGET_MACOSX && defined(__ppc__)
173 size_t len = strlen(buffer);
174 if (12 <= len && 0 == strcmp("LaunchCFMApp", buffer + len - 12)) {
175 struct stat exec, lcfm;
176 const char *launchcfm = "/System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp";
177 if (0 == stat(launchcfm, &lcfm) && 0 == stat(buffer, &exec) && (lcfm.st_dev == exec.st_dev) && (lcfm.st_ino == exec.st_ino)) {
178 // Executable is LaunchCFMApp, take special action
179 __CFIsCFM = true;
180 if ((*_NSGetArgv())[1] && '/' == *((*_NSGetArgv())[1])) {
181 strlcpy(buffer, (*_NSGetArgv())[1], sizeof(buffer));
182 }
183 }
184 }
185 #endif
186 __CFProcessPath = strdup(buffer);
187 __CFprogname = strrchr(__CFProcessPath, PATH_SEP);
188 __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath);
189 }
190 if (!__CFProcessPath) {
191 __CFProcessPath = "";
192 __CFprogname = __CFProcessPath;
193 }
194 return __CFProcessPath;
195 }
196 #endif
197
198 __private_extern__ CFStringRef _CFProcessNameString(void) {
199 static CFStringRef __CFProcessNameString = NULL;
200 if (!__CFProcessNameString) {
201 const char *processName = *_CFGetProgname();
202 if (!processName) processName = "";
203 CFStringRef newStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, processName, kCFPlatformInterfaceStringEncoding);
204 if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *) newStr, (void * volatile *)& __CFProcessNameString)) {
205 CFRelease(newStr); // someone else made the assignment, so just release the extra string.
206 }
207 }
208 return __CFProcessNameString;
209 }
210
211 static CFStringRef __CFUserName = NULL;
212
213 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
214 static CFURLRef __CFHomeDirectory = NULL;
215 static uint32_t __CFEUID = -1;
216 static uint32_t __CFUID = -1;
217
218 static CFURLRef _CFCopyHomeDirURLForUser(struct passwd *upwd) {
219 CFURLRef home = NULL;
220 if (!issetugid()) {
221 const char *path = __CFgetenv("CFFIXED_USER_HOME");
222 if (path) {
223 home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)path, strlen(path), true);
224 }
225 }
226 if (!home) {
227 if (upwd && upwd->pw_dir) {
228 home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)upwd->pw_dir, strlen(upwd->pw_dir), true);
229 }
230 }
231 return home;
232 }
233
234 static void _CFUpdateUserInfo(void) {
235 struct passwd *upwd;
236
237 __CFEUID = geteuid();
238 __CFUID = getuid();
239 if (__CFHomeDirectory) CFRelease(__CFHomeDirectory);
240 __CFHomeDirectory = NULL;
241 if (__CFUserName) CFRelease(__CFUserName);
242 __CFUserName = NULL;
243
244 upwd = getpwuid(__CFEUID ? __CFEUID : __CFUID);
245 __CFHomeDirectory = _CFCopyHomeDirURLForUser(upwd);
246 if (!__CFHomeDirectory) {
247 const char *cpath = __CFgetenv("HOME");
248 if (cpath) {
249 __CFHomeDirectory = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)cpath, strlen(cpath), true);
250 }
251 }
252
253 // This implies that UserManager stores directory info in CString
254 // rather than FileSystemRep. Perhaps this is wrong & we should
255 // expect NeXTSTEP encodings. A great test of our localized system would
256 // be to have a user "O-umlat z e r". XXX
257 if (upwd && upwd->pw_name) {
258 __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, upwd->pw_name, kCFPlatformInterfaceStringEncoding);
259 } else {
260 const char *cuser = __CFgetenv("USER");
261 if (cuser)
262 __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cuser, kCFPlatformInterfaceStringEncoding);
263 }
264 }
265 #endif
266
267 #if DEPLOYMENT_TARGET_WINDOWS
268 typedef DWORD (*NetUserGetInfoCall)(wchar_t *, wchar_t *, DWORD, char* *);
269 #endif
270
271 static CFURLRef _CFCreateHomeDirectoryURLForUser(CFStringRef uName) {
272 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
273 if (!uName) {
274 if (geteuid() != __CFEUID || getuid() != __CFUID || !__CFHomeDirectory)
275 _CFUpdateUserInfo();
276 if (__CFHomeDirectory) CFRetain(__CFHomeDirectory);
277 return __CFHomeDirectory;
278 } else {
279 struct passwd *upwd = NULL;
280 char buf[128], *user;
281 SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding);
282 CFIndex usedSize;
283 if (size < 127) {
284 user = buf;
285 } else {
286 user = CFAllocatorAllocate(kCFAllocatorSystemDefault, size+1, 0);
287 if (__CFOASafe) __CFSetLastAllocationEventName(user, "CFUtilities (temp)");
288 }
289 if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, (uint8_t *)user, size, &usedSize) == len) {
290 user[usedSize] = '\0';
291 upwd = getpwnam(user);
292 }
293 if (buf != user) {
294 CFAllocatorDeallocate(kCFAllocatorSystemDefault, user);
295 }
296 return _CFCopyHomeDirURLForUser(upwd);
297 }
298 #elif DEPLOYMENT_TARGET_WINDOWS
299 CFStringRef user = !uName ? _CFUserName() : uName;
300 CFURLRef retVal = NULL;
301 CFIndex len = 0;
302 CFStringRef str = NULL;
303
304 if (!uName || CFEqual(user, _CFUserName())) {
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 if (retVal == NULL) {
317 UniChar pathChars[MAX_PATH];
318 if (S_OK == SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, (wchar_t *) pathChars)) {
319 UniChar* p = pathChars;
320 while (*p++ != 0)
321 ++len;
322 str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathChars, len);
323 retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
324 } else {
325 // We have to get "some" directory location, so fall-back to the
326 // processes current directory.
327 UniChar currDir[MAX_PATH];
328 DWORD dwChars = GetCurrentDirectoryW(MAX_PATH + 1, (wchar_t *)currDir);
329 if (dwChars > 0) {
330 UniChar* p = currDir;
331 while (*p++ != 0)
332 ++len;
333 str = CFStringCreateWithCharacters(kCFAllocatorDefault, currDir, len);
334 retVal = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true);
335 }
336 CFRelease(str);
337 }
338 }
339 #if 0
340 char fullPath[CFMaxPathSize];
341 HRESULT hr = SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0 /* SHGFP_TYPE_CURRENT */, fullPath);
342 if (SUCCEEDED(hr)) {
343 CFStringRef str = CFStringCreateWithCString(NULL, fullPath, kCFPlatformInterfaceStringEncoding);
344 retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
345 CFRelease(str);
346 }
347 }
348 if (!retVal) {
349 struct _USER_INFO_2 *userInfo;
350 HINSTANCE hinstDll = GetModuleHandleA("NETAPI32");
351 if (!hinstDll)
352 hinstDll = LoadLibraryExA("NETAPI32", NULL, 0);
353 if (hinstDll) {
354 NetUserGetInfoCall lpfn = (NetUserGetInfoCall)GetProcAddress(hinstDll, "NetUserGetInfo");
355 if (lpfn) {
356 unsigned namelen = CFStringGetLength(user);
357 UniChar *username;
358 username = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (namelen + 1), 0);
359 CFStringGetCharacters(user, CFRangeMake(0, namelen), username);
360 if (!(*lpfn)(NULL, (wchar_t *)username, 2, (char * *)&userInfo)) {
361 UInt32 len = 0;
362 CFMutableStringRef str;
363 while (userInfo->usri2_home_dir[len] != 0) len ++;
364 str = CFStringCreateMutable(kCFAllocatorSystemDefault, len+1);
365 CFStringAppendCharacters(str, (const UniChar *)userInfo->usri2_home_dir, len);
366 retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true);
367 CFRelease(str);
368 }
369 CFAllocatorDeallocate(kCFAllocatorSystemDefault, username);
370 }
371 } else {
372 }
373 }
374 #endif
375
376 // We could do more here (as in KB Article Q101507). If that article is to
377 // be believed, we should only run into this case on Win95, or through
378 // user error.
379 #if DEPLOYMENT_TARGET_WINDOWS_SAFARI
380 if (retVal) {
381 CFStringRef str = CFURLCopyFileSystemPath(retVal, kCFURLWindowsPathStyle);
382 if (str && CFStringGetLength(str) == 0) {
383 CFRelease(retVal);
384 retVal=NULL;
385 }
386 if (str) CFRelease(str);
387 }
388 #else
389 CFStringRef testPath = CFURLCopyPath(retVal);
390 if (CFStringGetLength(testPath) == 0) {
391 CFRelease(retVal);
392 retVal=NULL;
393 }
394 if (testPath) {
395 CFRelease(testPath);
396 }
397 #endif
398 return retVal;
399
400 #else
401 #error Dont know how to compute users home directories on this platform
402 #endif
403 }
404
405 static CFStringRef _CFUserName(void) {
406 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD
407 if (geteuid() != __CFEUID || getuid() != __CFUID)
408 _CFUpdateUserInfo();
409 #elif DEPLOYMENT_TARGET_WINDOWS
410 if (!__CFUserName) {
411 wchar_t username[1040];
412 DWORD size = 1040;
413 username[0] = 0;
414 if (GetUserNameW(username, &size)) {
415 // discount the extra NULL by decrementing the size
416 __CFUserName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)username, size - 1);
417 } else {
418 const char *cname = __CFgetenv("USERNAME");
419 if (cname)
420 __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cname, kCFPlatformInterfaceStringEncoding);
421 }
422 }
423 #else
424 #error Dont know how to compute user name on this platform
425 #endif
426 if (!__CFUserName)
427 __CFUserName = (CFStringRef)CFRetain(CFSTR(""));
428 return __CFUserName;
429 }
430
431 __private_extern__ CFStringRef _CFGetUserName(void) {
432 return CFStringCreateCopy(kCFAllocatorSystemDefault, _CFUserName());
433 }
434
435 #define CFMaxHostNameLength 256
436 #define CFMaxHostNameSize (CFMaxHostNameLength+1)
437
438 __private_extern__ CFStringRef _CFStringCreateHostName(void) {
439 char myName[CFMaxHostNameSize];
440
441 // return @"" instead of nil a la CFUserName() and Ali Ozer
442 if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0';
443 return CFStringCreateWithCString(kCFAllocatorSystemDefault, myName, kCFPlatformInterfaceStringEncoding);
444 }
445
446 /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday.
447 These can return NULL.
448 */
449 CF_EXPORT CFStringRef CFGetUserName(void) {
450 return _CFUserName();
451 }
452
453 CF_EXPORT CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName) {
454 return _CFCreateHomeDirectoryURLForUser(uName);
455 }
456
457 #undef CFMaxHostNameLength
458 #undef CFMaxHostNameSize
459
460 #if DEPLOYMENT_TARGET_WINDOWS
461 CF_INLINE CFIndex strlen_UniChar(const UniChar* p) {
462 CFIndex result = 0;
463 while ((*p++) != 0)
464 ++result;
465 return result;
466 }
467
468 //#include <shfolder.h>
469 /*
470 * _CFCreateApplicationRepositoryPath returns the path to the application's
471 * repository in a CFMutableStringRef. The path returned will be:
472 * <nFolder_path>\Apple Computer\<bundle_name>\
473 * or if the bundle name cannot be obtained:
474 * <nFolder_path>\Apple Computer\
475 * where nFolder_path is obtained by calling SHGetFolderPath with nFolder
476 * (for example, with CSIDL_APPDATA or CSIDL_LOCAL_APPDATA).
477 *
478 * The CFMutableStringRef result must be released by the caller.
479 *
480 * If anything fails along the way, the result will be NULL.
481 */
482 CF_EXPORT CFMutableStringRef _CFCreateApplicationRepositoryPath(CFAllocatorRef alloc, int nFolder) {
483 CFMutableStringRef result = NULL;
484 UniChar szPath[MAX_PATH];
485
486 // get the current path to the data repository: CSIDL_APPDATA (roaming) or CSIDL_LOCAL_APPDATA (nonroaming)
487 if (S_OK == SHGetFolderPathW(NULL, nFolder, NULL, 0, (wchar_t *) szPath)) {
488 CFStringRef directoryPath;
489
490 // make it a CFString
491 directoryPath = CFStringCreateWithCharacters(alloc, szPath, strlen_UniChar(szPath));
492 if (directoryPath) {
493 CFBundleRef bundle;
494 CFStringRef bundleName;
495 CFStringRef completePath;
496
497 // attempt to get the bundle name
498 bundle = CFBundleGetMainBundle();
499 if (bundle) {
500 bundleName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey);
501 }
502 else {
503 bundleName = NULL;
504 }
505
506 if (bundleName) {
507 // the path will be "<directoryPath>\Apple Computer\<bundleName>\" if there is a bundle name
508 completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\%@\\"), directoryPath, bundleName);
509 }
510 else {
511 // or "<directoryPath>\Apple Computer\" if there is no bundle name.
512 completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\"), directoryPath);
513 }
514
515 CFRelease(directoryPath);
516
517 // make a mutable copy to return
518 if (completePath) {
519 result = CFStringCreateMutableCopy(alloc, 0, completePath);
520 CFRelease(completePath);
521 }
522 }
523 }
524
525 return ( result );
526 }
527 #endif
528
529 #if DEPLOYMENT_TARGET_WINDOWS
530 /* 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. */
531
532 #include <sys/stat.h>
533 #include <share.h>
534
535 // 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.
536 static wchar_t *createWideFileSystemRepresentation(const char *str, CFIndex *resultLen) {
537 // Get the real length of the string in UTF16 characters
538 CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8);
539 CFIndex strLen = CFStringGetLength(cfStr);
540
541 // Allocate a wide buffer to hold the converted string, including space for a NULL terminator
542 wchar_t *wideBuf = (wchar_t *)malloc((strLen + 1) * sizeof(wchar_t));
543
544 // Copy the string into the buffer and terminate
545 CFStringGetCharacters(cfStr, CFRangeMake(0, strLen), (UniChar *)wideBuf);
546 wideBuf[strLen] = 0;
547
548 CFRelease(cfStr);
549 if (resultLen) *resultLen = strLen;
550 return wideBuf;
551 }
552
553 // Copies a UTF16 buffer into a supplied UTF8 buffer.
554 static void copyToNarrowFileSystemRepresentation(const wchar_t *wide, CFIndex dstBufSize, char *dstbuf) {
555 // Get the real length of the wide string in UTF8 characters
556 CFStringRef cfStr = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)wide, wcslen(wide));
557 CFIndex strLen = CFStringGetLength(cfStr);
558 CFIndex bytesUsed;
559
560 // Copy the wide string into the buffer and terminate
561 CFStringGetBytes(cfStr, CFRangeMake(0, strLen), kCFStringEncodingUTF8, 0, false, (uint8_t *)dstbuf, dstBufSize, &bytesUsed);
562 dstbuf[bytesUsed] = 0;
563
564 CFRelease(cfStr);
565 }
566
567 CF_EXPORT int _NS_stat(const char *name, struct _stat *st) {
568 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
569 int res = _wstat(wide, st);
570 free(wide);
571 return res;
572 }
573
574 CF_EXPORT int _NS_mkdir(const char *name) {
575 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
576 int res = _wmkdir(wide);
577 free(wide);
578 return res;
579 }
580
581 CF_EXPORT int _NS_rmdir(const char *name) {
582 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
583 int res = _wrmdir(wide);
584 free(wide);
585 return res;
586 }
587
588 CF_EXPORT int _NS_chmod(const char *name, int mode) {
589 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
590 int res = _wchmod(wide, mode);
591 free(wide);
592 return res;
593 }
594
595 CF_EXPORT int _NS_unlink(const char *name) {
596 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
597 int res = _wunlink(wide);
598 free(wide);
599 return res;
600 }
601
602 // Warning: this doesn't support dstbuf as null even though 'getcwd' does
603 CF_EXPORT char *_NS_getcwd(char *dstbuf, size_t size) {
604 if (!dstbuf) {
605 CFLog(kCFLogLevelWarning, CFSTR("CFPlatform: getcwd called with null buffer"));
606 return 0;
607 }
608
609 wchar_t *buf = _wgetcwd(NULL, 0);
610 if (!buf) {
611 return NULL;
612 }
613
614 // Convert result to UTF8
615 copyToNarrowFileSystemRepresentation(buf, (CFIndex)size, dstbuf);
616 free(buf);
617 return dstbuf;
618 }
619
620 CF_EXPORT char *_NS_getenv(const char *name) {
621 // todo: wide env variables
622 return getenv(name);
623 }
624
625 CF_EXPORT int _NS_rename(const char *oldName, const char *newName) {
626 wchar_t *oldWide = createWideFileSystemRepresentation(oldName, NULL);
627 wchar_t *newWide = createWideFileSystemRepresentation(newName, NULL);
628 // _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
629 // To simulate the Mac OS behavior, we use the Win32 API then fill out errno if something goes wrong
630 BOOL winRes = MoveFileExW(oldWide, newWide, MOVEFILE_REPLACE_EXISTING);
631 DWORD error = GetLastError();
632 if (!winRes) {
633 switch (error) {
634 case ERROR_SUCCESS:
635 errno = 0;
636 break;
637 case ERROR_FILE_NOT_FOUND:
638 case ERROR_PATH_NOT_FOUND:
639 case ERROR_OPEN_FAILED:
640 errno = ENOENT;
641 break;
642 case ERROR_ACCESS_DENIED:
643 errno = EACCES;
644 break;
645 default:
646 errno = error;
647 }
648 }
649 free(oldWide);
650 free(newWide);
651 return (winRes ? 0 : -1);
652 }
653
654 CF_EXPORT int _NS_open(const char *name, int oflag, int pmode) {
655 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
656 int fd;
657 _wsopen_s(&fd, wide, oflag, _SH_DENYNO, _S_IREAD | _S_IWRITE);
658 free(wide);
659 return fd;
660 }
661
662 CF_EXPORT int _NS_chdir(const char *name) {
663 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
664 int res = _wchdir(wide);
665 free(wide);
666 return res;
667 }
668
669 CF_EXPORT int _NS_access(const char *name, int amode) {
670 // execute is always true
671 if (amode == 1) return 0;
672
673 wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
674 // we only care about the read-only (04) and write-only (02) bits, so mask octal 06
675 int res = _waccess(wide, amode & 06);
676 free(wide);
677 return res;
678 }
679
680 // This is a bit different than the standard 'mkstemp', because the size parameter is needed so we know the size of the UTF8 buffer
681 // Also, we don't avoid the race between creating a temporary file name and opening it on Windows like we do on Mac
682 CF_EXPORT int _NS_mkstemp(char *name, int bufSize) {
683 CFIndex nameLen;
684 wchar_t *wide = createWideFileSystemRepresentation(name, &nameLen);
685
686 // 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.
687 // Look for the last '\' in the path
688 wchar_t *lastSlash = wcsrchr(wide, '\\');
689 if (!lastSlash) {
690 free(wide);
691 return -1;
692 }
693
694 // Set the last slash to NULL temporarily and use it for _wstat
695 *lastSlash = 0;
696 struct _stat dirInfo;
697 int res = _wstat(wide, &dirInfo);
698 if (res < 0) {
699 if (errno == ENOENT) {
700 errno = ENOTDIR;
701 }
702 free(wide);
703 return -1;
704 }
705 // Restore the last slash
706 *lastSlash = '\\';
707
708 errno_t err = _wmktemp_s(wide, nameLen + 1);
709 if (err != 0) {
710 free(wide);
711 return 0;
712 }
713
714 int fd;
715 _wsopen_s(&fd, wide, _O_RDWR | _O_CREAT | CF_OPENFLGS, _SH_DENYNO, _S_IREAD | _S_IWRITE);
716
717 // Convert the wide name back into the UTF8 buffer the caller supplied
718 copyToNarrowFileSystemRepresentation(wide, bufSize, name);
719 free(wide);
720 return fd;
721 }
722
723 #endif
724