#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2021 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License // If the library is compiled as a DLL, this ensures symbols are exported #define NUCLEX_STORAGE_SOURCE 1 #include "WindowsDirectoryContainer.h" #if defined(NUCLEX_STORAGE_WIN32) #include "Nuclex/Storage/FileSystem/Path.h" #include "WindowsFileApi.h" #include "Nuclex/Support/Text/StringConverter.h" #include namespace { // ------------------------------------------------------------------------------------------- // /// Checks if the specified name indicates the current or parent directory /// Name that will be checked /// True if the name indicates the current or parent directory /// /// Name must be at least 3 characters long. /// bool isCurrentOrParentDirectoryName(const wchar_t *name) { return ( (name[0] == L'.') && ( (name[1] == 0) || ( (name[1] == L'.') && (name[2] == 0) ) ) ); } // ------------------------------------------------------------------------------------------- // /// Enumerates the contents of a directory /// Absolute path of the directory whose contents will be enumerated /// Filenames to look for /// /// Set to true to enumerate subdirectories rather than files below the specified path /// /// /// The absolute paths of all files or subdirectories under the specified path /// std::vector enumerateDirectoryContents( const std::string &path, const std::string &wildcard, bool subDirectoriesInsteadOfFiles ) { using Nuclex::Storage::FileSystem::Windows::WindowsFileApi; std::vector result; std::string searchSpec = Nuclex::Storage::FileSystem::Path::Join(path, wildcard); ::WIN32_FIND_DATAW findData; HANDLE searchHandle = WindowsFileApi::FindFirstFile(searchSpec, findData); { Nuclex::Storage::FileSystem::Windows::SearchScope searchScope(searchHandle); for(;;) { // Check if this is the current or parent directory reference. // d_name is a fixed-size char array, we can safely assume it will always // hold at least the 3 characters we're touching. if(!isCurrentOrParentDirectoryName(findData.cFileName)) { bool isDirectory = ( ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) || ((findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) ); if(isDirectory == subDirectoriesInsteadOfFiles) { std::string utf8Filename = Nuclex::Support::Text::StringConverter::Utf8FromWide( findData.cFileName ); #if defined(NUCLEX_STORAGE_ENUMERATION_RETURNS_FULL_PATHS) result.push_back(Nuclex::Storage::FileSystem::Path::Join(path, utf8Filename)); #else result.push_back(std::move(utf8Filename)); #endif } } // Advance to the next file or directory if(!WindowsFileApi::FindNextFile(searchHandle, findData)) { break; } } } return result; } // ------------------------------------------------------------------------------------------- // /// Extracts the name of a directory from its absolute path /// /// Absolute path to a directory whose name will be extracted /// /// The name of the directory at the specified absolute path std::string getDirectoryName(const std::string &absolutePath) { std::string::size_type pathLength = absolutePath.length(); if(pathLength == 0) { #if defined(NUCLEX_STORAGE_WIN32) return std::string(u8"C:\\"); // TODO: Look for CWD or system drive? #else return std::string(u8"/"); #endif } #if defined(NUCLEX_STORAGE_WIN32) bool endsWithDirectorySeparator = ( (absolutePath[pathLength - 1] == '/') || (absolutePath[pathLength - 1] == '\\') ); #else bool endsWithDirectorySeparator = (absolutePath[pathLength - 1] == '/'); #endif std::string name; if(endsWithDirectorySeparator) { name = Nuclex::Storage::FileSystem::Path::GetFilename( absolutePath.substr(0, pathLength - 1) ); } else { name = Nuclex::Storage::FileSystem::Path::GetFilename(absolutePath); } if(name.empty()) { #if defined(NUCLEX_STORAGE_WIN32) return std::string(u8"C:\\"); // TODO: Look for CWD or system drive? #else return std::string(u8"/"); #endif } return name; } // ------------------------------------------------------------------------------------------- // } // anonymous namespace namespace Nuclex { namespace Storage { namespace FileSystem { namespace Windows { // ------------------------------------------------------------------------------------------- // WindowsDirectoryContainer::WindowsDirectoryContainer(const std::string &absolutePath) : absolutePath(absolutePath), name(getDirectoryName(absolutePath)) {} // ------------------------------------------------------------------------------------------- // std::vector WindowsDirectoryContainer::EnumerateContainers( const std::string &wildcard /* = u8"*" */ ) const { return enumerateDirectoryContents(this->absolutePath, wildcard, true); } // ------------------------------------------------------------------------------------------- // std::vector WindowsDirectoryContainer::EnumerateFiles( const std::string &wildcard /* = u8"*" */ ) const { return enumerateDirectoryContents(this->absolutePath, wildcard, false); } // ------------------------------------------------------------------------------------------- // std::shared_ptr WindowsDirectoryContainer::AccessContainer( const std::string &containerName ) { bool containsDirectorySeparators = ( (containerName.find('/') != std::string::npos) || (containerName.find('\\') != std::string::npos) ); if(containsDirectorySeparators) { throw std::runtime_error(u8"Invalid subdirectory name"); } return std::make_shared( Path::Join(this->absolutePath, containerName) ); } // ------------------------------------------------------------------------------------------- // std::shared_ptr WindowsDirectoryContainer::AccessContainer( const std::string &containerName ) const { bool containsDirectorySeparators = ( (containerName.find('/') != std::string::npos) || (containerName.find('\\') != std::string::npos) ); if(containsDirectorySeparators) { throw std::runtime_error(u8"Invalid subdirectory name"); } return std::make_shared( Path::Join(this->absolutePath, containerName) ); } // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Storage::FileSystem::Windows #endif // defined(NUCLEX_STORAGE_WIN32)