/** * @file kDirectory.cpp * * @internal * Copyright (C) 2008-2021 by LMI Technologies Inc. * Licensed under the MIT License. * Redistributed files must retain the above copyright notice. */ #define K_PLATFORM #include #include #include #include #include #include #include kBeginStaticClassEx(k, kDirectory) kEndStaticClassEx() kFx(kStatus) xkDirectory_SetDefaultHandlers() { kApiDirectoryFx handlers = { kNULL }; handlers.create = xkDirectory_CreateImpl; handlers.exists = xkDirectory_ExistsImpl; handlers.move= xkDirectory_MoveImpl; handlers.del= xkDirectory_DeleteImpl; handlers.enumerate = xkDirectory_EnumerateImpl; handlers.setCurrent = xkDirectory_SetCurrentImpl; handlers.current = xkDirectory_CurrentImpl; handlers.appDirectory = xkDirectory_ApplicationImpl; handlers.apiDirectory = xkDirectory_ApiImpl; handlers.tempDirectory = xkDirectory_TempImpl; handlers.appConfigDirectory = xkDirectory_AppConfigImpl; handlers.appDataDirectory = xkDirectory_AppDataImpl; handlers.toVirtual = xkDirectory_ToVirtualImpl; handlers.fromVirtual = xkDirectory_FromVirtualImpl; //respect handlers that have already been installed if (kApiLib_HasDirectoryHandlers()) { kCheck(xkOverrideFunctions(&handlers, sizeof(handlers), kApiLib_DirectoryHandlers())); } kCheck(kApiLib_SetDirectoryHandlers(&handlers)); return kOK; } kFx(kStatus) xkDirectory_InitStatic() { return xkDirectory_SetDefaultHandlers(); } kFx(kStatus) xkDirectory_ReleaseStatic() { return kOK; } kFx(kStatus) kDirectory_Create(const kChar* directory) { kChar parent[kPATH_MAX]; //create any missing parent directories while (kSuccess(kPath_Directory(directory, parent, kCountOf(parent))) && !kDirectory_Exists(parent)) { kCheck(kDirectory_Create(parent)); } //create the requested directory return kApiLib_DirectoryHandlers()->create(directory); } kFx(kBool) kDirectory_Exists(const kChar* directory) { return kApiLib_DirectoryHandlers()->exists(directory); } kFx(kStatus) kDirectory_Copy(const kChar* source, const kChar* destination) { kSize i; kArrayList files = kNULL; kArrayList directories = kNULL; kString srcItem = kNULL; kChar srcPath[kPATH_MAX]; kChar destPath[kPATH_MAX]; if (kStrEquals(source, destination)) { return kOK; } kCheckArgs(kDirectory_Exists(source)); //create destination root folder kCheck(kDirectory_Create(destination)); kTry { //copy files kTest(kArrayList_Construct(&files, kNULL, 0, kNULL)); kTest(kDirectory_ListFiles(source, files)); for (i = 0; i < kArrayList_Count(files); ++i) { kTest(kArrayList_ItemT(files, i, &srcItem)); kTest(kPath_Combine(source, kString_Chars(srcItem), srcPath, kCountOf(srcPath))); kTest(kPath_Combine(destination, kString_Chars(srcItem), destPath, kCountOf(destPath))); kTest(kFile_Copy(srcPath, destPath)); } //copy directories kTest(kArrayList_Construct(&directories, kNULL, 0, kNULL)); kTest(kDirectory_ListDirectories(source, directories)); for (i = 0; i < kArrayList_Count(directories); ++i) { kTest(kArrayList_ItemT(directories, i, &srcItem)); kTest(kPath_Combine(source, kString_Chars(srcItem), srcPath, kCountOf(srcPath))); kTest(kPath_Combine(destination, kString_Chars(srcItem), destPath, kCountOf(destPath))); kTest(kDirectory_Copy(srcPath, destPath)); } } kFinally { kCheck(kObject_Dispose(files)); kCheck(kObject_Dispose(directories)); kEndFinally(); } return kOK; } kFx(kStatus) kDirectory_Move(const kChar* source, const kChar* destination) { return kApiLib_DirectoryHandlers()->move(source, destination); } kFx(kStatus) kDirectory_Delete(const kChar* directory) { kCheckTrue(kDirectory_Exists(directory), kERROR_NOT_FOUND); kCheck(kDirectory_DeleteEntries(directory)); //delete the target directory kCheck(kApiLib_DirectoryHandlers()->del(directory)); return kOK; } kFx(kStatus) kDirectory_DeleteEntries(const kChar* directory) { kSize i; kArrayList files = kNULL; kArrayList directories = kNULL; kString srcItem = kNULL; kChar srcPath[kPATH_MAX]; kCheckTrue(kDirectory_Exists(directory), kERROR_NOT_FOUND); kTry { //delete files kTest(kArrayList_Construct(&files, kNULL, 0, kNULL)); kTest(kDirectory_ListFiles(directory, files)); for (i = 0; i < kArrayList_Count(files); ++i) { kTest(kArrayList_ItemT(files, i, &srcItem)); kTest(kPath_Combine(directory, kString_Chars(srcItem), srcPath, kCountOf(srcPath))); kTest(kFile_Delete(srcPath)); } //delete sub-directories kTest(kArrayList_Construct(&directories, kNULL, 0, kNULL)); kTest(kDirectory_ListDirectories(directory, directories)); for (i = 0; i < kArrayList_Count(directories); ++i) { kTest(kArrayList_ItemT(directories, i, &srcItem)); kTest(kPath_Combine(directory, kString_Chars(srcItem), srcPath, kCountOf(srcPath))); kTest(kDirectory_Delete(srcPath)); } } kFinally { kCheck(kObject_Dispose(files)); kCheck(kObject_Dispose(directories)); kEndFinally(); } return kOK; } kFx(kStatus) kDirectory_Enumerate(const kChar* directory, kBool includeFiles, kBool includeDirectories, kArrayList entries) { kCheck(kArrayList_Purge(entries)); return kApiLib_DirectoryHandlers()->enumerate(directory, includeFiles, includeDirectories, entries); } kFx(kStatus) kDirectory_ListFiles(const kChar* directory, kArrayList files) { kCheck(kArrayList_Purge(files)); return kApiLib_DirectoryHandlers()->enumerate(directory, kTRUE, kFALSE, files); } kFx(kStatus) kDirectory_ListDirectories(const kChar* directory, kArrayList directories) { kCheck(kArrayList_Purge(directories)); return kApiLib_DirectoryHandlers()->enumerate(directory, kFALSE, kTRUE, directories); } kFx(kStatus) kDirectory_ListEntries(const kChar* directory, kArrayList entries) { kCheck(kArrayList_Purge(entries)); return kApiLib_DirectoryHandlers()->enumerate(directory, kTRUE, kTRUE, entries); } kFx(kStatus) kDirectory_SetCurrent(const kChar* directory) { return kApiLib_DirectoryHandlers()->setCurrent(directory); } kFx(kStatus) kDirectory_Current(kChar* directory, kSize capacity) { return kApiLib_DirectoryHandlers()->current(directory, capacity); } kFx(kStatus) kDirectory_Application(kChar* directory, kSize capacity) { return kApiLib_DirectoryHandlers()->appDirectory(directory, capacity); } kFx(kStatus) kDirectory_Api(kChar* directory, kSize capacity) { return kApiLib_DirectoryHandlers()->apiDirectory(directory, capacity); } kFx(kStatus) kDirectory_Temp(kChar* directory, kSize capacity) { return kApiLib_DirectoryHandlers()->tempDirectory(directory, capacity); } kFx(kStatus) kDirectory_AppConfig(const kChar* appName, kChar* directory, kSize capacity) { return kApiLib_DirectoryHandlers()->appConfigDirectory(appName, directory, capacity); } kFx(kStatus) kDirectory_AppData(const kChar* appName, kChar* directory, kSize capacity) { return kApiLib_DirectoryHandlers()->appDataDirectory(appName, directory, capacity); } kFx(kStatus) xkDirectory_AppConfigImpl(const kChar* appName, kChar* directory, kSize capacity) { kCheck(kDirectory_Api(directory, capacity)); kCheck(kPath_Directory(directory, directory, capacity)); kCheck(kPath_Directory(directory, directory, capacity)); kCheck(kPath_Combine(directory, xkDIRECTORY_DEFAULT_CONFIG_FOLDER, directory, capacity)); if (!kIsNull(appName)) { kCheck(kPath_Combine(directory, appName, directory, capacity)); } return kOK; } kFx(kStatus) xkDirectory_AppDataImpl(const kChar* appName, kChar* directory, kSize capacity) { kCheck(kDirectory_Application(directory, capacity)); kCheck(kPath_Directory(directory, directory, capacity)); kCheck(kPath_Directory(directory, directory, capacity)); kCheck(kPath_Combine(directory, xkDIRECTORY_DEFAULT_DATA_FOLDER, directory, capacity)); if (!kIsNull(appName)) { kCheck(kPath_Combine(directory, appName, directory, capacity)); } return kOK; } kFx(kStatus) xkDirectory_FromVirtualImpl(const kChar* vPath, kChar* path, kSize capacity) { return kStrCopy(path, capacity, vPath); } kFx(kStatus) xkDirectory_ToVirtualImpl(const kChar* path, kChar* vPath, kSize capacity) { return kStrCopy(vPath, capacity, path); } kFx(kStatus) kDirectory_Plugin(kChar* directory, kSize capacity) { return kDirectory_Api(directory, capacity); } #if defined(K_WINDOWS) kFx(kStatus) xkDirectory_CreateImpl(const kChar* path) { WCHAR wpath[MAX_PATH]; kCheck(xkPath_NormalizedToNativeWideWin(path, wpath, kCountOf(wpath))); return CreateDirectory(wpath, NULL) ? kOK : kERROR_OS; } kFx(kBool) xkDirectory_ExistsImpl(const kChar* path) { WCHAR wpath[MAX_PATH]; DWORD attr = 0; kCheck(xkPath_NormalizedToNativeWideWin(path, wpath, kCountOf(wpath))); attr = GetFileAttributes(wpath); return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY); } kFx(kStatus) xkDirectory_MoveImpl(const kChar* source, const kChar* destination) { WCHAR wpathSrc[MAX_PATH]; WCHAR wpathDst[MAX_PATH]; kSize i; kCheck(xkPath_NormalizedToNativeWideWin(source, wpathSrc, kCountOf(wpathSrc))); kCheck(xkPath_NormalizedToNativeWideWin(destination, wpathDst, kCountOf(wpathDst))); for (i = 0; i < xkDIRECTORY_WINDOWS_IO_ATTEMPTS; i++) { if (MoveFile(wpathSrc, wpathDst)) { return kOK; } else if (GetLastError() == xkDIRECTORY_WINDOWS_ACCESS_DENIED) { kCheck(kThread_Sleep(100000)); } else { return kERROR_OS; } } return kERROR_OS; } kFx(kStatus) xkDirectory_DeleteImpl(const kChar* path) { WCHAR tempWidePath[MAX_PATH]; kChar absPath[kPATH_MAX]; kChar parent[kPATH_MAX]; kChar tempName[kPATH_MAX]; kBool directoryAlreadyExists = kFALSE; kBool fileAlreadyExists = kFALSE; kBool moveFailure = kFALSE; kSize retryCount = 0; //we want to get the parent directory, but the given path may be relative; //if so, we convert to an absolute path that is relative to the current working directory kCheck(kPath_ToAbsolute(kNULL, path, absPath, sizeof(absPath))); kCheck(kPath_Directory(absPath, parent, kCountOf(parent))); do { kCheckTrue(retryCount++ < xkFILE_WINDOWS_IO_ATTEMPTS, kERROR_OS); kCheck(kFile_TempName(tempName, kCountOf(tempName))); kCheck(kPath_Combine(parent, tempName, tempName, kCountOf(tempName))); kCheck(xkPath_NormalizedToNativeWideWin(tempName, tempWidePath, kCountOf(tempWidePath))); directoryAlreadyExists = kDirectory_Exists(tempName); if (directoryAlreadyExists) { //should never get here; but if we do, clean up and select a new name RemoveDirectory(tempWidePath); } else { fileAlreadyExists = kFile_Exists(tempName); if (fileAlreadyExists) { //should never get here; but if we do, clean up and select a new name kFile_Delete(tempName); } else { moveFailure = kIsError(kDirectory_Move(absPath, tempName)); } } } while (directoryAlreadyExists || fileAlreadyExists || moveFailure); return RemoveDirectory(tempWidePath) ? kOK : kERROR_OS; } kFx(kStatus) xkDirectory_EnumerateImpl(const kChar* path, kBool includeFile, kBool includeDirectories, kArrayList entries) { HANDLE findHandle = INVALID_HANDLE_VALUE; WCHAR searchFilter[MAX_PATH]; kChar entry[MAX_PATH]; WIN32_FIND_DATA findData; kString item = kNULL; kTry { kTest(xkPath_NormalizedToNativeWideWin(path, searchFilter, kCountOf(searchFilter))); wcscat(searchFilter, L"\\*"); if ((findHandle = FindFirstFile(searchFilter, &findData)) == INVALID_HANDLE_VALUE) { kThrow(kERROR_OS); } kTest(kArrayList_Allocate(entries, kTypeOf(kString), 0)); do { kBool isDirectory = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; kBool isSpecial = (wcscmp(findData.cFileName, L".") == 0) || (wcscmp(findData.cFileName, L"..") == 0); kBool filter = !isSpecial && ((includeFile && !isDirectory) || (includeDirectories && isDirectory)); if (filter) { kTest(xkPath_NativeWideToNormalizedWin(findData.cFileName, entry, kCountOf(entry))); kTest(kString_Construct(&item, entry, kNULL)); kTest(kArrayList_AddT(entries, &item)); item = kNULL; } } while (FindNextFile(findHandle, &findData)); } kFinally { kCheck(kObject_Destroy(item)); if (findHandle != INVALID_HANDLE_VALUE) { FindClose(findHandle); } kEndFinally(); } return kOK; } kFx(kStatus) xkDirectory_SetCurrentImpl(const kChar* path) { WCHAR wpath[MAX_PATH]; kCheck(xkPath_NormalizedToNativeWideWin(path, wpath, kCountOf(wpath))); return SetCurrentDirectory(wpath) ? kOK : kERROR_OS; } kFx(kStatus) xkDirectory_CurrentImpl(kChar* directory, kSize capacity) { WCHAR wpath[MAX_PATH]; if (GetCurrentDirectory(kCountOf(wpath), wpath) == 0) { return kERROR_OS; } kCheck(xkPath_NativeWideToNormalizedWin(wpath, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_ApplicationImpl(kChar* directory, kSize capacity) { WCHAR wmodulePath[kPATH_MAX]; HANDLE module; if (kIsNull(module = GetModuleHandle(NULL))) { return kERROR; } if (GetModuleFileName((HMODULE)module, wmodulePath, kCountOf(wmodulePath)) == 0) { return kERROR; } kCheck(xkPath_NativeWideToNormalizedWin(wmodulePath, directory, capacity)); kCheck(kPath_Directory(directory, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_ApiImpl(kChar* directory, kSize capacity) { WCHAR wmodulePath[kPATH_MAX]; HMODULE module; if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCSTR) &xkDirectory_ApiImpl, &module)) { return kERROR; } if (GetModuleFileName(module, wmodulePath, kCountOf(wmodulePath)) == 0) { return kERROR; } kCheck(xkPath_NativeWideToNormalizedWin(wmodulePath, directory, capacity)); kCheck(kPath_Directory(directory, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_TempImpl(kChar* path, kSize capacity) { WCHAR wpath[kPATH_MAX]; if (GetTempPath(kCountOf(wpath), wpath) == 0) { return kERROR_OS; } kCheck(xkPath_NativeWideToNormalizedWin(wpath, path, capacity)); return kOK; } #elif defined(K_DARWIN) kFx(kStatus) xkDirectory_CreateImpl(const kChar* directory) { kChar nativeDirectory[kPATH_MAX]; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); return (mkdir(nativeDirectory, S_IRWXU) == -1) ? kERROR_OS : kOK; } kFx(kBool) xkDirectory_ExistsImpl(const kChar* directory) { struct stat st; kChar nativeDirectory[kPATH_MAX]; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); return (stat(nativeDirectory, &st) != -1) && S_ISDIR(st.st_mode); } kFx(kStatus) xkDirectory_MoveImpl(const kChar* source, const kChar* destination) { kChar nativeSource[kPATH_MAX]; kChar nativeDestination[kPATH_MAX]; kCheck(xkPath_FromVirtual(source, nativeSource, kCountOf(nativeSource))); kCheck(xkPath_FromVirtual(destination, nativeDestination, kCountOf(nativeDestination))); return (rename(nativeSource, nativeDestination) == -1) ? kERROR_OS : kOK; } kFx(kStatus) xkDirectory_DeleteImpl(const kChar* directory) { kChar nativeDirectory[kPATH_MAX]; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); return (rmdir(nativeDirectory) == -1) ? kERROR_OS : kOK; } kFx(kStatus) xkDirectory_EnumerateImpl(const kChar* directory, kBool includeFile, kBool includeDirectories, kArrayList entries) { kChar nativeDirectory[kPATH_MAX]; DIR* dirIt = kNULL; struct dirent* dirEntry = kNULL; kString item = kNULL; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); kCheckTrue((dirIt = opendir(nativeDirectory)) != kNULL, kERROR_NOT_FOUND); kTry { kTest(kArrayList_Allocate(entries, kTypeOf(kString), 0)); while ((dirEntry = readdir(dirIt)) != kNULL) { kBool isDirectory = (dirEntry->d_type == DT_DIR); kBool isSpecial = (kStrEquals(dirEntry->d_name, ".") || kStrEquals(dirEntry->d_name, "..")); kBool filter = !isSpecial && ((includeFile && !isDirectory) || (includeDirectories && isDirectory)); if (filter) { kTest(kString_Construct(&item, dirEntry->d_name, kNULL)); kTest(kArrayList_AddT(entries, &item)); item = kNULL; } } } kFinally { closedir(dirIt); kObject_Destroy(item); kEndFinally(); } return kOK; } kFx(kStatus) xkDirectory_SetCurrentImpl(const kChar* directory) { kChar nativeDirectory[kPATH_MAX]; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); return (chdir(nativeDirectory) == -1) ? kERROR_OS : kOK; } kFx(kStatus) xkDirectory_CurrentImpl(kChar* directory, kSize capacity) { kChar nativePath[kPATH_MAX]; if (getcwd(nativePath, kCountOf(nativePath)) == kNULL) { return kERROR_OS; } kCheck(xkPath_ToVirtual(nativePath, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_ApplicationImpl(kChar* directory, kSize capacity) { kChar modulePath[kPATH_MAX]; uint32_t size = kCountOf(modulePath); if (_NSGetExecutablePath(modulePath, &size) != 0) { return kERROR; } kCheck(kPath_ToNormal(modulePath, modulePath, kCountOf(modulePath))); kCheck(xkPath_ToVirtual(modulePath, modulePath, kCountOf(modulePath))); kCheck(kPath_Directory(modulePath, directory, capacity)); kCheck(kPath_Directory(directory, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_ApiImpl(kChar* directory, kSize capacity) { kChar baseName[kPATH_MAX]; //resolving the plugin directory via the application directory is not necessarily correct, but good enough in most cases kCheck(xkDirectory_ApplicationImpl(directory, capacity)); kCheck(kPath_BaseName(directory, baseName, kCountOf(baseName))); kCheck(kPath_Directory(directory, directory, capacity)); kCheck(kPath_Directory(directory, directory, capacity)); kCheck(kPath_Combine(directory, "lib", directory, capacity)); kCheck(kPath_Combine(directory, baseName, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_TempImpl(kChar* directory, kSize capacity) { //TODO: system-specific; could probably be improved (?) return xkPath_ToVirtual("/tmp", directory, capacity); } #elif defined(K_LINUX) kFx(kStatus) xkDirectory_CreateImpl(const kChar* directory) { kChar nativeDirectory[kPATH_MAX]; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); return (mkdir(nativeDirectory, S_IRWXU) == -1) ? kERROR_OS : kOK; } kFx(kBool) xkDirectory_ExistsImpl(const kChar* directory) { struct stat st; kChar nativeDirectory[kPATH_MAX]; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); return (stat(nativeDirectory, &st) != -1) && S_ISDIR(st.st_mode); } kFx(kStatus) xkDirectory_MoveImpl(const kChar* source, const kChar* destination) { kChar nativeSource[kPATH_MAX]; kChar nativeDestination[kPATH_MAX]; kCheck(xkPath_FromVirtual(source, nativeSource, kCountOf(nativeSource))); kCheck(xkPath_FromVirtual(destination, nativeDestination, kCountOf(nativeDestination))); return (rename(nativeSource, nativeDestination) == -1) ? kERROR_OS : kOK; } kFx(kStatus) xkDirectory_DeleteImpl(const kChar* directory) { kChar nativeDirectory[kPATH_MAX]; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); return (rmdir(nativeDirectory) == -1) ? kERROR_OS : kOK; } kFx(kStatus) xkDirectory_EnumerateImpl(const kChar* directory, kBool includeFile, kBool includeDirectories, kArrayList entries) { kChar nativeDirectory[kPATH_MAX]; DIR* dirIt = kNULL; struct dirent* dirEntry = kNULL; kString item = kNULL; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); kCheckTrue((dirIt = opendir(nativeDirectory)) != kNULL, kERROR_NOT_FOUND); kTry { kTest(kArrayList_Allocate(entries, kTypeOf(kString), 0)); while ((dirEntry = readdir(dirIt)) != kNULL) { kBool isDirectory = (dirEntry->d_type == DT_DIR); kBool isSpecial = (kStrEquals(dirEntry->d_name, ".") || kStrEquals(dirEntry->d_name, "..")); kBool filter = !isSpecial && ((includeFile && !isDirectory) || (includeDirectories && isDirectory)); if (filter) { kTest(kString_Construct(&item, dirEntry->d_name, kNULL)); kTest(kArrayList_AddT(entries, &item)); item = kNULL; } } } kFinally { closedir(dirIt); kObject_Destroy(item); kEndFinally(); } return kOK; } kFx(kStatus) xkDirectory_SetCurrentImpl(const kChar* directory) { kChar nativeDirectory[kPATH_MAX]; kCheck(xkPath_FromVirtual(directory, nativeDirectory, kCountOf(nativeDirectory))); return (chdir(nativeDirectory) == -1) ? kERROR_OS : kOK; } kFx(kStatus) xkDirectory_CurrentImpl(kChar* directory, kSize capacity) { kChar nativePath[kPATH_MAX]; if (getcwd(nativePath, kCountOf(nativePath)) == kNULL) { return kERROR_OS; } kCheck(xkPath_ToVirtual(nativePath, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_ApplicationImpl(kChar* directory, kSize capacity) { kChar modulePath[kPATH_MAX]; ssize_t length; if ((length = readlink("/proc/self/exe", modulePath, kCountOf(modulePath)-1)) == -1) { return kERROR; } modulePath[length] = 0; kCheck(kPath_ToNormal(modulePath, modulePath, kCountOf(modulePath))); kCheck(xkPath_ToVirtual(modulePath, modulePath, kCountOf(modulePath))); kCheck(kPath_Directory(modulePath, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_ApiImpl(kChar* directory, kSize capacity) { Dl_info info; kCheck(dladdr((void*)xkDirectory_ApiImpl, &info) != 0); kCheck(kPath_Directory(info.dli_fname, directory, capacity)); return kOK; } kFx(kStatus) xkDirectory_TempImpl(kChar* directory, kSize capacity) { //TODO: system-specific; could probably be improved (?) return xkPath_ToVirtual("/tmp", directory, capacity); } #else kFx(kStatus) xkDirectory_CreateImpl(const kChar* directory) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } kFx(kBool) xkDirectory_ExistsImpl(const kChar* directory) { kAssert(kFALSE); return kFALSE; } kFx(kStatus) xkDirectory_MoveImpl(const kChar* source, const kChar* destination) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } kFx(kStatus) xkDirectory_DeleteImpl(const kChar* directory) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } kFx(kStatus) xkDirectory_EnumerateImpl(const kChar* directory, kBool includeFile, kBool includeDirectories, kArrayList entries) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } kFx(kStatus) xkDirectory_SetCurrentImpl(const kChar* directory) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } kFx(kStatus) xkDirectory_CurrentImpl(kChar* directory, kSize capacity) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } kFx(kStatus) xkDirectory_ApplicationImpl(kChar* directory, kSize capacity) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } kFx(kStatus) xkDirectory_ApiImpl(kChar* directory, kSize capacity) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } kFx(kStatus) xkDirectory_TempImpl(kChar* directory, kSize capacity) { kAssert(kFALSE); return kERROR_UNIMPLEMENTED; } #endif