// // Copyright Contributors to the MaterialX Project // SPDX-License-Identifier: Apache-2.0 // #include #include #include #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include #include #else #include #include #include #endif #if defined(__linux__) #include #elif defined(__FreeBSD__) #include #elif defined(__APPLE__) #include #endif #include #include #include #include MATERIALX_NAMESPACE_BEGIN const string VALID_SEPARATORS = "/\\"; const char PREFERRED_SEPARATOR_WINDOWS = '\\'; const char PREFERRED_SEPARATOR_POSIX = '/'; const string CURRENT_PATH_STRING = "."; const string PARENT_PATH_STRING = ".."; #if defined(_WIN32) const string PATH_LIST_SEPARATOR = ";"; #else const string PATH_LIST_SEPARATOR = ":"; #endif const string MATERIALX_SEARCH_PATH_ENV_VAR = "MATERIALX_SEARCH_PATH"; inline bool hasWindowsDriveSpecifier(const string& val) { return (val.length() > 1 && std::isalpha((unsigned char) val[0]) && (val[1] == ':')); } // // FilePath methods // void FilePath::assign(const string& str) { _type = TypeRelative; _vec = splitString(str, VALID_SEPARATORS); if (!str.empty()) { if (str[0] == PREFERRED_SEPARATOR_POSIX) { _type = TypeAbsolute; } else if (str.size() >= 2) { if (hasWindowsDriveSpecifier(str)) { _type = TypeAbsolute; } else if (str[0] == '\\' && str[1] == '\\') { _type = TypeNetwork; } } } } string FilePath::asString(Format format) const { string str; if (format == FormatPosix && isAbsolute()) { // Don't prepend a POSIX separator on a Windows absolute path if (_vec.empty() || !hasWindowsDriveSpecifier(_vec[0])) { str += "/"; } } else if (format == FormatWindows && _type == TypeNetwork) { str += "\\\\"; } for (size_t i = 0; i < _vec.size(); i++) { str += _vec[i]; if (i + 1 < _vec.size()) { if (format == FormatPosix) { str += PREFERRED_SEPARATOR_POSIX; } else { str += PREFERRED_SEPARATOR_WINDOWS; } } } return str; } FilePath FilePath::operator/(const FilePath& rhs) const { if (rhs.isAbsolute()) { throw Exception("Appended path must be relative."); } FilePath combined(*this); for (const string& str : rhs._vec) { combined._vec.push_back(str); } return combined; } FilePath FilePath::getNormalized() const { FilePath res; for (const string& str : _vec) { if (str == CURRENT_PATH_STRING) { continue; } if (str == PARENT_PATH_STRING && !res.isEmpty() && res[res.size() - 1] != PARENT_PATH_STRING) { res._vec.pop_back(); continue; } res._vec.push_back(str); } res._type = _type; return res; } bool FilePath::exists() const { #if defined(_WIN32) uint32_t result = GetFileAttributesA(asString().c_str()); return result != INVALID_FILE_ATTRIBUTES; #else struct stat sb; return stat(asString().c_str(), &sb) == 0; #endif } bool FilePath::isDirectory() const { #if defined(_WIN32) uint32_t result = GetFileAttributesA(asString().c_str()); if (result == INVALID_FILE_ATTRIBUTES) return false; return (result & FILE_ATTRIBUTE_DIRECTORY) != 0; #else struct stat sb; if (stat(asString().c_str(), &sb)) return false; return S_ISDIR(sb.st_mode); #endif } FilePathVec FilePath::getFilesInDirectory(const string& extension) const { FilePathVec files; #if defined(_WIN32) WIN32_FIND_DATAA fd; string wildcard = "*." + extension; HANDLE hFind = FindFirstFileA((*this / wildcard).asString().c_str(), &fd); if (hFind != INVALID_HANDLE_VALUE) { do { if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { files.emplace_back(fd.cFileName); } } while (FindNextFileA(hFind, &fd)); FindClose(hFind); } #else DIR* dir = opendir(asString().c_str()); if (dir) { while (struct dirent* entry = readdir(dir)) { if (entry->d_type != DT_DIR && FilePath(entry->d_name).getExtension() == extension) { files.push_back(FilePath(entry->d_name)); } } closedir(dir); } #endif return files; } FilePathVec FilePath::getSubDirectories() const { if (!isDirectory()) { return FilePathVec(); } FilePathVec dirs{ *this }; #if defined(_WIN32) WIN32_FIND_DATAA fd; string wildcard = "*"; HANDLE hFind = FindFirstFileA((*this / wildcard).asString().c_str(), &fd); if (hFind != INVALID_HANDLE_VALUE) { do { string path = fd.cFileName; if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (path != "." && path != "..")) { FilePath newDir = *this / path; FilePathVec newDirs = newDir.getSubDirectories(); dirs.insert(dirs.end(), newDirs.begin(), newDirs.end()); } } while (FindNextFileA(hFind, &fd)); FindClose(hFind); } #else DIR* dir = opendir(asString().c_str()); if (dir) { while (struct dirent* entry = readdir(dir)) { string path = entry->d_name; if (path == "." || path == "..") { continue; } auto d_type = entry->d_type; FilePath newDir = *this / path; if (d_type == DT_UNKNOWN) { if (newDir.isDirectory()) { d_type = DT_DIR; } } if (d_type == DT_DIR) { FilePathVec newDirs = newDir.getSubDirectories(); dirs.insert(dirs.end(), newDirs.begin(), newDirs.end()); } } closedir(dir); } #endif return dirs; } void FilePath::createDirectory() const { #if defined(_WIN32) _mkdir(asString().c_str()); #else mkdir(asString().c_str(), 0777); #endif } bool FilePath::setCurrentPath() { #if defined(_WIN32) return (_chdir(asString().c_str()) == 0); #else return (chdir(asString().c_str()) == 0); #endif } FilePath FilePath::getCurrentPath() { #if defined(_WIN32) std::array buf; if (!GetCurrentDirectoryA(MAX_PATH, buf.data())) { throw Exception("Error in getCurrentPath: " + std::to_string(GetLastError())); } return FilePath(buf.data()); #else std::array buf; if (getcwd(buf.data(), PATH_MAX) == NULL) { throw Exception("Error in getCurrentPath: " + string(strerror(errno))); } return FilePath(buf.data()); #endif } FilePath FilePath::getModulePath() { #if defined(_WIN32) vector buf(MAX_PATH); while (true) { uint32_t reqSize = GetModuleFileNameA(NULL, buf.data(), (uint32_t) buf.size()); if (!reqSize) { throw Exception("Error in getModulePath: " + std::to_string(GetLastError())); } else if ((size_t) reqSize >= buf.size()) { buf.resize(buf.size() * 2); } else { return FilePath(buf.data()).getParentPath(); } } #elif defined(__APPLE__) vector buf(PATH_MAX); while (true) { uint32_t reqSize = static_cast(buf.size()); if (_NSGetExecutablePath(buf.data(), &reqSize) == -1) { buf.resize((size_t) reqSize); } else { return FilePath(buf.data()).getParentPath(); } } #else vector buf(PATH_MAX); while (true) { ssize_t reqSize = readlink("/proc/self/exe", buf.data(), buf.size()); if (reqSize == -1) { throw Exception("Error in getModulePath: " + string(strerror(errno))); } else if ((size_t) reqSize >= buf.size()) { buf.resize(buf.size() * 2); } else { buf.data()[reqSize] = '\0'; return FilePath(buf.data()).getParentPath(); } } #endif } FileSearchPath getEnvironmentPath(const string& sep) { string searchPathEnv = getEnviron(MATERIALX_SEARCH_PATH_ENV_VAR); return FileSearchPath(searchPathEnv, sep); } MATERIALX_NAMESPACE_END