TDME2  1.9.200
StandardFileSystem.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <array>
5 #include <filesystem>
6 #include <fstream>
7 #include <iostream>
8 #include <sstream>
9 #include <string>
10 #include <vector>
11 
12 #include <tdme/tdme.h>
14 #include <tdme/utilities/Console.h>
18 
19 using std::array;
20 using std::getline;
21 using std::ifstream;
22 using std::ios;
23 using std::ofstream;
24 using std::sort;
25 using std::string;
26 using std::stringstream;
27 using std::to_string;
28 using std::vector;
29 
31 
37 
38 StandardFileSystem::StandardFileSystem()
39 {
40 }
41 
43 {
44 }
45 
46 const string StandardFileSystem::composeURI(const string& pathName, const string& fileName) {
47  return pathName + "/" + fileName;
48 }
49 
50 void StandardFileSystem::list(const string& pathName, vector<string>& files, FileNameFilter* filter, bool addDrives)
51 {
52  auto _pathName = pathName;
53  if (StringTools::endsWith(_pathName, "/") == false) _pathName+= "/";
54 
55  try {
56  for (const auto& entry: std::filesystem::directory_iterator(std::filesystem::u8path(_pathName))) {
57  auto u8FileName = entry.path().filename().u8string();
58  string fileName(u8FileName.size(), 0);
59  for (auto i = 0; i < u8FileName.size(); i++) fileName[i] = u8FileName[i];
60  try {
61  if (filter != nullptr && filter->accept(pathName, fileName) == false) continue;
62  } catch (Exception& exception) {
63  Console::println("StandardFileSystem::list(): Filter::accept(): " + pathName + "/" + fileName + ": " + string(exception.what()));
64  continue;
65  }
66  files.push_back(fileName);
67  }
68  } catch (Exception& exception) {
69  throw FileSystemException("Unable to list path: " + pathName + ": " + string(exception.what()));
70  }
71 
72  //
73  sort(files.begin(), files.end());
74 
75  //
76  if (isDrive(_pathName) == false && _pathName.empty() == false && _pathName != "/") {
77  if (filter == nullptr || filter->accept(pathName, "..") == true) files.insert(files.begin(), "..");
78  }
79 
80  #if defined(_WIN32)
81  if (addDrives == true) {
82  for (char drive = 'A'; drive <= 'Z'; drive++) {
83  string fileName;
84  fileName+= drive;
85  fileName+= ":";
86  try {
87  if (exists(fileName + "/") == true) files.insert(files.begin() + (drive - 'C'), fileName);
88  } catch (Exception& exception) {
89  Console::println("StandardFileSystem::list(): fileExists(): " + pathName + "/" + fileName + ": " + string(exception.what()));
90  }
91  }
92  }
93  #endif
94 }
95 
96 bool StandardFileSystem::isPath(const string& pathName) {
97  try {
98  auto status = std::filesystem::status(std::filesystem::u8path(pathName));
99  return std::filesystem::is_directory(status);
100  } catch (Exception& exception) {
101  throw FileSystemException("Unable to check if path: " + pathName + ": " + string(exception.what()));
102  }
103  //
104  return false;
105 }
106 
107 bool StandardFileSystem::isDrive(const string& pathName) {
108  return StringTools::regexMatch(pathName, "^[a-zA-Z]{1}\\:.*$");
109 }
110 
111 bool StandardFileSystem::exists(const string& uri) {
112  try {
113  return std::filesystem::exists(std::filesystem::u8path(uri));
114  } catch (Exception& exception) {
115  throw FileSystemException("Unable to check if file exists: " + uri + ": " + string(exception.what()));
116  }
117  //
118  return false;
119 }
120 
121 bool StandardFileSystem::isExecutable(const string& pathName, const string& fileName) {
122  try {
123  auto permissions = std::filesystem::status(std::filesystem::u8path(pathName + "/" + fileName)).permissions();
124  return
125  (permissions & std::filesystem::perms::owner_exec) == std::filesystem::perms::owner_exec ||
126  (permissions & std::filesystem::perms::group_exec) == std::filesystem::perms::group_exec ||
127  (permissions & std::filesystem::perms::others_exec) == std::filesystem::perms::others_exec;
128  } catch (Exception& exception) {
129  throw FileSystemException("Unable to determine permissions: " + pathName + "/" + fileName + ": " + string(exception.what()));
130  }
131  //
132  return false;
133 }
134 
135 void StandardFileSystem::setExecutable(const string& pathName, const string& fileName) {
136  try {
137  std::filesystem::permissions(
138  std::filesystem::u8path(pathName + "/" + fileName),
139  std::filesystem::perms::owner_exec,
140  std::filesystem::perm_options::add
141  );
142  } catch (Exception& exception) {
143  throw FileSystemException("Unable to set executable permissions: " + pathName + "/" + fileName + ": " + string(exception.what()));
144  }
145 }
146 
147 uint64_t StandardFileSystem::getFileSize(const string& pathName, const string& fileName) {
148  try {
149  return std::filesystem::file_size(std::filesystem::u8path(pathName + "/" + fileName));
150  } catch (Exception& exception) {
151  throw FileSystemException("Unable to determine file size: " + fileName + ": " + string(exception.what()));
152  }
153  //
154  return false;
155 }
156 
157 const string StandardFileSystem::getContentAsString(const string& pathName, const string& fileName) {
158  ifstream ifs(std::filesystem::u8path(composeURI(pathName, fileName)));
159  if (ifs.is_open() == false) {
160  throw FileSystemException("Unable to open file for reading(" + to_string(errno) + "): " + pathName + "/" + fileName);
161  }
162  stringstream stringStream;
163  stringStream << ifs.rdbuf();
164  ifs.close();
165  return (stringStream.str());
166 }
167 
168 void StandardFileSystem::setContentFromString(const string& pathName, const string& fileName, const string& content) {
169  ofstream ofs(std::filesystem::u8path(composeURI(pathName, fileName)));
170  if (ofs.is_open() == false) {
171  throw FileSystemException("Unable to open file for writing(" + to_string(errno) + "): " + pathName + "/" + fileName);
172  }
173  ofs << (content);
174  ofs.close();
175  return;
176 }
177 
178 void StandardFileSystem::getContent(const string& pathName, const string& fileName, vector<uint8_t>& content)
179 {
180  ifstream ifs(std::filesystem::u8path(composeURI(pathName, fileName)), ifstream::binary);
181  if (ifs.is_open() == false) {
182  throw FileSystemException("Unable to open file for reading(" + to_string(errno) + "): " + pathName + "/" + fileName);
183  }
184  ifs.seekg( 0, ios::end );
185  size_t size = ifs.tellg();
186  content.resize(size);
187  ifs.seekg(0, ios::beg);
188  ifs.read((char*)content.data(), size);
189  ifs.close();
190 }
191 
192 void StandardFileSystem::setContent(const string& pathName, const string& fileName, const vector<uint8_t>& content) {
193  ofstream ofs(std::filesystem::u8path(composeURI(pathName, fileName)), ofstream::binary);
194  if (ofs.is_open() == false) {
195  throw FileSystemException("Unable to open file for writing(" + to_string(errno) + "): " + pathName + "/" + fileName);
196  }
197  ofs.write((char*)content.data(), content.size());
198  ofs.close();
199 }
200 
201 void StandardFileSystem::getContentAsStringArray(const string& pathName, const string& fileName, vector<string>& content)
202 {
203  ifstream ifs(std::filesystem::u8path(composeURI(pathName, fileName)));
204  if(ifs.is_open() == false) {
205  throw FileSystemException("Unable to open file for reading(" + to_string(errno) + "): " + pathName + "/" + fileName);
206  }
207 
208  string line;
209  while (getline(ifs, line)) {
210  content.push_back(StringTools::replace(line, "\r", ""));
211  }
212 
213  ifs.close();
214 }
215 
216 void StandardFileSystem::setContentFromStringArray(const string& pathName, const string& fileName, const vector<string>& content)
217 {
218  ofstream ofs(std::filesystem::u8path(composeURI(pathName, fileName)), ofstream::binary);
219  if(ofs.is_open() == false) {
220  throw FileSystemException("Unable to open file for writing(" + to_string(errno) + "): " + pathName + "/" + fileName);
221  }
222 
223  for (auto i = 0; i < content.size(); i++) {
224  ofs << (content.at(i)) << "\n";
225  }
226 
227  ofs.close();
228  return;
229 }
230 
231 const string StandardFileSystem::getCanonicalURI(const string& pathName, const string& fileName) {
232  string unixPathName = StringTools::replace(pathName, "\\", "/");
233  string unixFileName = StringTools::replace(fileName, "\\", "/");
234 
235  //
236  auto pathString = composeURI(unixPathName, unixFileName);
237 
238  // separate into path components
239  vector<string> pathComponents;
240  StringTokenizer t;
241  t.tokenize(pathString, "/");
242  while (t.hasMoreTokens()) {
243  pathComponents.push_back(t.nextToken());
244  }
245 
246  // process path components
247  for (auto i = 0; i < pathComponents.size(); i++) {
248  auto pathComponent = pathComponents[i];
249  if (pathComponent == ".") {
250  pathComponents[i].clear();
251  } else
252  if (pathComponent == "..") {
253  pathComponents[i].clear();
254  int j = i - 1;
255  for (int pathComponentReplaced = 0; pathComponentReplaced < 1 && j >= 0; ) {
256  if (pathComponents[j].empty() == false) {
257  pathComponents[j].clear();
258  pathComponentReplaced++;
259  }
260  j--;
261  }
262  }
263  }
264 
265  // process path components
266  string canonicalPath = "";
267  bool slash = StringTools::startsWith(pathString, "/");
268  for (auto i = 0; i < pathComponents.size(); i++) {
269  auto pathComponent = pathComponents[i];
270  if (pathComponent.empty() == true) {
271  // no op
272  } else {
273  canonicalPath = canonicalPath + (slash == true?"/":"") + pathComponent;
274  slash = true;
275  }
276  }
277 
278  // add cwd if required
279  auto canonicalPathString = canonicalPath.empty() == true?"/":canonicalPath;
280  /*
281  if (canonicalPathString.length() == 0 ||
282  (StringTools::startsWith(canonicalPathString, "/") == false &&
283  StringTools::regexMatch(canonicalPathString, "^[a-zA-Z]\\:.*$") == false)) {
284  canonicalPathString = getCurrentWorkingPathName() + "/" + canonicalPathString;
285  }
286  */
287 
288  //
289  return canonicalPathString;
290 }
291 
293  try {
294  auto u8Cwd = std::filesystem::current_path().u8string();
295  string cwd(u8Cwd.size(), 0);
296  for (auto i = 0; i < u8Cwd.size(); i++) cwd[i] = u8Cwd[i];
297  return cwd;
298  } catch (Exception& exception) {
299  throw FileSystemException("Unable to get current path: " + string(exception.what()));
300  }
301  //
302  return string(".");
303 }
304 
305 void StandardFileSystem::changePath(const string& pathName) {
306  try {
307  return std::filesystem::current_path(std::filesystem::u8path(pathName));
308  } catch (Exception& exception) {
309  throw FileSystemException("Unable to change path: " + pathName + ": " + string(exception.what()));
310  }
311 }
312 
313 const string StandardFileSystem::getPathName(const string& uri) {
314  auto unixFileName = StringTools::replace(uri, '\\', '/');
315  auto lastPathSeparator = StringTools::lastIndexOf(unixFileName, '/');
316  if (lastPathSeparator == -1) return ".";
317  return StringTools::substring(unixFileName, 0, lastPathSeparator);
318 }
319 
320 const string StandardFileSystem::getFileName(const string& uri) {
321  auto unixFileName = StringTools::replace(uri, '\\', '/');
322  auto lastPathSeparator = StringTools::lastIndexOf(unixFileName, '/');
323  if (lastPathSeparator == -1) return uri;
324  return StringTools::substring(unixFileName, lastPathSeparator + 1, unixFileName.length());
325 }
326 
327 const string StandardFileSystem::removeFileExtension(const string& fileName) {
328  auto idx = fileName.rfind('.');
329  if (idx == string::npos) {
330  return fileName;
331  } else {
332  return fileName.substr(0, idx);
333  }
334 }
335 
336 void StandardFileSystem::createPath(const string& pathName) {
337  try {
338  if (std::filesystem::create_directory(std::filesystem::u8path(pathName)) == false) {
339  throw FileSystemException("Unable to create path: " + pathName);
340  }
341  } catch (Exception& exception) {
342  throw FileSystemException("Unable to create path: " + pathName + ": " + string(exception.what()));
343  }
344 }
345 
346 void StandardFileSystem::removePath(const string& pathName, bool recursive) {
347  try {
348  if (recursive == false) {
349  if (std::filesystem::remove(std::filesystem::u8path(pathName)) == false) {
350  throw FileSystemException("Unable to remove path: " + pathName);
351  }
352  } else {
353  if (std::filesystem::remove_all(std::filesystem::u8path(pathName)) == false) {
354  throw FileSystemException("Unable to remove path recursively: " + pathName);
355  }
356  }
357  } catch (Exception& exception) {
358  throw FileSystemException("Unable to remove path: " + pathName + ": " + string(exception.what()));
359  }
360 }
361 
362 void StandardFileSystem::removeFile(const string& pathName, const string& fileName) {
363  try {
364  if (std::filesystem::remove(std::filesystem::u8path(composeURI(pathName, fileName))) == false) {
365  throw FileSystemException("Unable to remove file: " + pathName + "/" + fileName);
366  }
367  } catch (Exception& exception) {
368  throw FileSystemException("Unable to remove file: " + pathName + "/" + fileName + ": " + string(exception.what()));
369  }
370 }
371 
372 void StandardFileSystem::rename(const string& fileNameFrom, const string& fileNameTo) {
373  try {
374  std::filesystem::rename(std::filesystem::u8path(fileNameFrom), std::filesystem::u8path(fileNameTo));
375  } catch (Exception& exception) {
376  throw FileSystemException("Unable to rename file: " + fileNameFrom + " -> " + fileNameTo + ": " + string(exception.what()));
377  }
378 }
379 
380 
381 bool StandardFileSystem::getThumbnailAttachment(const string& pathName, const string& fileName, vector<uint8_t>& thumbnailAttachmentContent) {
382  ifstream ifs(std::filesystem::u8path(getCanonicalURI(pathName, fileName)), ifstream::binary);
383  if (ifs.is_open() == false) {
384  throw FileSystemException("Unable to open file for reading(" + to_string(errno) + "): " + pathName + "/" + fileName);
385  }
386 
387  // check size
388  ifs.seekg( 0, ios::end );
389  size_t size = ifs.tellg();
390  if (size < 12) return false;
391 
392  array<uint8_t, 12> id;
393 
394  ifs.seekg(size - 12, ios::beg);
395  ifs.read((char*)id.data(), 12);
396 
397  // attachment
398  if (id[8] != 'A' || id[9] != 'T' || id[10] != 'M' || id[11] != 'T') {
399  return false;
400  }
401  // thumbnail
402  if (id[4] != 'T' || id[5] != 'M' || id[6] != 'B' || id[7] != 'N') {
403  return false;
404  }
405  // attachment size
406  int32_t attachmentSize =
407  ((static_cast<int32_t>(id[0]) & 0xFF) << 24) +
408  ((static_cast<int32_t>(id[1]) & 0xFF) << 16) +
409  ((static_cast<int32_t>(id[2]) & 0xFF) << 8) +
410  ((static_cast<int32_t>(id[3]) & 0xFF) << 0);
411 
412  // do read the attachment
413  thumbnailAttachmentContent.resize(attachmentSize);
414  ifs.seekg(size - 12 - attachmentSize, ios::beg);
415  ifs.read((char*)thumbnailAttachmentContent.data(), attachmentSize);
416  ifs.close();
417 
418  //
419  return thumbnailAttachmentContent.empty() == false;
420 }
421 
422 bool StandardFileSystem::getThumbnailAttachment(const vector<uint8_t>& content, vector<uint8_t>& thumbnailAttachmentContent) {
423  if (content.size() < 12) return false;
424 
425  array<uint8_t, 12> id;
426 
427  //
428  for (auto i = 0; i < 12; i++) id[i] = content[content.size() - 12 + i];
429 
430  // attachment
431  if (id[8] != 'A' || id[9] != 'T' || id[10] != 'M' || id[11] != 'T') {
432  return false;
433  }
434  // thumbnail
435  if (id[4] != 'T' || id[5] != 'M' || id[6] != 'B' || id[7] != 'N') {
436  return false;
437  }
438  // attachment size
439  int32_t attachmentSize =
440  ((static_cast<int32_t>(id[0]) & 0xFF) << 24) +
441  ((static_cast<int32_t>(id[1]) & 0xFF) << 16) +
442  ((static_cast<int32_t>(id[2]) & 0xFF) << 8) +
443  ((static_cast<int32_t>(id[3]) & 0xFF) << 0);
444 
445  // do read the attachment
446  thumbnailAttachmentContent.resize(attachmentSize);
447  for (auto i = 0; i < attachmentSize; i++) thumbnailAttachmentContent[i] = content[content.size() - 12 - attachmentSize + i];
448 
449  //
450  return thumbnailAttachmentContent.empty() == false;
451 }
Standard file system implementation.
const string removeFileExtension(const string &fileName)
Remove file extension, e.g.
bool isDrive(const string &uri)
Check if file is a drive (applies to Microsoft Windows only)
void setContentFromString(const string &pathName, const string &fileName, const string &content)
Set content from string.
const string composeURI(const string &pathName, const string &fileName)
Compose URI from path name and file name.
const string getPathName(const string &uri)
Get path name.
const string getCanonicalURI(const string &pathName, const string &fileName)
Get canonical URI from given path name and file name.
void createPath(const string &pathName)
Create path.
bool getThumbnailAttachment(const string &pathName, const string &fileName, vector< uint8_t > &thumbnailAttachmentContent)
Reads a thumbnail attachment from binary file.
void list(const string &pathName, vector< string > &files, FileNameFilter *filter=nullptr, bool addDrives=false)
List files for given path and filter by a file name filter if not null.
void changePath(const string &pathName)
Change path.
void removePath(const string &pathName, bool recursive)
Remove path.
void setExecutable(const string &pathName, const string &fileName) override
Set up file to be an executable file.
void setContent(const string &pathName, const string &fileName, const vector< uint8_t > &content)
Set file content.
void getContent(const string &pathName, const string &fileName, vector< uint8_t > &content)
Get file content.
void setContentFromStringArray(const string &pathName, const string &fileName, const vector< string > &content)
Set file content as string array.
void removeFile(const string &pathName, const string &fileName)
Remove file.
uint64_t getFileSize(const string &pathName, const string &fileName)
Return file size of given file.
void getContentAsStringArray(const string &pathName, const string &fileName, vector< string > &content)
Get file content as string array.
const string getFileName(const string &uri)
Get file name.
bool isPath(const string &uri)
Check if file is a path.
bool exists(const string &uri)
Check if file exists.
const string getCurrentWorkingPathName()
Get current working path name.
bool isExecutable(const string &pathName, const string &fileName) override
Returns if file is a executable file.
const string getContentAsString(const string &pathName, const string &fileName)
Get content as string.
void rename(const string &fileNameFrom, const string &fileNameTo)
Rename file.
Console class.
Definition: Console.h:29
String tokenizer class.
void tokenize(const string &str, const string &delimiters, bool emptyTokens=false)
Tokenize.
String tools class.
Definition: StringTools.h:22
std::exception Exception
Exception base class.
Definition: Exception.h:18
File system file name filter interface.
virtual bool accept(const string &path, const string &file)=0
Accept a file.