diff --git a/applications/test/readDir/Make/files b/applications/test/readDir/Make/files new file mode 100644 index 0000000000..4ef8e0d2b1 --- /dev/null +++ b/applications/test/readDir/Make/files @@ -0,0 +1,3 @@ +Test-readDir.C + +EXE = $(FOAM_USER_APPBIN)/Test-readDir diff --git a/applications/test/readDir/Make/options b/applications/test/readDir/Make/options new file mode 100644 index 0000000000..4e772fdf9d --- /dev/null +++ b/applications/test/readDir/Make/options @@ -0,0 +1,2 @@ +/* EXE_INC = -I$(LIB_SRC)/finiteVolume/lnInclude */ +/* EXE_LIBS = -lfiniteVolume */ diff --git a/applications/test/readDir/Test-readDir.C b/applications/test/readDir/Test-readDir.C new file mode 100644 index 0000000000..17827e8ae7 --- /dev/null +++ b/applications/test/readDir/Test-readDir.C @@ -0,0 +1,74 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2019 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM 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 GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see . + +Description + Test functionality of Foam::readDir + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "OSspecific.H" +#include "fileNameList.H" + +using namespace Foam; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Main program: + +int main(int argc, char *argv[]) +{ + argList::noBanner(); + argList::noParallel(); + argList::addBoolOption("dir", "list directories instead of files"); + + #include "setRootCase.H" + + fileName::Type listType = fileName::FILE; + + if (args.found("dir")) + { + Info<< "Listing directories" << nl; + listType = fileName::DIRECTORY; + } + else + { + Info<< "Listing files" << nl; + } + + + { + Info<< nl; + for (const word& item : readDir(".", listType)) + { + Info<< " " << item << nl; + } + } + + + Info<< "\nEnd\n" << endl; + + return 0; +} + + +// ************************************************************************* // diff --git a/src/OSspecific/POSIX/POSIX.C b/src/OSspecific/POSIX/POSIX.C index 652aa9e212..57ffb64476 100644 --- a/src/OSspecific/POSIX/POSIX.C +++ b/src/OSspecific/POSIX/POSIX.C @@ -104,6 +104,141 @@ static inline void redirects(const bool bg) } +// * * * * * * * * * * * * * * * * Local Classes * * * * * * * * * * * * * * // + +namespace Foam +{ +namespace POSIX +{ + +//- A simple directory contents iterator +class directoryIterator +{ + DIR* dirptr_; + + bool exists_; + + bool hidden_; + + std::string item_; + + //- Accept file/dir name + inline bool accept() const + { + return + ( + item_.size() && item_ != "." && item_ != ".." + && (hidden_ || item_[0] != '.') + ); + } + + +public: + + // Constructors + + //- Construct for dirName, optionally allowing hidden files/dirs + directoryIterator(const fileName& dirName, bool allowHidden = false) + : + dirptr_(nullptr), + exists_(false), + hidden_(allowHidden), + item_() + { + if (!dirName.empty()) + { + dirptr_ = ::opendir(dirName.c_str()); + exists_ = (dirptr_ != nullptr); + next(); // Move to first element + } + } + + + //- Destructor + ~directoryIterator() + { + close(); + } + + + // Member Functions + + //- Directory open succeeded + bool exists() const + { + return exists_; + } + + //- Directory pointer is valid + bool good() const + { + return dirptr_; + } + + //- Close directory + void close() + { + if (dirptr_) + { + ::closedir(dirptr_); + dirptr_ = nullptr; + } + } + + //- The current item + const std::string& val() const + { + return item_; + } + + //- Read next item, always ignoring "." and ".." entries. + // Normally also ignore hidden files/dirs (beginning with '.') + // Automatically close when there are no more items + bool next() + { + struct dirent *list; + + while (dirptr_ && (list = ::readdir(dirptr_)) != nullptr) + { + item_ = list->d_name; + + if (accept()) + { + return true; + } + } + close(); // No more items + + return false; + } + + + // Member Operators + + //- Same as good() + operator bool() const + { + return good(); + } + + //- Same as val() + const std::string& operator*() const + { + return val(); + } + + //- Same as next() + directoryIterator& operator++() + { + next(); + return *this; + } +}; + +} // End namespace POSIX +} // End namespace Foam + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // pid_t Foam::pid() @@ -743,7 +878,7 @@ Foam::fileNameList Foam::readDir ) { // Initial filename list size and the increment when resizing the list - static const int maxNnames = 100; + constexpr int maxNnames = 100; // Basic sanity: cannot strip '.gz' from directory names const bool stripgz = filtergz && (type != fileName::DIRECTORY); @@ -751,14 +886,10 @@ Foam::fileNameList Foam::readDir fileNameList dirEntries; - // Open directory and set the structure pointer - // Do not attempt to open an empty directory name - DIR *source; - if - ( - directory.empty() - || (source = ::opendir(directory.c_str())) == nullptr - ) + // Iterate contents (ignores an empty directory name) + + POSIX::directoryIterator dirIter(directory); + if (!dirIter.exists()) { if (POSIX::debug) { @@ -781,19 +912,12 @@ Foam::fileNameList Foam::readDir label nFailed = 0; // Entries with invalid characters label nEntries = 0; // Number of selected entries - dirEntries.setSize(maxNnames); + dirEntries.resize(maxNnames); - // Read and parse all the entries in the directory - for (struct dirent *list; (list = ::readdir(source)) != nullptr; /*nil*/) + // Process the directory entries + for (/*nil*/; dirIter; ++dirIter) { - const std::string item(list->d_name); - - // Ignore files/directories beginning with "." - // These are the ".", ".." directories and any hidden files/dirs - if (item.empty() || item[0] == '.') - { - continue; - } + const std::string& item = *dirIter; // Validate filename without spaces, quotes, etc in the name. // No duplicate slashes to strip - dirent will not have them anyhow. @@ -813,7 +937,7 @@ Foam::fileNameList Foam::readDir { if (nEntries >= dirEntries.size()) { - dirEntries.setSize(dirEntries.size() + maxNnames); + dirEntries.resize(dirEntries.size() + maxNnames); } if (stripgz && name.hasExt(extgz)) @@ -827,10 +951,9 @@ Foam::fileNameList Foam::readDir } } } - ::closedir(source); // Finalize the length of the entries list - dirEntries.setSize(nEntries); + dirEntries.resize(nEntries); if (nFailed && POSIX::debug) { @@ -1171,14 +1294,11 @@ bool Foam::rm(const fileName& file) bool Foam::rmDir(const fileName& directory, const bool silent) { - // Open directory and set the structure pointer - // Do not attempt to open an empty directory name - DIR *source; - if - ( - directory.empty() - || (source = ::opendir(directory.c_str())) == nullptr - ) + // Iterate contents (ignores an empty directory name) + // Also retain hidden files/dirs for removal + + POSIX::directoryIterator dirIter(directory, true); + if (!dirIter.exists()) { if (!silent) { @@ -1201,18 +1321,13 @@ bool Foam::rmDir(const fileName& directory, const bool silent) // Process each directory entry, counting any errors encountered label nErrors = 0; - for (struct dirent *list; (list = ::readdir(source)) != nullptr; /*nil*/) - { - const std::string item(list->d_name); - // Ignore "." and ".." directories - if (item.empty() || item == "." || item == "..") - { - continue; - } + for (/*nil*/; dirIter; ++dirIter) + { + const std::string& item = *dirIter; // Allow invalid characters (spaces, quotes, etc), - // otherwise we cannot subdirs with these types of names. + // otherwise we cannot remove subdirs with these types of names. // -> const fileName path = directory/name; <- const fileName path(fileName::concat(directory, item)); @@ -1256,7 +1371,6 @@ bool Foam::rmDir(const fileName& directory, const bool silent) } // clean up - ::closedir(source); return !nErrors; }