mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
DirectoryIterator: Avoid recursing into previously-visited directories
This commit is contained in:
parent
4cf036bb8b
commit
00e7fbf1c2
7 changed files with 95 additions and 24 deletions
|
|
@ -75,13 +75,26 @@ bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fi
|
||||||
|
|
||||||
if (! filename.containsOnly ("."))
|
if (! filename.containsOnly ("."))
|
||||||
{
|
{
|
||||||
|
const auto fullPath = File::createFileWithoutCheckingPath (path + filename);
|
||||||
bool matches = false;
|
bool matches = false;
|
||||||
|
|
||||||
if (isDirectory)
|
if (isDirectory)
|
||||||
{
|
{
|
||||||
if (isRecursive && ((whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden))
|
const auto mayRecurseIntoPossibleHiddenDir = [this, &isHidden]
|
||||||
subIterator.reset (new DirectoryIterator (File::createFileWithoutCheckingPath (path + filename),
|
{
|
||||||
true, wildCard, whatToLookFor));
|
return (whatToLookFor & File::ignoreHiddenFiles) == 0 || ! isHidden;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto mayRecurseIntoPossibleSymlink = [this, &fullPath]
|
||||||
|
{
|
||||||
|
return followSymlinks == File::FollowSymlinks::yes
|
||||||
|
|| ! fullPath.isSymbolicLink()
|
||||||
|
|| (followSymlinks == File::FollowSymlinks::noCycles
|
||||||
|
&& knownPaths->find (fullPath.getLinkedTarget()) == knownPaths->end());
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isRecursive && mayRecurseIntoPossibleHiddenDir() && mayRecurseIntoPossibleSymlink())
|
||||||
|
subIterator.reset (new DirectoryIterator (fullPath, true, wildCard, whatToLookFor, followSymlinks, knownPaths));
|
||||||
|
|
||||||
matches = (whatToLookFor & File::findDirectories) != 0;
|
matches = (whatToLookFor & File::findDirectories) != 0;
|
||||||
}
|
}
|
||||||
|
|
@ -99,7 +112,7 @@ bool DirectoryIterator::next (bool* isDirResult, bool* isHiddenResult, int64* fi
|
||||||
|
|
||||||
if (matches)
|
if (matches)
|
||||||
{
|
{
|
||||||
currentFile = File::createFileWithoutCheckingPath (path + filename);
|
currentFile = fullPath;
|
||||||
if (isHiddenResult != nullptr) *isHiddenResult = isHidden;
|
if (isHiddenResult != nullptr) *isHiddenResult = isHidden;
|
||||||
if (isDirResult != nullptr) *isDirResult = isDirectory;
|
if (isDirResult != nullptr) *isDirResult = isDirectory;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,10 @@ namespace juce
|
||||||
A DirectoryIterator will search through a directory and its subdirectories using
|
A DirectoryIterator will search through a directory and its subdirectories using
|
||||||
a wildcard filepattern match.
|
a wildcard filepattern match.
|
||||||
|
|
||||||
|
The iterator keeps track of directories that it has previously traversed, and will
|
||||||
|
skip any previously-seen directories in the case of cycles caused by symbolic links.
|
||||||
|
It is also possible to avoid following symbolic links altogether.
|
||||||
|
|
||||||
If you may be scanning a large number of files, it's usually smarter to use this
|
If you may be scanning a large number of files, it's usually smarter to use this
|
||||||
class than File::findChildFiles() because it allows you to stop at any time, rather
|
class than File::findChildFiles() because it allows you to stop at any time, rather
|
||||||
than having to wait for the entire scan to finish before getting the results.
|
than having to wait for the entire scan to finish before getting the results.
|
||||||
|
|
@ -73,17 +77,10 @@ public:
|
||||||
DirectoryIterator (const File& directory,
|
DirectoryIterator (const File& directory,
|
||||||
bool recursive,
|
bool recursive,
|
||||||
const String& pattern = "*",
|
const String& pattern = "*",
|
||||||
int type = File::findFiles)
|
int type = File::findFiles,
|
||||||
: wildCards (parseWildcards (pattern)),
|
File::FollowSymlinks follow = File::FollowSymlinks::yes)
|
||||||
fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern),
|
: DirectoryIterator (directory, recursive, pattern, type, follow, nullptr)
|
||||||
wildCard (pattern),
|
|
||||||
path (File::addTrailingSeparator (directory.getFullPathName())),
|
|
||||||
whatToLookFor (type),
|
|
||||||
isRecursive (recursive)
|
|
||||||
{
|
{
|
||||||
// you have to specify the type of files you're looking for!
|
|
||||||
jassert ((whatToLookFor & (File::findFiles | File::findDirectories)) != 0);
|
|
||||||
jassert (whatToLookFor > 0 && whatToLookFor <= 7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Moves the iterator along to the next file.
|
/** Moves the iterator along to the next file.
|
||||||
|
|
@ -126,6 +123,39 @@ public:
|
||||||
float getEstimatedProgress() const;
|
float getEstimatedProgress() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
using KnownPaths = std::set<File>;
|
||||||
|
|
||||||
|
DirectoryIterator (const File& directory,
|
||||||
|
bool recursive,
|
||||||
|
const String& pattern,
|
||||||
|
int type,
|
||||||
|
File::FollowSymlinks follow,
|
||||||
|
KnownPaths* seenPaths)
|
||||||
|
: wildCards (parseWildcards (pattern)),
|
||||||
|
fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern),
|
||||||
|
wildCard (pattern),
|
||||||
|
path (File::addTrailingSeparator (directory.getFullPathName())),
|
||||||
|
whatToLookFor (type),
|
||||||
|
isRecursive (recursive),
|
||||||
|
followSymlinks (follow),
|
||||||
|
knownPaths (seenPaths)
|
||||||
|
{
|
||||||
|
// you have to specify the type of files you're looking for!
|
||||||
|
jassert ((whatToLookFor & (File::findFiles | File::findDirectories)) != 0);
|
||||||
|
jassert (whatToLookFor > 0 && whatToLookFor <= 7);
|
||||||
|
|
||||||
|
if (followSymlinks == File::FollowSymlinks::noCycles)
|
||||||
|
{
|
||||||
|
if (knownPaths == nullptr)
|
||||||
|
{
|
||||||
|
heapKnownPaths = std::make_unique<KnownPaths>();
|
||||||
|
knownPaths = heapKnownPaths.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
knownPaths->insert (directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
struct NativeIterator
|
struct NativeIterator
|
||||||
{
|
{
|
||||||
|
|
@ -152,6 +182,9 @@ private:
|
||||||
bool hasBeenAdvanced = false;
|
bool hasBeenAdvanced = false;
|
||||||
std::unique_ptr<DirectoryIterator> subIterator;
|
std::unique_ptr<DirectoryIterator> subIterator;
|
||||||
File currentFile;
|
File currentFile;
|
||||||
|
File::FollowSymlinks followSymlinks = File::FollowSymlinks::yes;
|
||||||
|
KnownPaths* knownPaths = nullptr;
|
||||||
|
std::unique_ptr<KnownPaths> heapKnownPaths;
|
||||||
|
|
||||||
static StringArray parseWildcards (const String& pattern);
|
static StringArray parseWildcards (const String& pattern);
|
||||||
static bool fileMatches (const StringArray& wildCards, const String& filename);
|
static bool fileMatches (const StringArray& wildCards, const String& filename);
|
||||||
|
|
|
||||||
|
|
@ -561,18 +561,18 @@ void File::readLines (StringArray& destLines) const
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
Array<File> File::findChildFiles (int whatToLookFor, bool searchRecursively, const String& wildcard) const
|
Array<File> File::findChildFiles (int whatToLookFor, bool searchRecursively, const String& wildcard, FollowSymlinks followSymlinks) const
|
||||||
{
|
{
|
||||||
Array<File> results;
|
Array<File> results;
|
||||||
findChildFiles (results, whatToLookFor, searchRecursively, wildcard);
|
findChildFiles (results, whatToLookFor, searchRecursively, wildcard, followSymlinks);
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
int File::findChildFiles (Array<File>& results, int whatToLookFor, bool searchRecursively, const String& wildcard) const
|
int File::findChildFiles (Array<File>& results, int whatToLookFor, bool searchRecursively, const String& wildcard, FollowSymlinks followSymlinks) const
|
||||||
{
|
{
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
for (const auto& di : RangedDirectoryIterator (*this, searchRecursively, wildcard, whatToLookFor))
|
for (const auto& di : RangedDirectoryIterator (*this, searchRecursively, wildcard, whatToLookFor, followSymlinks))
|
||||||
{
|
{
|
||||||
results.add (di.getFile());
|
results.add (di.getFile());
|
||||||
++total;
|
++total;
|
||||||
|
|
|
||||||
|
|
@ -560,6 +560,23 @@ public:
|
||||||
ignoreHiddenFiles = 4 /**< Add this flag to avoid returning any hidden files in the results. */
|
ignoreHiddenFiles = 4 /**< Add this flag to avoid returning any hidden files in the results. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class FollowSymlinks
|
||||||
|
{
|
||||||
|
/** Requests that a file system traversal should not follow any symbolic links. */
|
||||||
|
no,
|
||||||
|
|
||||||
|
/** Requests that a file system traversal may follow symbolic links, but should attempt to
|
||||||
|
skip any symbolic links to directories that may cause a cycle.
|
||||||
|
*/
|
||||||
|
noCycles,
|
||||||
|
|
||||||
|
/** Requests that a file system traversal follow all symbolic links. Use with care, as this
|
||||||
|
may produce inconsistent results, or fail to terminate, if the filesystem contains cycles
|
||||||
|
due to symbolic links.
|
||||||
|
*/
|
||||||
|
yes
|
||||||
|
};
|
||||||
|
|
||||||
/** Searches this directory for files matching a wildcard pattern.
|
/** Searches this directory for files matching a wildcard pattern.
|
||||||
|
|
||||||
Assuming that this file is a directory, this method will search it
|
Assuming that this file is a directory, this method will search it
|
||||||
|
|
@ -572,13 +589,15 @@ public:
|
||||||
@param searchRecursively if true, all subdirectories will be recursed into to do
|
@param searchRecursively if true, all subdirectories will be recursed into to do
|
||||||
an exhaustive search
|
an exhaustive search
|
||||||
@param wildCardPattern the filename pattern to search for, e.g. "*.txt"
|
@param wildCardPattern the filename pattern to search for, e.g. "*.txt"
|
||||||
|
@param followSymlinks the method that should be used to handle symbolic links
|
||||||
@returns the set of files that were found
|
@returns the set of files that were found
|
||||||
|
|
||||||
@see getNumberOfChildFiles, RangedDirectoryIterator
|
@see getNumberOfChildFiles, RangedDirectoryIterator
|
||||||
*/
|
*/
|
||||||
Array<File> findChildFiles (int whatToLookFor,
|
Array<File> findChildFiles (int whatToLookFor,
|
||||||
bool searchRecursively,
|
bool searchRecursively,
|
||||||
const String& wildCardPattern = "*") const;
|
const String& wildCardPattern = "*",
|
||||||
|
FollowSymlinks followSymlinks = FollowSymlinks::yes) const;
|
||||||
|
|
||||||
/** Searches inside a directory for files matching a wildcard pattern.
|
/** Searches inside a directory for files matching a wildcard pattern.
|
||||||
Note that there's a newer, better version of this method which returns the results
|
Note that there's a newer, better version of this method which returns the results
|
||||||
|
|
@ -586,7 +605,8 @@ public:
|
||||||
mainly for legacy code to use.
|
mainly for legacy code to use.
|
||||||
*/
|
*/
|
||||||
int findChildFiles (Array<File>& results, int whatToLookFor,
|
int findChildFiles (Array<File>& results, int whatToLookFor,
|
||||||
bool searchRecursively, const String& wildCardPattern = "*") const;
|
bool searchRecursively, const String& wildCardPattern = "*",
|
||||||
|
FollowSymlinks followSymlinks = FollowSymlinks::yes) const;
|
||||||
|
|
||||||
/** Searches inside a directory and counts how many files match a wildcard pattern.
|
/** Searches inside a directory and counts how many files match a wildcard pattern.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,13 @@ float DirectoryEntry::getEstimatedProgress() const
|
||||||
RangedDirectoryIterator::RangedDirectoryIterator (const File& directory,
|
RangedDirectoryIterator::RangedDirectoryIterator (const File& directory,
|
||||||
bool isRecursive,
|
bool isRecursive,
|
||||||
const String& wildCard,
|
const String& wildCard,
|
||||||
int whatToLookFor)
|
int whatToLookFor,
|
||||||
|
File::FollowSymlinks followSymlinks)
|
||||||
: iterator (new DirectoryIterator (directory,
|
: iterator (new DirectoryIterator (directory,
|
||||||
isRecursive,
|
isRecursive,
|
||||||
wildCard,
|
wildCard,
|
||||||
whatToLookFor))
|
whatToLookFor,
|
||||||
|
followSymlinks))
|
||||||
{
|
{
|
||||||
entry.iterator = iterator;
|
entry.iterator = iterator;
|
||||||
increment();
|
increment();
|
||||||
|
|
|
||||||
|
|
@ -118,11 +118,13 @@ public:
|
||||||
separated by a semi-colon or comma, e.g. "*.jpg;*.png"
|
separated by a semi-colon or comma, e.g. "*.jpg;*.png"
|
||||||
@param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying
|
@param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying
|
||||||
whether to look for files, directories, or both.
|
whether to look for files, directories, or both.
|
||||||
|
@param followSymlinks the policy to use when symlinks are encountered
|
||||||
*/
|
*/
|
||||||
RangedDirectoryIterator (const File& directory,
|
RangedDirectoryIterator (const File& directory,
|
||||||
bool isRecursive,
|
bool isRecursive,
|
||||||
const String& wildCard = "*",
|
const String& wildCard = "*",
|
||||||
int whatToLookFor = File::findFiles);
|
int whatToLookFor = File::findFiles,
|
||||||
|
File::FollowSymlinks followSymlinks = File::FollowSymlinks::yes);
|
||||||
|
|
||||||
/** Returns true if both iterators are in their end/sentinel state,
|
/** Returns true if both iterators are in their end/sentinel state,
|
||||||
otherwise returns false.
|
otherwise returns false.
|
||||||
|
|
|
||||||
|
|
@ -55,13 +55,14 @@
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <unordered_map>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <set>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <typeindex>
|
#include <typeindex>
|
||||||
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue