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