File.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. /*
  2. * Author: Patrick-Christopher Mattulat
  3. * Company: Lynar Studios
  4. * E-Mail: webmaster@lynarstudios.com
  5. * Created: 2020-08-15
  6. * Changed: 2023-02-22
  7. *
  8. * */
  9. #include <algorithm>
  10. #include <cstdio>
  11. #ifdef _WIN32
  12. #include <direct.h>
  13. #endif
  14. #include <fstream>
  15. #include <ls-std/core/exception/FileOperationException.hpp>
  16. #include <ls-std/io/File.hpp>
  17. #include <ls-std/io/FilePathSeparator.hpp>
  18. #include <ls-std/io/FilePathSeparatorMatch.hpp>
  19. #include <sstream>
  20. #include <sys/stat.h>
  21. #ifdef _WIN32
  22. #include <tchar.h>
  23. #endif
  24. #if defined(unix) || defined(__APPLE__)
  25. #include <unistd.h>
  26. #endif
  27. #include <vector>
  28. ls::std::io::File::File(::std::string _absoluteFilePath) : ls::std::core::Class("File"), absoluteFilePath(ls::std::io::File::_normalizePath(::std::move(_absoluteFilePath)))
  29. {}
  30. ls::std::io::File::~File() noexcept = default;
  31. bool ls::std::io::File::operator==(ls::std::io::File &_file)
  32. {
  33. return ls::std::io::File::_equals(*this, _file);
  34. }
  35. bool ls::std::io::File::operator!=(ls::std::io::File &_file)
  36. {
  37. return !ls::std::io::File::_equals(*this, _file);
  38. }
  39. bool ls::std::io::File::canExecute()
  40. {
  41. return ls::std::io::File::_isExecutable(this->absoluteFilePath);
  42. }
  43. bool ls::std::io::File::canRead()
  44. {
  45. bool readable;
  46. #if defined(unix) || defined(__APPLE__)
  47. readable = ls::std::io::File::_isReadableUnix(this->absoluteFilePath);
  48. #endif
  49. #ifdef _WIN32
  50. readable = ls::std::io::File::_isReadableWindows(this->absoluteFilePath);
  51. #endif
  52. return readable;
  53. }
  54. bool ls::std::io::File::canWrite()
  55. {
  56. return ls::std::io::File::_isWritable(this->absoluteFilePath);
  57. }
  58. void ls::std::io::File::createNewFile()
  59. {
  60. if (!ls::std::io::File::_exists(this->absoluteFilePath))
  61. {
  62. ::std::ofstream file{this->absoluteFilePath};
  63. file.close();
  64. }
  65. else
  66. {
  67. throw ls::std::core::FileOperationException{"operation: create new file"};
  68. }
  69. }
  70. bool ls::std::io::File::exists()
  71. {
  72. return ls::std::io::File::_exists(this->absoluteFilePath);
  73. }
  74. ::std::string ls::std::io::File::getAbsoluteFilePath()
  75. {
  76. return this->absoluteFilePath;
  77. }
  78. ::std::string ls::std::io::File::getName()
  79. {
  80. ::std::string copy = this->absoluteFilePath;
  81. // if it's a directory, remove separator from end, if it does exist
  82. if (ls::std::io::File::_isDirectory(this->absoluteFilePath))
  83. {
  84. copy.erase(::std::remove_if(copy.end() - 1, copy.end(), ls::std::io::FilePathSeparatorMatch()), copy.end());
  85. }
  86. // now get the file / directory name
  87. auto base = ::std::find_if(copy.rbegin(), copy.rend(), ls::std::io::FilePathSeparatorMatch()).base();
  88. return ::std::string{base, copy.end()};
  89. }
  90. ::std::string ls::std::io::File::getParent()
  91. {
  92. return ls::std::io::File::_getParent(this->absoluteFilePath);
  93. }
  94. ::std::string ls::std::io::File::getWorkingDirectory()
  95. {
  96. ::std::string workingDirectory{};
  97. #if defined(unix) || defined(__APPLE__)
  98. workingDirectory = ls::std::io::File::_getWorkingDirectoryUnix();
  99. #endif
  100. #ifdef _WIN32
  101. workingDirectory = ls::std::io::File::_getWorkingDirectoryWindows();
  102. #endif
  103. return workingDirectory;
  104. }
  105. long ls::std::io::File::getSize()
  106. {
  107. ::std::streampos fileSize{};
  108. if (ls::std::io::File::_exists(this->absoluteFilePath))
  109. {
  110. ::std::ifstream fileHandler{this->absoluteFilePath, ::std::ios::in};
  111. fileSize = fileHandler.tellg();
  112. fileHandler.seekg(0, ::std::ios::end);
  113. fileSize = fileHandler.tellg() - fileSize;
  114. fileHandler.close();
  115. }
  116. return (long) fileSize;
  117. }
  118. bool ls::std::io::File::isDirectory()
  119. {
  120. return ls::std::io::File::_isDirectory(this->absoluteFilePath);
  121. }
  122. bool ls::std::io::File::isFile()
  123. {
  124. return ls::std::io::File::_isFile(this->absoluteFilePath);
  125. }
  126. time_t ls::std::io::File::lastModified()
  127. {
  128. return ls::std::io::File::_lastModified(this->absoluteFilePath);
  129. }
  130. ::std::list<::std::string> ls::std::io::File::list()
  131. {
  132. ::std::list<::std::string> fileList{};
  133. if (ls::std::io::File::_isDirectory(this->absoluteFilePath))
  134. {
  135. fileList = ls::std::io::File::_list(this->absoluteFilePath);
  136. }
  137. return fileList;
  138. }
  139. ::std::list<::std::string> ls::std::io::File::listFiles()
  140. {
  141. ::std::list<::std::string> fileList{};
  142. if (ls::std::io::File::_isDirectory(this->absoluteFilePath))
  143. {
  144. fileList = ls::std::io::File::_listFiles(this->absoluteFilePath);
  145. }
  146. return fileList;
  147. }
  148. void ls::std::io::File::makeDirectory()
  149. {
  150. if (!ls::std::io::File::_makeDirectory(this->absoluteFilePath))
  151. {
  152. throw ls::std::core::FileOperationException{"operation: create directory"};
  153. }
  154. }
  155. void ls::std::io::File::makeDirectories()
  156. {
  157. ::std::vector<::std::string> subDirectories = ls::std::io::File::_splitIntoSubDirectoryNames(this->absoluteFilePath);
  158. const char separator = ls::std::io::FilePathSeparator::get();
  159. ::std::string currentHierarchy{};
  160. for (const auto &subDirectory : subDirectories)
  161. {
  162. currentHierarchy += subDirectory;
  163. if (!ls::std::io::File::_exists(currentHierarchy + separator) && !currentHierarchy.empty())
  164. {
  165. if (!ls::std::io::File::_makeDirectory(currentHierarchy))
  166. {
  167. throw ls::std::core::FileOperationException{"operation: create directory"}; // TODO: add missing test
  168. }
  169. }
  170. currentHierarchy += separator;
  171. }
  172. }
  173. void ls::std::io::File::remove()
  174. {
  175. if (ls::std::io::File::_isFile(this->absoluteFilePath))
  176. {
  177. ::std::remove(this->absoluteFilePath.c_str());
  178. }
  179. if (ls::std::io::File::_isDirectory(this->absoluteFilePath))
  180. {
  181. ls::std::io::File::_remove(this->absoluteFilePath);
  182. }
  183. }
  184. bool ls::std::io::File::renameTo(const ::std::string &_newName)
  185. {
  186. bool renamed = ls::std::io::File::_renameTo(this->absoluteFilePath, _newName);
  187. if (renamed)
  188. {
  189. this->absoluteFilePath = _newName;
  190. }
  191. return renamed;
  192. }
  193. void ls::std::io::File::reset(const ::std::string &_newPath)
  194. {
  195. this->absoluteFilePath = ls::std::io::File::_normalizePath(_newPath);
  196. }
  197. #ifdef _WIN32
  198. void ls::std::io::File::_addToFileListWindows(const ::std::string &_path, bool _withDirectories, WIN32_FIND_DATA _data, ::std::list<::std::string> &_list)
  199. {
  200. const char separator = ls::std::io::FilePathSeparator::get();
  201. ::std::string absolutePath = _path + separator + _data.cFileName;
  202. if (_withDirectories)
  203. {
  204. _list.emplace_back(absolutePath);
  205. }
  206. else
  207. {
  208. if (ls::std::io::File::_isFile(absolutePath))
  209. {
  210. _list.emplace_back(absolutePath);
  211. }
  212. }
  213. }
  214. #endif
  215. #if defined(unix) || defined(__APPLE__)
  216. void ls::std::io::File::_addToFileListUnix(const ::std::string &_path, bool _withDirectories, dirent *directoryEntity, ::std::list<::std::string> &_list)
  217. {
  218. const char separator = ls::std::io::FilePathSeparator::get();
  219. ::std::string absolutePath = _path + separator + directoryEntity->d_name;
  220. if (_withDirectories)
  221. {
  222. _list.emplace_back(absolutePath);
  223. }
  224. else
  225. {
  226. if (ls::std::io::File::_isFile(absolutePath))
  227. {
  228. _list.emplace_back(absolutePath);
  229. }
  230. }
  231. }
  232. #endif
  233. bool ls::std::io::File::_equals(ls::std::io::File &_file, ls::std::io::File &_foreignFile)
  234. {
  235. bool isEqual = _file.getAbsoluteFilePath() == _foreignFile.getAbsoluteFilePath();
  236. if (_file.exists() && _foreignFile.exists())
  237. {
  238. isEqual = isEqual && _file.canRead() == _foreignFile.canRead();
  239. isEqual = isEqual && _file.canWrite() == _foreignFile.canWrite();
  240. isEqual = isEqual && _file.canExecute() == _foreignFile.canExecute();
  241. }
  242. return isEqual;
  243. }
  244. bool ls::std::io::File::_exists(const ::std::string &_path)
  245. {
  246. struct stat _stat
  247. {
  248. };
  249. return (stat(_path.c_str(), &_stat) == 0);
  250. }
  251. ::std::string ls::std::io::File::_getParent(const ::std::string &_path)
  252. {
  253. ::std::string parent{};
  254. ::std::vector<::std::string> subDirectoryNames = ls::std::io::File::_splitIntoSubDirectoryNames(_path);
  255. const char separator = ls::std::io::FilePathSeparator::get();
  256. subDirectoryNames.pop_back();
  257. for (auto const &subDirectoryName : subDirectoryNames)
  258. {
  259. parent += subDirectoryName + separator;
  260. }
  261. return parent;
  262. }
  263. #if defined(unix) || defined(__APPLE__)
  264. ::std::string ls::std::io::File::_getWorkingDirectoryUnix()
  265. {
  266. ::std::string workingDirectory{};
  267. char buffer[PATH_MAX];
  268. if (getcwd(buffer, sizeof(buffer)) == nullptr)
  269. {
  270. throw ls::std::core::FileOperationException{"operation: get working directory"};
  271. }
  272. else
  273. {
  274. workingDirectory = ::std::string(buffer);
  275. }
  276. return workingDirectory;
  277. }
  278. #endif
  279. #ifdef _WIN32
  280. ::std::string ls::std::io::File::_getWorkingDirectoryWindows()
  281. {
  282. ::std::string workingDirectory{};
  283. TCHAR buffer[MAX_PATH];
  284. if (!GetCurrentDirectory(MAX_PATH, buffer))
  285. {
  286. throw ls::std::core::FileOperationException{"operation: get working directory"};
  287. }
  288. else
  289. {
  290. workingDirectory = ::std::string(buffer);
  291. }
  292. return workingDirectory;
  293. }
  294. #endif
  295. bool ls::std::io::File::_isDirectory(const ::std::string &_path)
  296. {
  297. bool match{};
  298. struct stat _stat
  299. {
  300. };
  301. if (stat(_path.c_str(), &_stat) == 0)
  302. {
  303. match = _stat.st_mode & (unsigned short) S_IFDIR;
  304. }
  305. return match;
  306. }
  307. bool ls::std::io::File::_isExecutable(const ::std::string &_path)
  308. {
  309. bool executable{};
  310. if (ls::std::io::File::_exists(_path))
  311. {
  312. struct stat _stat
  313. {
  314. };
  315. if (stat(_path.c_str(), &_stat) == 0)
  316. {
  317. executable = (_stat.st_mode & (unsigned short) S_IEXEC) != 0;
  318. }
  319. }
  320. return executable;
  321. }
  322. bool ls::std::io::File::_isFile(const ::std::string &_path)
  323. {
  324. bool match{};
  325. struct stat _stat
  326. {
  327. };
  328. if (stat(_path.c_str(), &_stat) == 0)
  329. {
  330. match = _stat.st_mode & (unsigned) S_IFREG;
  331. }
  332. return match;
  333. }
  334. #if defined(unix) || defined(__APPLE__)
  335. bool ls::std::io::File::_isReadableUnix(const ::std::string &_path)
  336. {
  337. bool readable{};
  338. if (ls::std::io::File::_exists(_path))
  339. {
  340. struct stat _stat
  341. {
  342. };
  343. if (stat(_path.c_str(), &_stat) == 0)
  344. {
  345. readable = (_stat.st_mode & (unsigned) S_IREAD) != 0;
  346. }
  347. }
  348. else
  349. {
  350. throw ls::std::core::FileOperationException{"operation: fetch permissions"};
  351. }
  352. return readable;
  353. }
  354. #endif
  355. #ifdef _WIN32
  356. bool ls::std::io::File::_isReadableWindows(const ::std::string &_path)
  357. {
  358. bool readable;
  359. WIN32_FIND_DATA data{};
  360. HANDLE handleFind = FindFirstFile(_path.c_str(), &data);
  361. if (handleFind != INVALID_HANDLE_VALUE)
  362. {
  363. readable = GetFileAttributes(data.cFileName) & (unsigned) FILE_ATTRIBUTE_READONLY;
  364. }
  365. else
  366. {
  367. throw ls::std::core::FileOperationException{"operation: fetch permissions"};
  368. }
  369. return readable;
  370. }
  371. #endif
  372. bool ls::std::io::File::_isWritable(const ::std::string &_path)
  373. {
  374. bool writable{};
  375. if (ls::std::io::File::_exists(_path))
  376. {
  377. struct stat _stat
  378. {
  379. };
  380. if (stat(_path.c_str(), &_stat) == 0)
  381. {
  382. writable = (_stat.st_mode & (unsigned) S_IWRITE) != 0;
  383. }
  384. }
  385. return writable;
  386. }
  387. time_t ls::std::io::File::_lastModified(const ::std::string &_path)
  388. {
  389. time_t lastModifiedTimeStamp{};
  390. struct stat _stat
  391. {
  392. };
  393. if (stat(_path.c_str(), &_stat) == 0)
  394. {
  395. lastModifiedTimeStamp = _stat.st_mtime;
  396. }
  397. return lastModifiedTimeStamp;
  398. }
  399. ::std::list<::std::string> ls::std::io::File::_list(const ::std::string &_path)
  400. {
  401. ::std::list<::std::string> filesInDirectory{};
  402. #if defined(unix) || defined(__APPLE__)
  403. filesInDirectory = ls::std::io::File::_listUnix(_path, true);
  404. #endif
  405. #ifdef _WIN32
  406. filesInDirectory = ls::std::io::File::_listWindows(_path, true);
  407. #endif
  408. return filesInDirectory;
  409. }
  410. ::std::list<::std::string> ls::std::io::File::_listFiles(const ::std::string &_path)
  411. {
  412. ::std::list<::std::string> filesInDirectory{};
  413. #if defined(unix) || defined(__APPLE__)
  414. filesInDirectory = ls::std::io::File::_listUnix(_path, false);
  415. #endif
  416. #ifdef _WIN32
  417. filesInDirectory = ls::std::io::File::_listWindows(_path, false);
  418. #endif
  419. return filesInDirectory;
  420. }
  421. #if defined(unix) || defined(__APPLE__)
  422. ::std::list<::std::string> ls::std::io::File::_listUnix(const ::std::string &_path, bool withDirectories)
  423. {
  424. ::std::list<::std::string> filesInDirectory{};
  425. DIR *directory = opendir(_path.c_str());
  426. struct dirent *directoryEntity;
  427. ::std::string absolutePath{};
  428. while ((directoryEntity = readdir(directory)) != nullptr)
  429. {
  430. ls::std::io::File::_addToFileListUnix(_path, withDirectories, directoryEntity, filesInDirectory);
  431. }
  432. closedir(directory);
  433. return filesInDirectory;
  434. }
  435. #endif
  436. #ifdef _WIN32
  437. ::std::list<::std::string> ls::std::io::File::_listWindows(const ::std::string &_path, bool withDirectories)
  438. {
  439. ::std::list<::std::string> filesInDirectory{};
  440. WIN32_FIND_DATA data{};
  441. HANDLE hFind;
  442. ::std::string pattern{_path + ls::std::io::FilePathSeparator::get() + "*"};
  443. if ((hFind = FindFirstFile(pattern.c_str(), &data)) != INVALID_HANDLE_VALUE)
  444. {
  445. do
  446. {
  447. ls::std::io::File::_addToFileListWindows(_path, withDirectories, data, filesInDirectory);
  448. } while (FindNextFile(hFind, &data) != 0);
  449. FindClose(hFind);
  450. }
  451. return filesInDirectory;
  452. }
  453. #endif
  454. bool ls::std::io::File::_makeDirectory(const ::std::string &_path)
  455. {
  456. int result;
  457. #ifdef _WIN32
  458. result = _mkdir(_path.c_str());
  459. #endif
  460. #if defined(unix) || defined(__APPLE__)
  461. result = mkdir(_path.c_str(), 0777);
  462. #endif
  463. return result == 0;
  464. }
  465. ::std::string ls::std::io::File::_normalizePath(::std::string _path)
  466. {
  467. _path = ls::std::io::File::_replaceWrongSeparator(_path);
  468. _path = ls::std::io::File::_reduceSeparators(_path);
  469. return _path;
  470. }
  471. ::std::string ls::std::io::File::_reduceSeparators(const ::std::string &_path)
  472. {
  473. static const char separator = {ls::std::io::FilePathSeparator::get()};
  474. ::std::string normalizedPath{};
  475. int index{};
  476. while (index < _path.size())
  477. {
  478. if (_path[index] == separator)
  479. {
  480. normalizedPath += _path[index];
  481. do
  482. {
  483. index++;
  484. } while (_path[index] == separator);
  485. }
  486. else
  487. {
  488. normalizedPath += _path[index];
  489. index++;
  490. }
  491. }
  492. return normalizedPath;
  493. }
  494. void ls::std::io::File::_remove(const ::std::string &_path)
  495. {
  496. #if defined(unix) || defined(__APPLE__)
  497. ls::std::io::File::_removeUnix(_path);
  498. #endif
  499. #ifdef _WIN32
  500. ls::std::io::File::_removeWindows(_path);
  501. #endif
  502. }
  503. #if defined(unix) || defined(__APPLE__)
  504. void ls::std::io::File::_removeUnix(const ::std::string &_path)
  505. {
  506. rmdir(_path.c_str());
  507. }
  508. #endif
  509. #ifdef _WIN32
  510. void ls::std::io::File::_removeWindows(const ::std::string &_path)
  511. {
  512. _rmdir(_path.c_str());
  513. }
  514. #endif
  515. bool ls::std::io::File::_renameTo(const ::std::string &_oldName, const ::std::string &_newName)
  516. {
  517. return ::std::rename(_oldName.c_str(), _newName.c_str()) == 0;
  518. }
  519. ::std::string ls::std::io::File::_replaceWrongSeparator(::std::string _path)
  520. {
  521. static const char unixSeparator = ls::std::io::FilePathSeparator::getUnixFilePathSeparator();
  522. static const char windowsSeparator = ls::std::io::FilePathSeparator::getWindowsFilePathSeparator();
  523. #if defined(unix) || defined(__APPLE__)
  524. ::std::replace(_path.begin(), _path.end(), windowsSeparator, unixSeparator);
  525. #endif
  526. #ifdef _WIN32
  527. ::std::replace(_path.begin(), _path.end(), unixSeparator, windowsSeparator);
  528. #endif
  529. return _path;
  530. }
  531. ::std::vector<::std::string> ls::std::io::File::_splitIntoSubDirectoryNames(const ::std::string &_path)
  532. {
  533. ::std::vector<::std::string> subDirectoryNames{};
  534. ::std::stringstream _stream{_path};
  535. ::std::string subDirectoryName{};
  536. const char separator = ls::std::io::FilePathSeparator::get();
  537. while (::std::getline(_stream, subDirectoryName, separator))
  538. {
  539. subDirectoryNames.push_back(subDirectoryName);
  540. }
  541. return subDirectoryNames;
  542. }