1 | /* |
2 | * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"). |
5 | * You may not use this file except in compliance with the License. |
6 | * A copy of the License is located at |
7 | * |
8 | * http://aws.amazon.com/apache2.0 |
9 | * |
10 | * or in the "license" file accompanying this file. This file is distributed |
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
12 | * express or implied. See the License for the specific language governing |
13 | * permissions and limitations under the License. |
14 | */ |
15 | |
16 | #include <aws/core/platform/FileSystem.h> |
17 | #include <aws/core/utils/memory/stl/AWSQueue.h> |
18 | #include <aws/core/utils/memory/stl/AWSStringStream.h> |
19 | #include <aws/core/utils/memory/stl/AWSStreamFwd.h> |
20 | #include <aws/core/utils/StringUtils.h> |
21 | #include <fstream> |
22 | #include <cassert> |
23 | |
24 | namespace Aws |
25 | { |
26 | namespace FileSystem |
27 | { |
28 | Aws::String Join(const Aws::String& leftSegment, const Aws::String& rightSegment) |
29 | { |
30 | return Join(PATH_DELIM, leftSegment, rightSegment); |
31 | } |
32 | |
33 | Aws::String Join(char delimiter, const Aws::String& leftSegment, const Aws::String& rightSegment) |
34 | { |
35 | Aws::StringStream ss; |
36 | |
37 | if (!leftSegment.empty()) |
38 | { |
39 | if (leftSegment.back() == delimiter) |
40 | { |
41 | ss << leftSegment.substr(0, leftSegment.length() - 1); |
42 | } |
43 | else |
44 | { |
45 | ss << leftSegment; |
46 | } |
47 | } |
48 | |
49 | ss << delimiter; |
50 | |
51 | if (!rightSegment.empty()) |
52 | { |
53 | if (rightSegment.front() == delimiter) |
54 | { |
55 | ss << rightSegment.substr(1); |
56 | } |
57 | else |
58 | { |
59 | ss << rightSegment; |
60 | } |
61 | } |
62 | |
63 | return ss.str(); |
64 | } |
65 | |
66 | bool DeepCopyDirectory(const char* from, const char* to) |
67 | { |
68 | if (!from || !to) return false; |
69 | |
70 | DirectoryTree fromDir(from); |
71 | |
72 | if (!fromDir) return false; |
73 | |
74 | CreateDirectoryIfNotExists(to); |
75 | DirectoryTree toDir(to); |
76 | |
77 | if (!toDir) return false; |
78 | |
79 | bool success(true); |
80 | |
81 | auto visitor = [to,&success](const DirectoryTree*, const DirectoryEntry& entry) |
82 | { |
83 | auto newPath = Aws::FileSystem::Join(to, entry.relativePath); |
84 | |
85 | if (entry.fileType == Aws::FileSystem::FileType::File) |
86 | { |
87 | Aws::OFStream copyOutStream(newPath.c_str()); |
88 | Aws::IFStream originalStream(entry.path.c_str()); |
89 | |
90 | if(!copyOutStream.good() || !originalStream.good()) |
91 | { |
92 | success = false; |
93 | return false; |
94 | } |
95 | |
96 | std::copy(std::istreambuf_iterator<char>(originalStream), |
97 | std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(copyOutStream)); |
98 | } |
99 | else if (entry.fileType == Aws::FileSystem::FileType::Directory) |
100 | { |
101 | success = CreateDirectoryIfNotExists(newPath.c_str()); |
102 | return success; |
103 | } |
104 | |
105 | return success; |
106 | }; |
107 | |
108 | fromDir.TraverseDepthFirst(visitor); |
109 | return success; |
110 | } |
111 | |
112 | bool DeepDeleteDirectory(const char* toDelete) |
113 | { |
114 | bool success(true); |
115 | |
116 | //scope this to a new stack frame, because we won't be able to delete the root directory |
117 | //unless the directory handle has closed. |
118 | { |
119 | DirectoryTree delDir(toDelete); |
120 | |
121 | if (!delDir) return false; |
122 | |
123 | auto visitor = [&success](const DirectoryTree*, const DirectoryEntry& entry) |
124 | { |
125 | if (entry.fileType == FileType::File) |
126 | { |
127 | success = RemoveFileIfExists(entry.path.c_str()); |
128 | } |
129 | else |
130 | { |
131 | success = RemoveDirectoryIfExists(entry.path.c_str()); |
132 | } |
133 | |
134 | return success; |
135 | }; |
136 | |
137 | delDir.TraverseDepthFirst(visitor, true); |
138 | } |
139 | |
140 | if (success) |
141 | { |
142 | success = RemoveDirectoryIfExists(toDelete); |
143 | } |
144 | |
145 | return success; |
146 | } |
147 | |
148 | Directory::Directory(const Aws::String& path, const Aws::String& relativePath) |
149 | { |
150 | auto trimmedPath = Utils::StringUtils::Trim(path.c_str()); |
151 | auto trimmedRelativePath = Utils::StringUtils::Trim(relativePath.c_str()); |
152 | |
153 | if (!trimmedPath.empty() && trimmedPath[trimmedPath.length() - 1] == PATH_DELIM) |
154 | { |
155 | m_directoryEntry.path = trimmedPath.substr(0, trimmedPath.length() - 1); |
156 | } |
157 | else |
158 | { |
159 | m_directoryEntry.path = trimmedPath; |
160 | } |
161 | |
162 | if (!trimmedRelativePath.empty() && trimmedRelativePath[trimmedRelativePath.length() - 1] == PATH_DELIM) |
163 | { |
164 | m_directoryEntry.relativePath = trimmedRelativePath.substr(0, trimmedRelativePath.length() - 1); |
165 | } |
166 | else |
167 | { |
168 | m_directoryEntry.relativePath = trimmedRelativePath; |
169 | } |
170 | } |
171 | |
172 | Aws::UniquePtr<Directory> Directory::Descend(const DirectoryEntry& directoryEntry) |
173 | { |
174 | assert(directoryEntry.fileType != FileType::File); |
175 | return OpenDirectory(directoryEntry.path, directoryEntry.relativePath); |
176 | } |
177 | |
178 | Aws::Vector<Aws::String> Directory::GetAllFilePathsInDirectory(const Aws::String& path) |
179 | { |
180 | Aws::FileSystem::DirectoryTree tree(path); |
181 | Aws::Vector<Aws::String> filesVector; |
182 | auto visitor = [&](const Aws::FileSystem::DirectoryTree*, const Aws::FileSystem::DirectoryEntry& entry) |
183 | { |
184 | if (entry.fileType == Aws::FileSystem::FileType::File) |
185 | { |
186 | filesVector.push_back(entry.path); |
187 | } |
188 | return true; |
189 | }; |
190 | tree.TraverseBreadthFirst(visitor); |
191 | return filesVector; |
192 | } |
193 | |
194 | DirectoryTree::DirectoryTree(const Aws::String& path) |
195 | { |
196 | m_dir = OpenDirectory(path); |
197 | } |
198 | |
199 | DirectoryTree::operator bool() const |
200 | { |
201 | return m_dir->operator bool(); |
202 | } |
203 | |
204 | bool DirectoryTree::operator==(DirectoryTree& other) |
205 | { |
206 | return Diff(other).size() == 0; |
207 | } |
208 | |
209 | bool DirectoryTree::operator==(const Aws::String& path) |
210 | { |
211 | return *this == DirectoryTree(path); |
212 | } |
213 | |
214 | Aws::Map<Aws::String, DirectoryEntry> DirectoryTree::Diff(DirectoryTree& other) |
215 | { |
216 | Aws::Map<Aws::String, DirectoryEntry> thisEntries; |
217 | auto thisTraversal = [&thisEntries](const DirectoryTree*, const DirectoryEntry& entry) |
218 | { |
219 | thisEntries[entry.relativePath] = entry; |
220 | return true; |
221 | }; |
222 | |
223 | Aws::Map<Aws::String, DirectoryEntry> otherEntries; |
224 | auto otherTraversal = [&thisEntries, &otherEntries](const DirectoryTree*, const DirectoryEntry& entry) |
225 | { |
226 | auto thisEntry = thisEntries.find(entry.relativePath); |
227 | if (thisEntry != thisEntries.end()) |
228 | { |
229 | thisEntries.erase(entry.relativePath); |
230 | } |
231 | else |
232 | { |
233 | otherEntries[entry.relativePath] = entry; |
234 | } |
235 | |
236 | return true; |
237 | }; |
238 | |
239 | TraverseDepthFirst(thisTraversal); |
240 | other.TraverseDepthFirst(otherTraversal); |
241 | |
242 | thisEntries.insert(otherEntries.begin(), otherEntries.end()); |
243 | return thisEntries; |
244 | } |
245 | |
246 | void DirectoryTree::TraverseDepthFirst(const DirectoryEntryVisitor& visitor, bool postOrderTraversal) |
247 | { |
248 | TraverseDepthFirst(*m_dir, visitor, postOrderTraversal); |
249 | m_dir = OpenDirectory(m_dir->GetPath()); |
250 | } |
251 | |
252 | void DirectoryTree::TraverseBreadthFirst(const DirectoryEntryVisitor& visitor) |
253 | { |
254 | TraverseBreadthFirst(*m_dir, visitor); |
255 | m_dir = OpenDirectory(m_dir->GetPath()); |
256 | } |
257 | |
258 | void DirectoryTree::TraverseBreadthFirst(Directory& dir, const DirectoryEntryVisitor& visitor) |
259 | { |
260 | if (!dir) |
261 | { |
262 | return; |
263 | } |
264 | |
265 | Aws::Queue<DirectoryEntry> queue; |
266 | while (DirectoryEntry&& entry = dir.Next()) |
267 | { |
268 | queue.push(std::move(entry)); |
269 | } |
270 | |
271 | while (queue.size() > 0) |
272 | { |
273 | auto entry = queue.front(); |
274 | queue.pop(); |
275 | if(visitor(this, entry)) |
276 | { |
277 | if(entry.fileType == FileType::Directory) |
278 | { |
279 | auto currentDir = dir.Descend(entry); |
280 | |
281 | while (DirectoryEntry&& dirEntry = currentDir->Next()) |
282 | { |
283 | queue.push(std::move(dirEntry)); |
284 | } |
285 | } |
286 | } |
287 | else |
288 | { |
289 | return; |
290 | } |
291 | } |
292 | } |
293 | |
294 | bool DirectoryTree::TraverseDepthFirst(Directory& dir, const DirectoryEntryVisitor& visitor, bool postOrder) |
295 | { |
296 | if (!dir) |
297 | { |
298 | return true; |
299 | } |
300 | |
301 | bool exitTraversal(false); |
302 | DirectoryEntry entry; |
303 | |
304 | while ((entry = dir.Next()) && !exitTraversal) |
305 | { |
306 | if(!postOrder) |
307 | { |
308 | if(!visitor(this, entry)) |
309 | { |
310 | return false; |
311 | } |
312 | } |
313 | |
314 | if (entry.fileType == FileType::Directory) |
315 | { |
316 | auto subDir = dir.Descend(entry); |
317 | exitTraversal = !TraverseDepthFirst(*subDir, visitor, postOrder); |
318 | } |
319 | |
320 | if (postOrder) |
321 | { |
322 | if (!visitor(this, entry)) |
323 | { |
324 | return false; |
325 | } |
326 | } |
327 | } |
328 | |
329 | return !exitTraversal; |
330 | } |
331 | |
332 | } |
333 | } |
334 | |