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
24namespace 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