#include "CFInternal.h"
#include <CoreFoundation/CFPriv.h>
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
#include <stdlib.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <crt_externs.h>
#include <mach-o/dyld.h>
#endif
#if DEPLOYMENT_TARGET_WINDOWS
#include <shellapi.h>
#include <shlobj.h>
#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
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;
__private_extern__ Boolean _CFIsCFM(void) {
return __CFIsCFM;
}
#if DEPLOYMENT_TARGET_WINDOWS
#define PATH_SEP '\\'
#else
#define PATH_SEP '/'
#endif
#if DEPLOYMENT_TARGET_WINDOWS
bool bDllPathCached = false;
__private_extern__ const wchar_t *_CFDLLPath(void) {
static wchar_t cachedPath[MAX_PATH+1];
if (!bDllPathCached) {
#ifdef _DEBUG
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);
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(); return &__CFprogname;
}
const char **_CFGetProcessPath(void) {
if (!__CFProcessPath)
_CFProcessPath(); 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)) {
__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); }
}
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);
}
}
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 {
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 , 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
#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)) {
__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];
if (0 != gethostname(myName, CFMaxHostNameSize)) myName[0] = '\0';
return CFStringCreateWithCString(kCFAllocatorSystemDefault, myName, kCFPlatformInterfaceStringEncoding);
}
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;
}
CF_EXPORT CFMutableStringRef _CFCreateApplicationRepositoryPath(CFAllocatorRef alloc, int nFolder) {
CFMutableStringRef result = NULL;
UniChar szPath[MAX_PATH];
if (S_OK == SHGetFolderPathW(NULL, nFolder, NULL, 0, (wchar_t *) szPath)) {
CFStringRef directoryPath;
directoryPath = CFStringCreateWithCharacters(alloc, szPath, strlen_UniChar(szPath));
if (directoryPath) {
CFBundleRef bundle;
CFStringRef bundleName;
CFStringRef completePath;
bundle = CFBundleGetMainBundle();
if (bundle) {
bundleName = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey);
}
else {
bundleName = NULL;
}
if (bundleName) {
completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\%@\\"), directoryPath, bundleName);
}
else {
completePath = CFStringCreateWithFormat(alloc, NULL, CFSTR("%@\\Apple Computer\\"), directoryPath);
}
CFRelease(directoryPath);
if (completePath) {
result = CFStringCreateMutableCopy(alloc, 0, completePath);
CFRelease(completePath);
}
}
}
return ( result );
}
#endif
#if DEPLOYMENT_TARGET_WINDOWS
#include <sys/stat.h>
#include <share.h>
static wchar_t *createWideFileSystemRepresentation(const char *str, CFIndex *resultLen) {
CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorSystemDefault, str, kCFStringEncodingUTF8);
CFIndex strLen = CFStringGetLength(cfStr);
wchar_t *wideBuf = (wchar_t *)malloc((strLen + 1) * sizeof(wchar_t));
CFStringGetCharacters(cfStr, CFRangeMake(0, strLen), (UniChar *)wideBuf);
wideBuf[strLen] = 0;
CFRelease(cfStr);
if (resultLen) *resultLen = strLen;
return wideBuf;
}
static void copyToNarrowFileSystemRepresentation(const wchar_t *wide, CFIndex dstBufSize, char *dstbuf) {
CFStringRef cfStr = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, (const UniChar *)wide, wcslen(wide));
CFIndex strLen = CFStringGetLength(cfStr);
CFIndex bytesUsed;
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;
}
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;
}
copyToNarrowFileSystemRepresentation(buf, (CFIndex)size, dstbuf);
free(buf);
return dstbuf;
}
CF_EXPORT char *_NS_getenv(const char *name) {
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);
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) {
if (amode == 1) return 0;
wchar_t *wide = createWideFileSystemRepresentation(name, NULL);
int res = _waccess(wide, amode & 06);
free(wide);
return res;
}
CF_EXPORT int _NS_mkstemp(char *name, int bufSize) {
CFIndex nameLen;
wchar_t *wide = createWideFileSystemRepresentation(name, &nameLen);
wchar_t *lastSlash = wcsrchr(wide, '\\');
if (!lastSlash) {
free(wide);
return -1;
}
*lastSlash = 0;
struct _stat dirInfo;
int res = _wstat(wide, &dirInfo);
if (res < 0) {
if (errno == ENOENT) {
errno = ENOTDIR;
}
free(wide);
return -1;
}
*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);
copyToNarrowFileSystemRepresentation(wide, bufSize, name);
free(wide);
return fd;
}
#endif