#include "fsutils.h" #include "runtime.h" #include "threading.h" #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include struct rt_scandir_handle_s { HANDLE handle; rt_dirent next; }; static rt_scandir_handle _dirs[256]; static unsigned int _next = 0; static rt_mutex *_guard = NULL; RT_DLLEXPORT rt_scandir_handle *rtScanDirectory(const char *path) { char wildcard_path[MAX_PATH]; strncpy(wildcard_path, path, MAX_PATH); strncat(wildcard_path, "\\*", MAX_PATH - strlen(path)); WCHAR wpath[MAX_PATH]; if (rtUTF8ToWStr(wildcard_path, wpath, MAX_PACKAGE_NAME) != RT_SUCCESS) return NULL; WIN32_FIND_DATAW data; HANDLE h = FindFirstFileW(wpath, &data); if (h == INVALID_HANDLE_VALUE) return NULL; if (!_guard) { _guard = rtCreateMutex(); if (!_guard) { FindClose(h); return NULL; } } rtLockMutex(_guard); rt_scandir_handle *dir = &_dirs[_next]; _next = (_next + 1) % RT_ARRAY_COUNT(_dirs); rtUnlockMutex(_guard); if (dir->handle != NULL) { rtLog("core", "Failed to acquire a scandir handle."); FindClose(h); return NULL; } dir->handle = h; dir->next.is_last = false; rtWStrToUTF8(data.cFileName, dir->next.name, 260); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) dir->next.type = RT_DIRENT_TYPE_DIRECTORY; else dir->next.type = RT_DIRENT_TYPE_FILE; return dir; } RT_DLLEXPORT rt_dirent rtNextDirectoryEntry(rt_scandir_handle *dir) { rt_dirent current; memcpy(¤t, &dir->next, sizeof(rt_dirent)); WIN32_FIND_DATAW data; if (!FindNextFileW(dir->handle, &data)) { current.is_last = true; return current; } rtWStrToUTF8(data.cFileName, dir->next.name, 260); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) dir->next.type = RT_DIRENT_TYPE_DIRECTORY; else dir->next.type = RT_DIRENT_TYPE_FILE; return current; } RT_DLLEXPORT void rtCloseDirectory(rt_scandir_handle *dir) { if (!dir) return; FindClose(dir->handle); dir->handle = NULL; } RT_DLLEXPORT bool rtCreateDirectory(const char *path) { WCHAR wpath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH); return CreateDirectoryW(wpath, NULL); } RT_DLLEXPORT size_t rtGetFileSize(const char *path) { WCHAR wpath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH); WIN32_FILE_ATTRIBUTE_DATA attribs; if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &attribs)) return 0; return (size_t)attribs.nFileSizeHigh << 32 | (size_t)attribs.nFileSizeLow; } uint64_t rtGetCurrentTimestamp(void) { FILETIME ft; GetSystemTimeAsFileTime(&ft); uint64_t ts = ft.dwLowDateTime; ts |= (uint64_t)ft.dwHighDateTime << 32; return ts; } uint64_t rtGetFileModificationTimestamp(const char *path) { WCHAR wpath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, path, -1, wpath, MAX_PATH); WIN32_FILE_ATTRIBUTE_DATA attribs; if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &attribs)) return 0; uint64_t ts = attribs.ftLastWriteTime.dwLowDateTime; ts |= (uint64_t)attribs.ftLastWriteTime.dwHighDateTime << 32; return ts; } #elif defined(__linux__) #include #include #include #include struct rt_scandir_handle_s { DIR *handle; rt_dirent next; }; static rt_scandir_handle _dirs[256]; static unsigned int _next = 0; static rt_mutex *_guard = NULL; RT_DLLEXPORT rt_scandir_handle *rtScanDirectory(const char *path) { DIR *h = opendir(path); if (!h) return NULL; if (!_guard) { _guard = rtCreateMutex(); if (!_guard) { closedir(h); return NULL; } } rtLockMutex(_guard); rt_scandir_handle *dir = &_dirs[_next]; _next = (_next + 1) % RT_ARRAY_COUNT(_dirs); rtUnlockMutex(_guard); if (dir->handle != NULL) { rtLog("core", "Failed to acquire a scandir handle."); closedir(h); return NULL; } struct dirent *ent = readdir(h); if (!ent) { closedir(h); return NULL; } dir->handle = h; dir->next.is_last = false; memcpy(dir->next.name, ent->d_name, 255); if (ent->d_type == DT_DIR) dir->next.type = RT_DIRENT_TYPE_DIRECTORY; else dir->next.type = RT_DIRENT_TYPE_FILE; return dir; } RT_DLLEXPORT rt_dirent rtNextDirectoryEntry(rt_scandir_handle *dir) { rt_dirent current; memcpy(¤t, &dir->next, sizeof(rt_dirent)); struct dirent *ent = readdir(dir->handle); if (!ent) { current.is_last = true; return current; } memcpy(dir->next.name, ent->d_name, 255); if (ent->d_type == DT_DIR) dir->next.type = RT_DIRENT_TYPE_DIRECTORY; else dir->next.type = RT_DIRENT_TYPE_FILE; return current; } RT_DLLEXPORT void rtCloseDirectory(rt_scandir_handle *dir) { if (!dir) return; closedir(dir->handle); dir->handle = NULL; } RT_DLLEXPORT bool rtCreateDirectory(const char *path) { return mkdir(path, S_IRWXU | S_IRGRP) == 0; } RT_DLLEXPORT size_t rtGetFileSize(const char *path) { struct stat st; if (stat(path, &st) != 0) return 0; return (size_t)st.st_size; } uint64_t rtGetCurrentTimestamp(void) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); return (uint64_t)ts.tv_sec; } uint64_t rtGetFileModificationTimestamp(const char *path) { struct stat st; if (stat(path, &st) != 0) return 0; return (uint64_t)st.st_mtim.tv_sec; } #endif