1//
2// RecursiveDirectoryIteratorStategies.cpp
3//
4// Library: Foundation
5// Package: Filesystem
6// Module: RecursiveDirectoryIterator
7//
8// Copyright (c) 2012, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/DirectoryIteratorStrategy.h"
16
17
18namespace Poco {
19
20
21//
22// TraverseBase
23//
24TraverseBase::TraverseBase(DepthFunPtr depthDeterminer, UInt16 maxDepth): _depthDeterminer(depthDeterminer),
25 _maxDepth(maxDepth)
26{
27}
28
29
30inline bool TraverseBase::isFiniteDepth()
31{
32 return _maxDepth != D_INFINITE;
33}
34
35
36bool TraverseBase::isDirectory(Poco::File& file)
37{
38 try
39 {
40 return file.isDirectory();
41 }
42 catch (...)
43 {
44 return false;
45 }
46}
47
48
49//
50// ChildrenFirstTraverse
51//
52ChildrenFirstTraverse::ChildrenFirstTraverse(DepthFunPtr depthDeterminer, UInt16 maxDepth):
53 TraverseBase(depthDeterminer, maxDepth)
54{
55}
56
57
58const std::string ChildrenFirstTraverse::next(Stack* itStack, bool* isFinished)
59{
60 // pointer mustn't point to NULL and iteration mustn't be finished
61 poco_check_ptr(isFinished);
62 poco_assert(!(*isFinished));
63
64 // go deeper into not empty directory
65 // (if depth limit allows)
66 bool isDepthLimitReached = isFiniteDepth() && _depthDeterminer(*itStack) >= _maxDepth;
67 if (!isDepthLimitReached && isDirectory(*itStack->top()))
68 {
69 try
70 {
71 DirectoryIterator child_it(itStack->top().path());
72 // check if directory is empty
73 if (child_it != _itEnd)
74 {
75 itStack->push(child_it);
76 return child_it->path();
77 }
78 }
79 catch (...)
80 {
81 // Failed to iterate child dir.
82 traverseError.notify(this, itStack->top()->path());
83 }
84 }
85
86 ++(itStack->top());
87
88 poco_assert(!itStack->empty());
89 // return up until there isn't right sibling
90 while (itStack->top() == _itEnd)
91 {
92 itStack->pop();
93
94 // detect end of traversal
95 if (itStack->empty())
96 {
97 *isFinished = true;
98 return _itEnd->path();
99 }
100 else
101 {
102 ++(itStack->top());
103 }
104 }
105
106 return itStack->top()->path();
107}
108
109
110//
111// SiblingsFirstTraverse
112//
113SiblingsFirstTraverse::SiblingsFirstTraverse(DepthFunPtr depthDeterminer, UInt16 maxDepth)
114 : TraverseBase(depthDeterminer, maxDepth)
115{
116 _dirsStack.push(std::queue<std::string>());
117}
118
119
120const std::string SiblingsFirstTraverse::next(Stack* itStack, bool* isFinished)
121{
122 // pointer mustn't point to NULL and iteration mustn't be finished
123 poco_check_ptr(isFinished);
124 poco_assert(!(*isFinished));
125
126 // add dirs to queue (if depth limit allows)
127 bool isDepthLimitReached = isFiniteDepth() && _depthDeterminer(*itStack) >= _maxDepth;
128 if (!isDepthLimitReached && isDirectory(*itStack->top()))
129 {
130 const std::string& p = itStack->top()->path();
131 _dirsStack.top().push(p);
132 }
133
134 ++(itStack->top());
135
136 poco_assert(!itStack->empty());
137 // return up until there isn't right sibling
138 while (itStack->top() == _itEnd)
139 {
140 // try to find first not empty directory and go deeper
141 while (!_dirsStack.top().empty())
142 {
143 std::string dir = _dirsStack.top().front();
144 _dirsStack.top().pop();
145
146 try
147 {
148 DirectoryIterator child_it(dir);
149
150 // check if directory is empty
151 if (child_it != _itEnd)
152 {
153 itStack->push(child_it);
154 _dirsStack.push(std::queue<std::string>());
155 return child_it->path();
156 }
157 }
158 catch (...)
159 {
160 // Failed to iterate child dir.
161 traverseError.notify(this, dir);
162 }
163 }
164
165 // if fail go upper
166 itStack->pop();
167 _dirsStack.pop();
168
169 // detect end of traversal
170 if (itStack->empty())
171 {
172 *isFinished = true;
173 return _itEnd->path();
174 }
175 }
176
177 return itStack->top()->path();
178}
179
180
181} // namespace Poco
182