/* * Copyright (c) 2012-2024 Daniele Bartolini et al. * SPDX-License-Identifier: MIT */ #include "core/containers/vector.inl" #include "core/error/error.h" #include "core/memory/temp_allocator.inl" #include "core/os.h" #include "core/platform.h" #include "core/strings/dynamic_string.inl" #include "core/strings/string_stream.h" #include // strcmp #include // stat, mkdir #if CROWN_PLATFORM_WINDOWS #include // _access #include #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #else #include // opendir, readdir #include // dlopen, dlclose, dlsym #include #include // fputs, rename #include // getenv #include // memset #include // wait #include // clock_gettime #include // unlink, rmdir, getcwd, access, chdir #endif // if CROWN_PLATFORM_WINDOWS #if CROWN_PLATFORM_ANDROID #include #endif namespace crown { namespace os { void sleep(u32 ms) { #if CROWN_PLATFORM_WINDOWS Sleep(ms); #else usleep(ms * 1000); #endif } void *library_open(const char *path) { #if CROWN_PLATFORM_WINDOWS return (void *)LoadLibraryA(path); #else return ::dlopen(path, RTLD_LAZY); #endif } void library_close(void *library) { #if CROWN_PLATFORM_WINDOWS BOOL err = FreeLibrary((HMODULE)library); CE_ASSERT(err != 0, "FreeLibrary: error: %s", GetLastError()); #else int err = dlclose(library); CE_ASSERT(err == 0, "dlclose: error: %s", dlerror()); #endif CE_UNUSED(err); } void *library_symbol(void *library, const char *name) { #if CROWN_PLATFORM_WINDOWS return (void *)GetProcAddress((HMODULE)library, name); #else return ::dlsym(library, name); #endif } void log(const char *msg) { #if CROWN_PLATFORM_ANDROID __android_log_write(ANDROID_LOG_DEBUG, "crown", msg); #else fputs(msg, stdout); fflush(stdout); #endif #if CROWN_PLATFORM_WINDOWS OutputDebugStringA(msg); #endif } #if CROWN_PLATFORM_POSIX void stat(Stat &st, int fd) { st.file_type = Stat::NO_ENTRY; st.size = 0; st.mtime = 0; struct stat buf; memset(&buf, 0, sizeof(buf)); int err = ::fstat(fd, &buf); if (err != 0) return; if (S_ISREG(buf.st_mode) == 1) st.file_type = Stat::REGULAR; else if (S_ISDIR(buf.st_mode) == 1) st.file_type = Stat::DIRECTORY; st.size = buf.st_size; st.mtime = buf.st_mtim.tv_sec * s64(1000000000) + buf.st_mtim.tv_nsec; } #endif // if CROWN_PLATFORM_POSIX void stat(Stat &st, const char *path) { st.file_type = Stat::NO_ENTRY; st.size = 0; st.mtime = 0; #if CROWN_PLATFORM_WINDOWS WIN32_FIND_DATAA wfd; HANDLE fh = FindFirstFileA(path, &wfd); if (fh == INVALID_HANDLE_VALUE) return; FindClose(fh); if ((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) st.file_type = Stat::DIRECTORY; else // Assume regular file. st.file_type = Stat::REGULAR; ULARGE_INTEGER fs = {}; fs.LowPart = wfd.nFileSizeLow; fs.HighPart = wfd.nFileSizeHigh; st.size = fs.QuadPart; ULARGE_INTEGER lwt = {}; lwt.LowPart = wfd.ftLastWriteTime.dwLowDateTime; lwt.HighPart = wfd.ftLastWriteTime.dwHighDateTime; // Convert mtime to ns and subtract Unix epoch to Windows epoch // difference in ns (i.e. 1970-01-01, 12AM - 1601-01-01, 12AM). // See: https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime st.mtime = lwt.QuadPart * u64(100) - u64(11644473600) * u64(1000000000); #else struct stat buf; memset(&buf, 0, sizeof(buf)); int err = ::stat(path, &buf); if (err != 0) return; if (S_ISREG(buf.st_mode) == 1) st.file_type = Stat::REGULAR; else if (S_ISDIR(buf.st_mode) == 1) st.file_type = Stat::DIRECTORY; st.size = buf.st_size; st.mtime = buf.st_mtim.tv_sec * s64(1000000000) + buf.st_mtim.tv_nsec; #endif // if CROWN_PLATFORM_WINDOWS } DeleteResult delete_file(const char *path) { DeleteResult dr; #if CROWN_PLATFORM_WINDOWS if (DeleteFile(path) != 0) dr.error = DeleteResult::SUCCESS; else if (GetLastError() == ERROR_FILE_NOT_FOUND) dr.error = DeleteResult::NO_ENTRY; // else if (GetLastError() == ERROR_ACCESS_DENIED) // dr.error = DeleteResult::NOT_FILE; else dr.error = DeleteResult::UNKNOWN; #else if (::unlink(path) == 0) dr.error = DeleteResult::SUCCESS; else if (errno == ENOENT) dr.error = DeleteResult::NO_ENTRY; else dr.error = DeleteResult::UNKNOWN; #endif return dr; } CreateResult create_directory(const char *path) { CreateResult cr; #if CROWN_PLATFORM_WINDOWS if (CreateDirectory(path, NULL) != 0) cr.error = CreateResult::SUCCESS; else if (GetLastError() == ERROR_ALREADY_EXISTS) cr.error = CreateResult::ALREADY_EXISTS; else cr.error = CreateResult::UNKNOWN; #else if (::mkdir(path, 0755) == 0) cr.error = CreateResult::SUCCESS; else if (errno == EEXIST) cr.error = CreateResult::ALREADY_EXISTS; else cr.error = CreateResult::UNKNOWN; #endif return cr; } DeleteResult delete_directory(const char *path) { DeleteResult dr; #if CROWN_PLATFORM_WINDOWS if (RemoveDirectory(path) != 0) dr.error = DeleteResult::SUCCESS; else if (GetLastError() == ERROR_FILE_NOT_FOUND) dr.error = DeleteResult::NO_ENTRY; // else if (GetLastError() == ERROR_DIRECTORY // dr.error = DeleteResult::NOT_DIRECTORY; else dr.error = DeleteResult::UNKNOWN; #else if (::rmdir(path) == 0) dr.error = DeleteResult::SUCCESS; else if (errno == ENOENT) dr.error = DeleteResult::NO_ENTRY; else dr.error = DeleteResult::UNKNOWN; #endif return dr; } const char *getcwd(char *buf, u32 size) { #if CROWN_PLATFORM_WINDOWS GetCurrentDirectory(size, buf); return buf; #else return ::getcwd(buf, size); #endif } void setcwd(const char *cwd) { #if CROWN_PLATFORM_WINDOWS BOOL ret = SetCurrentDirectory(cwd); CE_UNUSED(ret); #else int ret = chdir(cwd); CE_UNUSED(ret); #endif } const char *getenv(const char *name) { #if CROWN_PLATFORM_WINDOWS // GetEnvironmentVariable(name, buf, size); return NULL; #else return ::getenv(name); #endif } void list_files(const char *path, Vector &files) { #if CROWN_PLATFORM_WINDOWS TempAllocator256 ta_path; DynamicString cur_path(ta_path); cur_path += path; cur_path += "\\*"; WIN32_FIND_DATA ffd; HANDLE file = FindFirstFile(cur_path.c_str(), &ffd); if (file != INVALID_HANDLE_VALUE) { do { const char *dname = ffd.cFileName; if (!strcmp(dname, ".") || !strcmp(dname, "..")) continue; TempAllocator256 ta; DynamicString fname(ta); fname.set(dname, strlen32(dname)); vector::push_back(files, fname); } while (FindNextFile(file, &ffd) != 0); FindClose(file); } #else struct dirent *entry; DIR *dir = opendir(path); if (dir != NULL) { while ((entry = readdir(dir))) { const char *dname = entry->d_name; if (!strcmp(dname, ".") || !strcmp(dname, "..")) continue; TempAllocator256 ta; DynamicString fname(ta); fname.set(dname, strlen32(dname)); vector::push_back(files, fname); } closedir(dir); } #endif // if CROWN_PLATFORM_WINDOWS } /// s32 access(const char *path, u32 flags) { #if CROWN_PLATFORM_WINDOWS return ::_access(path, flags == AccessFlags::EXECUTE ? AccessFlags::EXISTS : flags); #else return ::access(path, flags); #endif } RenameResult rename(const char *old_name, const char *new_name) { RenameResult rr; #if CROWN_PLATFORM_WINDOWS if (MoveFileEx(old_name, new_name, MOVEFILE_REPLACE_EXISTING) != 0) rr.error = RenameResult::SUCCESS; else rr.error = RenameResult::UNKNOWN; #else if (::rename(old_name, new_name) == 0) rr.error = RenameResult::SUCCESS; else rr.error = RenameResult::UNKNOWN; #endif return rr; } } // namespace os } // namespace crown