/* * Copyright (c) 2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* CFPlatform.c Copyright (c) 1999-2009, Apple Inc. All rights reserved. Responsibility: Christopher Kane */ #include "CFInternal.h" #include #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED #include #include #include #include #include #include #include #include #endif #if DEPLOYMENT_TARGET_WINDOWS #include #include #define getcwd _NS_getcwd #endif #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS_SYNC #define kCFPlatformInterfaceStringEncoding kCFStringEncodingUTF8 #else #define kCFPlatformInterfaceStringEncoding CFStringGetSystemEncoding() #endif static CFStringRef _CFUserName(void); #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED // CoreGraphics and LaunchServices are only projects (1 Dec 2006) that use these char **_CFArgv(void) { return *_NSGetArgv(); } int _CFArgc(void) { return *_NSGetArgc(); } #endif __private_extern__ Boolean _CFGetCurrentDirectory(char *path, int maxlen) { return getcwd(path, maxlen) != NULL; } static Boolean __CFIsCFM = false; // If called super early, we just return false __private_extern__ Boolean _CFIsCFM(void) { return __CFIsCFM; } #if DEPLOYMENT_TARGET_WINDOWS #define PATH_SEP '\\' #else #define PATH_SEP '/' #endif #if DEPLOYMENT_TARGET_WINDOWS // Returns the path to the CF DLL, which we can then use to find resources like char sets bool bDllPathCached = false; __private_extern__ const wchar_t *_CFDLLPath(void) { static wchar_t cachedPath[MAX_PATH+1]; if (!bDllPathCached) { #ifdef _DEBUG // might be nice to get this from the project file at some point wchar_t *DLLFileName = L"CoreFoundation_debug.dll"; #else wchar_t *DLLFileName = L"CoreFoundation.dll"; #endif HMODULE ourModule = GetModuleHandleW(DLLFileName); CFAssert(ourModule, __kCFLogAssertion, "GetModuleHandle failed"); DWORD wResult = GetModuleFileNameW(ourModule, cachedPath, MAX_PATH+1); CFAssert1(wResult > 0, __kCFLogAssertion, "GetModuleFileName failed: %d", GetLastError()); CFAssert1(wResult < MAX_PATH+1, __kCFLogAssertion, "GetModuleFileName result truncated: %s", cachedPath); // strip off last component, the DLL name CFIndex idx; for (idx = wResult - 1; idx; idx--) { if ('\\' == cachedPath[idx]) { cachedPath[idx] = '\0'; break; } } bDllPathCached = true; } return cachedPath; } #endif static const char *__CFProcessPath = NULL; static const char *__CFprogname = NULL; const char **_CFGetProgname(void) { if (!__CFprogname) _CFProcessPath(); // sets up __CFprogname as a side-effect return &__CFprogname; } const char **_CFGetProcessPath(void) { if (!__CFProcessPath) _CFProcessPath(); // sets up __CFProcessPath as a side-effect return &__CFProcessPath; } #if DEPLOYMENT_TARGET_WINDOWS const char *_CFProcessPath(void) { if (__CFProcessPath) return __CFProcessPath; wchar_t buf[CFMaxPathSize] = {0}; DWORD rlen = GetModuleFileNameW(NULL, buf, sizeof(buf) / sizeof(buf[0])); if (0 < rlen) { char asciiBuf[CFMaxPathSize] = {0}; int res = WideCharToMultiByte(CP_UTF8, 0, buf, rlen, asciiBuf, sizeof(asciiBuf) / sizeof(asciiBuf[0]), NULL, NULL); if (0 < res) { __CFProcessPath = strdup(asciiBuf); __CFprogname = strrchr(__CFProcessPath, PATH_SEP); __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath); } } if (!__CFProcessPath) { __CFProcessPath = ""; __CFprogname = __CFProcessPath; } return __CFProcessPath; } #endif #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED const char *_CFProcessPath(void) { if (__CFProcessPath) return __CFProcessPath; #if DEPLOYMENT_TARGET_MACOSX if (!issetugid()) { const char *path = (char *)__CFgetenv("CFProcessPath"); if (path) { __CFProcessPath = strdup(path); __CFprogname = strrchr(__CFProcessPath, PATH_SEP); __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath); return __CFProcessPath; } } #endif uint32_t size = CFMaxPathSize; char buffer[size]; if (0 == _NSGetExecutablePath(buffer, &size)) { #if DEPLOYMENT_TARGET_MACOSX && defined(__ppc__) size_t len = strlen(buffer); if (12 <= len && 0 == strcmp("LaunchCFMApp", buffer + len - 12)) { struct stat exec, lcfm; const char *launchcfm = "/System/Library/Frameworks/Carbon.framework/Versions/Current/Support/LaunchCFMApp"; if (0 == stat(launchcfm, &lcfm) && 0 == stat(buffer, &exec) && (lcfm.st_dev == exec.st_dev) && (lcfm.st_ino == exec.st_ino)) { // Executable is LaunchCFMApp, take special action __CFIsCFM = true; if ((*_NSGetArgv())[1] && '/' == *((*_NSGetArgv())[1])) { strlcpy(buffer, (*_NSGetArgv())[1], sizeof(buffer)); } } } #endif __CFProcessPath = strdup(buffer); __CFprogname = strrchr(__CFProcessPath, PATH_SEP); __CFprogname = (__CFprogname ? __CFprogname + 1 : __CFProcessPath); } if (!__CFProcessPath) { __CFProcessPath = ""; __CFprogname = __CFProcessPath; } return __CFProcessPath; } #endif __private_extern__ CFStringRef _CFProcessNameString(void) { static CFStringRef __CFProcessNameString = NULL; if (!__CFProcessNameString) { const char *processName = *_CFGetProgname(); if (!processName) processName = ""; CFStringRef newStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, processName, kCFPlatformInterfaceStringEncoding); if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *) newStr, (void * volatile *)& __CFProcessNameString)) { CFRelease(newStr); // someone else made the assignment, so just release the extra string. } } return __CFProcessNameString; } static CFStringRef __CFUserName = NULL; #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD static CFURLRef __CFHomeDirectory = NULL; static uint32_t __CFEUID = -1; static uint32_t __CFUID = -1; static CFURLRef _CFCopyHomeDirURLForUser(struct passwd *upwd) { CFURLRef home = NULL; if (!issetugid()) { const char *path = __CFgetenv("CFFIXED_USER_HOME"); if (path) { home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)path, strlen(path), true); } } if (!home) { if (upwd && upwd->pw_dir) { home = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)upwd->pw_dir, strlen(upwd->pw_dir), true); } } return home; } static void _CFUpdateUserInfo(void) { struct passwd *upwd; __CFEUID = geteuid(); __CFUID = getuid(); if (__CFHomeDirectory) CFRelease(__CFHomeDirectory); __CFHomeDirectory = NULL; if (__CFUserName) CFRelease(__CFUserName); __CFUserName = NULL; upwd = getpwuid(__CFEUID ? __CFEUID : __CFUID); __CFHomeDirectory = _CFCopyHomeDirURLForUser(upwd); if (!__CFHomeDirectory) { const char *cpath = __CFgetenv("HOME"); if (cpath) { __CFHomeDirectory = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)cpath, strlen(cpath), true); } } // This implies that UserManager stores directory info in CString // rather than FileSystemRep. Perhaps this is wrong & we should // expect NeXTSTEP encodings. A great test of our localized system would // be to have a user "O-umlat z e r". XXX if (upwd && upwd->pw_name) { __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, upwd->pw_name, kCFPlatformInterfaceStringEncoding); } else { const char *cuser = __CFgetenv("USER"); if (cuser) __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cuser, kCFPlatformInterfaceStringEncoding); } } #endif #if DEPLOYMENT_TARGET_WINDOWS typedef DWORD (*NetUserGetInfoCall)(wchar_t *, wchar_t *, DWORD, char* *); #endif static CFURLRef _CFCreateHomeDirectoryURLForUser(CFStringRef uName) { #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD if (!uName) { if (geteuid() != __CFEUID || getuid() != __CFUID || !__CFHomeDirectory) _CFUpdateUserInfo(); if (__CFHomeDirectory) CFRetain(__CFHomeDirectory); return __CFHomeDirectory; } else { struct passwd *upwd = NULL; char buf[128], *user; SInt32 len = CFStringGetLength(uName), size = CFStringGetMaximumSizeForEncoding(len, kCFPlatformInterfaceStringEncoding); CFIndex usedSize; if (size < 127) { user = buf; } else { user = CFAllocatorAllocate(kCFAllocatorSystemDefault, size+1, 0); if (__CFOASafe) __CFSetLastAllocationEventName(user, "CFUtilities (temp)"); } if (CFStringGetBytes(uName, CFRangeMake(0, len), kCFPlatformInterfaceStringEncoding, 0, true, (uint8_t *)user, size, &usedSize) == len) { user[usedSize] = '\0'; upwd = getpwnam(user); } if (buf != user) { CFAllocatorDeallocate(kCFAllocatorSystemDefault, user); } return _CFCopyHomeDirURLForUser(upwd); } #elif DEPLOYMENT_TARGET_WINDOWS CFStringRef user = !uName ? _CFUserName() : uName; CFURLRef retVal = NULL; CFIndex len = 0; CFStringRef str = NULL; if (!uName || CFEqual(user, _CFUserName())) { const char *cpath = __CFgetenv("HOMEPATH"); const char *cdrive = __CFgetenv("HOMEDRIVE"); if (cdrive && cpath) { char fullPath[CFMaxPathSize]; strlcpy(fullPath, cdrive, sizeof(fullPath)); strlcat(fullPath, cpath, sizeof(fullPath)); str = CFStringCreateWithCString(kCFAllocatorSystemDefault, fullPath, kCFPlatformInterfaceStringEncoding); retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true); CFRelease(str); } } if (retVal == NULL) { UniChar pathChars[MAX_PATH]; if (S_OK == SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, (wchar_t *) pathChars)) { UniChar* p = pathChars; while (*p++ != 0) ++len; str = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, pathChars, len); retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true); } else { // We have to get "some" directory location, so fall-back to the // processes current directory. UniChar currDir[MAX_PATH]; DWORD dwChars = GetCurrentDirectoryW(MAX_PATH + 1, (wchar_t *)currDir); if (dwChars > 0) { UniChar* p = currDir; while (*p++ != 0) ++len; str = CFStringCreateWithCharacters(kCFAllocatorDefault, currDir, len); retVal = CFURLCreateWithFileSystemPath(NULL, str, kCFURLWindowsPathStyle, true); } CFRelease(str); } } #if 0 char fullPath[CFMaxPathSize]; HRESULT hr = SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, 0 /* SHGFP_TYPE_CURRENT */, fullPath); if (SUCCEEDED(hr)) { CFStringRef str = CFStringCreateWithCString(NULL, fullPath, kCFPlatformInterfaceStringEncoding); retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true); CFRelease(str); } } if (!retVal) { struct _USER_INFO_2 *userInfo; HINSTANCE hinstDll = GetModuleHandleA("NETAPI32"); if (!hinstDll) hinstDll = LoadLibraryExA("NETAPI32", NULL, 0); if (hinstDll) { NetUserGetInfoCall lpfn = (NetUserGetInfoCall)GetProcAddress(hinstDll, "NetUserGetInfo"); if (lpfn) { unsigned namelen = CFStringGetLength(user); UniChar *username; username = (UniChar *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(UniChar) * (namelen + 1), 0); CFStringGetCharacters(user, CFRangeMake(0, namelen), username); if (!(*lpfn)(NULL, (wchar_t *)username, 2, (char * *)&userInfo)) { UInt32 len = 0; CFMutableStringRef str; while (userInfo->usri2_home_dir[len] != 0) len ++; str = CFStringCreateMutable(kCFAllocatorSystemDefault, len+1); CFStringAppendCharacters(str, (const UniChar *)userInfo->usri2_home_dir, len); retVal = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, kCFURLWindowsPathStyle, true); CFRelease(str); } CFAllocatorDeallocate(kCFAllocatorSystemDefault, username); } } else { } } #endif // 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. #if DEPLOYMENT_TARGET_WINDOWS_SAFARI if (retVal) { CFStringRef str = CFURLCopyFileSystemPath(retVal, kCFURLWindowsPathStyle); if (str && CFStringGetLength(str) == 0) { CFRelease(retVal); retVal=NULL; } if (str) CFRelease(str); } #else CFStringRef testPath = CFURLCopyPath(retVal); if (CFStringGetLength(testPath) == 0) { CFRelease(retVal); retVal=NULL; } if (testPath) { CFRelease(testPath); } #endif return retVal; #else #error Dont know how to compute users home directories on this platform #endif } static CFStringRef _CFUserName(void) { #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD if (geteuid() != __CFEUID || getuid() != __CFUID) _CFUpdateUserInfo(); #elif DEPLOYMENT_TARGET_WINDOWS if (!__CFUserName) { wchar_t username[1040]; DWORD size = 1040; username[0] = 0; if (GetUserNameW(username, &size)) { // discount the extra NULL by decrementing the size __CFUserName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)username, size - 1); } else { const char *cname = __CFgetenv("USERNAME"); if (cname) __CFUserName = CFStringCreateWithCString(kCFAllocatorSystemDefault, cname, kCFPlatformInterfaceStringEncoding); } } #else #error Dont know how to compute user name on this platform #endif if (!__CFUserName) __CFUserName = (CFStringRef)CFRetain(CFSTR("")); return __CFUserName; } __private_extern__ CFStringRef _CFGetUserName(void) { return CFStringCreateCopy(kCFAllocatorSystemDefault, _CFUserName()); } #define CFMaxHostNameLength 256 #define CFMaxHostNameSize (CFMaxHostNameLength+1) __private_extern__ CFStringRef _CFStringCreateHostName(void) { char myName[CFMaxHostNameSize]; // return @"" instead of nil a la CFUserName() and Ali Ozer if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0'; return CFStringCreateWithCString(kCFAllocatorSystemDefault, myName, kCFPlatformInterfaceStringEncoding); } /* These are sanitized versions of the above functions. We might want to eliminate the above ones someday. These can return NULL. */ CF_EXPORT CFStringRef CFGetUserName(void) { return _CFUserName(); } CF_EXPORT CFURLRef CFCopyHomeDirectoryURLForUser(CFStringRef uName) { return _CFCreateHomeDirectoryURLForUser(uName); } #undef CFMaxHostNameLength #undef CFMaxHostNameSize #if DEPLOYMENT_TARGET_WINDOWS CF_INLINE CFIndex strlen_UniChar(const UniChar* p) { CFIndex result = 0; while ((*p++) != 0) ++result; return result; } //#include /* * _CFCreateApplicationRepositoryPath returns the path to the application's * repository in a CFMutableStringRef. The path returned will be: * \Apple Computer\\ * or if the bundle name cannot be obtained: * \Apple Computer\ * where nFolder_path is obtained by calling SHGetFolderPath with nFolder * (for example, with CSIDL_APPDATA or CSIDL_LOCAL_APPDATA). * * The CFMutableStringRef result must be released by the caller. * * If anything fails along the way, the result will be NULL. */ CF_EXPORT CFMutableStringRef _CFCreateApplicationRepositoryPath(CFAllocatorRef alloc, int nFolder) { CFMutableStringRef result = NULL; UniChar szPath[MAX_PATH]; // get the current path to the data repository: CSIDL_APPDATA (roaming) or CSIDL_LOCAL_APPDATA (nonroaming) if (S_OK == SHGetFolderPathW(NULL, nFolder, NULL, 0, (wchar_t *) szPath)) { CFStringRef directoryPath; // make it a CFString directoryPath = CFStringCreateWithCharacters(alloc, szPath, strlen_UniChar(szPath)); if (directoryPath) { CFBundleRef bundle; CFStringRef bundleName; CFStringRef completePath; // attempt to get the bundle name bundle = CFBundleGetMainBundle(); if (bundle) { bundleName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey); } else { bundleName = NULL; } if (bundleName) { // the path will be "\Apple Computer\\" if there is a bundle name completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\%@\\"), directoryPath, bundleName); } else { // or "\Apple Computer\" if there is no bundle name. completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\"), directoryPath); } CFRelease(directoryPath); // make a mutable copy to return if (completePath) { result = CFStringCreateMutableCopy(alloc, 0, completePath); CFRelease(completePath); } } } return ( result ); } #endif #if DEPLOYMENT_TARGET_WINDOWS /* 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. */ #include #include // 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. static wchar_t *createWideFileSystemRepresentation(const char *str, CFIndex *resultLen) { // Get the real length of the string in UTF16 characters CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8); CFIndex strLen = CFStringGetLength(cfStr); // Allocate a wide buffer to hold the converted string, including space for a NULL terminator wchar_t *wideBuf = (wchar_t *)malloc((strLen + 1) * sizeof(wchar_t)); // Copy the string into the buffer and terminate CFStringGetCharacters(cfStr, CFRangeMake(0, strLen), (UniChar *)wideBuf); wideBuf[strLen] = 0; CFRelease(cfStr); if (resultLen) *resultLen = strLen; return wideBuf; } // Copies a UTF16 buffer into a supplied UTF8 buffer. static void copyToNarrowFileSystemRepresentation(const wchar_t *wide, CFIndex dstBufSize, char *dstbuf) { // Get the real length of the wide string in UTF8 characters CFStringRef cfStr = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)wide, wcslen(wide)); CFIndex strLen = CFStringGetLength(cfStr); CFIndex bytesUsed; // Copy the wide string into the buffer and terminate CFStringGetBytes(cfStr, CFRangeMake(0, strLen), kCFStringEncodingUTF8, 0, false, (uint8_t *)dstbuf, dstBufSize, &bytesUsed); dstbuf[bytesUsed] = 0; CFRelease(cfStr); } CF_EXPORT int _NS_stat(const char *name, struct _stat *st) { wchar_t *wide = createWideFileSystemRepresentation(name, NULL); int res = _wstat(wide, st); free(wide); return res; } CF_EXPORT int _NS_mkdir(const char *name) { wchar_t *wide = createWideFileSystemRepresentation(name, NULL); int res = _wmkdir(wide); free(wide); return res; } CF_EXPORT int _NS_rmdir(const char *name) { wchar_t *wide = createWideFileSystemRepresentation(name, NULL); int res = _wrmdir(wide); free(wide); return res; } CF_EXPORT int _NS_chmod(const char *name, int mode) { wchar_t *wide = createWideFileSystemRepresentation(name, NULL); int res = _wchmod(wide, mode); free(wide); return res; } CF_EXPORT int _NS_unlink(const char *name) { wchar_t *wide = createWideFileSystemRepresentation(name, NULL); int res = _wunlink(wide); free(wide); return res; } // Warning: this doesn't support dstbuf as null even though 'getcwd' does CF_EXPORT char *_NS_getcwd(char *dstbuf, size_t size) { if (!dstbuf) { CFLog(kCFLogLevelWarning, CFSTR("CFPlatform: getcwd called with null buffer")); return 0; } wchar_t *buf = _wgetcwd(NULL, 0); if (!buf) { return NULL; } // Convert result to UTF8 copyToNarrowFileSystemRepresentation(buf, (CFIndex)size, dstbuf); free(buf); return dstbuf; } CF_EXPORT char *_NS_getenv(const char *name) { // todo: wide env variables return getenv(name); } CF_EXPORT int _NS_rename(const char *oldName, const char *newName) { wchar_t *oldWide = createWideFileSystemRepresentation(oldName, NULL); wchar_t *newWide = createWideFileSystemRepresentation(newName, NULL); // _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 // To simulate the Mac OS behavior, we use the Win32 API then fill out errno if something goes wrong BOOL winRes = MoveFileExW(oldWide, newWide, MOVEFILE_REPLACE_EXISTING); DWORD error = GetLastError(); if (!winRes) { switch (error) { case ERROR_SUCCESS: errno = 0; break; case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: case ERROR_OPEN_FAILED: errno = ENOENT; break; case ERROR_ACCESS_DENIED: errno = EACCES; break; default: errno = error; } } free(oldWide); free(newWide); return (winRes ? 0 : -1); } CF_EXPORT int _NS_open(const char *name, int oflag, int pmode) { wchar_t *wide = createWideFileSystemRepresentation(name, NULL); int fd; _wsopen_s(&fd, wide, oflag, _SH_DENYNO, _S_IREAD | _S_IWRITE); free(wide); return fd; } CF_EXPORT int _NS_chdir(const char *name) { wchar_t *wide = createWideFileSystemRepresentation(name, NULL); int res = _wchdir(wide); free(wide); return res; } CF_EXPORT int _NS_access(const char *name, int amode) { // execute is always true if (amode == 1) return 0; wchar_t *wide = createWideFileSystemRepresentation(name, NULL); // we only care about the read-only (04) and write-only (02) bits, so mask octal 06 int res = _waccess(wide, amode & 06); free(wide); return res; } // This is a bit different than the standard 'mkstemp', because the size parameter is needed so we know the size of the UTF8 buffer // Also, we don't avoid the race between creating a temporary file name and opening it on Windows like we do on Mac CF_EXPORT int _NS_mkstemp(char *name, int bufSize) { CFIndex nameLen; wchar_t *wide = createWideFileSystemRepresentation(name, &nameLen); // 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. // Look for the last '\' in the path wchar_t *lastSlash = wcsrchr(wide, '\\'); if (!lastSlash) { free(wide); return -1; } // Set the last slash to NULL temporarily and use it for _wstat *lastSlash = 0; struct _stat dirInfo; int res = _wstat(wide, &dirInfo); if (res < 0) { if (errno == ENOENT) { errno = ENOTDIR; } free(wide); return -1; } // Restore the last slash *lastSlash = '\\'; errno_t err = _wmktemp_s(wide, nameLen + 1); if (err != 0) { free(wide); return 0; } int fd; _wsopen_s(&fd, wide, _O_RDWR | _O_CREAT | CF_OPENFLGS, _SH_DENYNO, _S_IREAD | _S_IWRITE); // Convert the wide name back into the UTF8 buffer the caller supplied copyToNarrowFileSystemRepresentation(wide, bufSize, name); free(wide); return fd; } #endif