// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors. // All rights reserved. // Code licensed under the BSD License. // http://www.anki3d.org/LICENSE // Some defines for extra posix features #define _XOPEN_SOURCE 700 #define _LARGEFILE64_SOURCE #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include // For walkDirectoryTree #include #include #ifndef USE_FDS # define USE_FDS 15 #endif // Define PATH_MAX if needed #ifndef PATH_MAX # define PATH_MAX 4096 #endif namespace anki { Bool fileExists(const CString& filename) { struct stat s; if(stat(filename.cstr(), &s) == 0) { return S_ISREG(s.st_mode); } else { return false; } } Bool directoryExists(const CString& filename) { struct stat s; if(stat(filename.cstr(), &s) == 0) { return S_ISDIR(s.st_mode); } else { return false; } } class WalkDirectoryTreeCallbackContext { public: const Function* m_callback = nullptr; U32 m_prefixLen; Error m_err = {Error::NONE}; }; static thread_local WalkDirectoryTreeCallbackContext g_walkDirectoryTreeContext; static int walkDirectoryTreeCallback(const char* filepath, const struct stat* info, const int typeflag, struct FTW* pathinfo) { Bool isDir; Bool ignored = true; if(typeflag == FTW_F) { isDir = false; ignored = false; } else if(typeflag == FTW_D || typeflag == FTW_DP) { isDir = true; ignored = false; } if(!ignored) { WalkDirectoryTreeCallbackContext& ctx = g_walkDirectoryTreeContext; ANKI_ASSERT(ctx.m_callback); if(ctx.m_err || strlen(filepath) <= ctx.m_prefixLen) { return 0; } ctx.m_err = (*ctx.m_callback)(filepath + ctx.m_prefixLen, isDir); } return 0; } Error walkDirectoryTreeInternal(const CString& dir, const Function& callback) { ANKI_ASSERT(dir.getLength() > 0); Error err = Error::NONE; // Compute how long it will be the prefix fts_read will add U32 prefixLen = dir.getLength(); if(dir[prefixLen - 1] != '/') { ++prefixLen; } WalkDirectoryTreeCallbackContext& ctx = g_walkDirectoryTreeContext; ctx.m_callback = &callback; ctx.m_prefixLen = prefixLen; ctx.m_err = Error::NONE; const int result = nftw(dir.cstr(), walkDirectoryTreeCallback, USE_FDS, FTW_PHYS); if(result != 0) { ANKI_UTIL_LOGE("nftw() failed"); err = Error::FUNCTION_FAILED; } else { err = ctx.m_err; } return err; } static Error removeDirectoryInternal(const CString& dirname, GenericMemoryPoolAllocator& alloc) { DIR* dir; struct dirent* entry; dir = opendir(dirname.cstr()); if(dir == nullptr) { ANKI_UTIL_LOGE("opendir() failed"); return Error::FUNCTION_FAILED; } while((entry = readdir(dir)) != nullptr) { if(strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) { StringAuto path(alloc); path.sprintf("%s/%s", dirname.cstr(), entry->d_name); if(entry->d_type == DT_DIR) { Error err = removeDirectoryInternal(path.toCString(), alloc); if(err) { return err; } } else { remove(path.cstr()); } } } closedir(dir); remove(dirname.cstr()); return Error::NONE; } Error removeDirectory(const CString& dirname, GenericMemoryPoolAllocator alloc) { return removeDirectoryInternal(dirname, alloc); } Error createDirectory(const CString& dir) { if(directoryExists(dir)) { return Error::NONE; } Error err = Error::NONE; if(mkdir(dir.cstr(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) { ANKI_UTIL_LOGE("%s : %s", strerror(errno), dir.cstr()); err = Error::FUNCTION_FAILED; } return err; } Error getHomeDirectory(StringAuto& out) { const char* home = getenv("HOME"); if(home == nullptr) { ANKI_UTIL_LOGE("HOME environment variable not set"); return Error::FUNCTION_FAILED; } out.create(home); return Error::NONE; } Error getTempDirectory(StringAuto& out) { out.create("/tmp/"); return Error::NONE; } Error getFileModificationTime(CString filename, U32& year, U32& month, U32& day, U32& hour, U32& min, U32& second) { struct stat buff; if(lstat(filename.cstr(), &buff)) { ANKI_UTIL_LOGE("stat() failed"); return Error::NONE; } struct tm t; localtime_r(&buff.st_mtim.tv_sec, &t); year = 1900 + t.tm_year; month = t.tm_mon + 1; day = t.tm_mday; hour = t.tm_hour; second = t.tm_sec; return Error::NONE; } } // end namespace anki