File.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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-03
  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/FilePathSeparatorMatch.hpp>
  18. #include <sys/stat.h>
  19. #include <sstream>
  20. #ifdef _WIN32
  21. #include <tchar.h>
  22. #endif
  23. #if defined(unix) || defined(__APPLE__)
  24. #include <unistd.h>
  25. #endif
  26. #include <vector>
  27. ls::std::io::File::File(::std::string _absoluteFilePath)
  28. : ls::std::core::Class("File"),
  29. absoluteFilePath(ls::std::io::File::_normalizePath(::std::move(_absoluteFilePath)))
  30. {}
  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{};
  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::_mkdir(this->absoluteFilePath))
  151. {
  152. throw ls::std::core::FileOperationException{};
  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))
  164. {
  165. ls::std::io::File::_mkdir(currentHierarchy);
  166. }
  167. currentHierarchy += separator;
  168. }
  169. }
  170. void ls::std::io::File::remove()
  171. {
  172. if (ls::std::io::File::_isFile(this->absoluteFilePath))
  173. {
  174. ::std::remove(this->absoluteFilePath.c_str());
  175. }
  176. if (ls::std::io::File::_isDirectory(this->absoluteFilePath))
  177. {
  178. ls::std::io::File::_remove(this->absoluteFilePath);
  179. }
  180. }
  181. bool ls::std::io::File::renameTo(const ::std::string &_newName)
  182. {
  183. bool renamed = ls::std::io::File::_renameTo(this->absoluteFilePath, _newName);
  184. if (renamed)
  185. {
  186. this->absoluteFilePath = _newName;
  187. }
  188. return renamed;
  189. }
  190. void ls::std::io::File::reset(const ::std::string &_newPath)
  191. {
  192. this->absoluteFilePath = ls::std::io::File::_normalizePath(_newPath);
  193. }
  194. #ifdef _WIN32
  195. void ls::std::io::File::_addToFileListWindows(const ::std::string &_path, bool _withDirectories, WIN32_FIND_DATA _data, ::std::list<::std::string> &_list)
  196. {
  197. const char separator = ls::std::io::FilePathSeparator::get();
  198. ::std::string absolutePath = _path + separator + _data.cFileName;
  199. if (_withDirectories)
  200. {
  201. _list.emplace_back(absolutePath);
  202. }
  203. else
  204. {
  205. if (ls::std::io::File::_isFile(absolutePath))
  206. {
  207. _list.emplace_back(absolutePath);
  208. }
  209. }
  210. }
  211. #endif
  212. #if defined(unix) || defined(__APPLE__)
  213. void ls::std::io::File::_addToFileListUnix(const ::std::string &_path, bool _withDirectories, dirent *directoryEntity, ::std::list<::std::string> &_list)
  214. {
  215. const char separator = ls::std::io::FilePathSeparator::get();
  216. ::std::string absolutePath = _path + separator + directoryEntity->d_name;
  217. if (_withDirectories)
  218. {
  219. _list.emplace_back(absolutePath);
  220. }
  221. else
  222. {
  223. if (ls::std::io::File::_isFile(absolutePath))
  224. {
  225. _list.emplace_back(absolutePath);
  226. }
  227. }
  228. }
  229. #endif
  230. bool ls::std::io::File::_equals(ls::std::io::File &_file, ls::std::io::File &_foreignFile)
  231. {
  232. bool isEqual = _file.getAbsoluteFilePath() == _foreignFile.getAbsoluteFilePath();
  233. if (_file.exists() && _foreignFile.exists())
  234. {
  235. isEqual = isEqual && _file.canRead() == _foreignFile.canRead();
  236. isEqual = isEqual && _file.canWrite() == _foreignFile.canWrite();
  237. isEqual = isEqual && _file.canExecute() == _foreignFile.canExecute();
  238. }
  239. return isEqual;
  240. }
  241. bool ls::std::io::File::_exists(const ::std::string &_path)
  242. {
  243. struct stat _stat{};
  244. return (stat(_path.c_str(), &_stat) == 0);
  245. }
  246. ::std::string ls::std::io::File::_getParent(const ::std::string &_path)
  247. {
  248. ::std::string parent{};
  249. ::std::vector<::std::string> subDirectoryNames = ls::std::io::File::_splitIntoSubDirectoryNames(_path);
  250. const char separator = ls::std::io::FilePathSeparator::get();
  251. subDirectoryNames.pop_back();
  252. for (auto const &subDirectoryName: subDirectoryNames)
  253. {
  254. parent += subDirectoryName + separator;
  255. }
  256. return parent;
  257. }
  258. #if defined(unix) || defined(__APPLE__)
  259. ::std::string ls::std::io::File::_getWorkingDirectoryUnix()
  260. {
  261. ::std::string workingDirectory{};
  262. char buffer[PATH_MAX];
  263. if (getcwd(buffer, sizeof(buffer)) == nullptr)
  264. {
  265. throw ls::std::core::FileOperationException{};
  266. }
  267. else
  268. {
  269. workingDirectory = ::std::string(buffer);
  270. }
  271. return workingDirectory;
  272. }
  273. #endif
  274. #ifdef _WIN32
  275. ::std::string ls::std::io::File::_getWorkingDirectoryWindows()
  276. {
  277. ::std::string workingDirectory{};
  278. TCHAR buffer[MAX_PATH];
  279. if (!GetCurrentDirectory(MAX_PATH, buffer))
  280. {
  281. throw ls::std::core::FileOperationException{};
  282. }
  283. else
  284. {
  285. workingDirectory = ::std::string(buffer);
  286. }
  287. return workingDirectory;
  288. }
  289. #endif
  290. bool ls::std::io::File::_isDirectory(const ::std::string &_path)
  291. {
  292. bool match{};
  293. struct stat _stat{};
  294. if (stat(_path.c_str(), &_stat) == 0)
  295. {
  296. match = _stat.st_mode & (unsigned short) S_IFDIR;
  297. }
  298. return match;
  299. }
  300. bool ls::std::io::File::_isExecutable(const ::std::string &_path)
  301. {
  302. bool executable{};
  303. if (ls::std::io::File::_exists(_path))
  304. {
  305. struct stat _stat{};
  306. if (stat(_path.c_str(), &_stat) == 0)
  307. {
  308. executable = (_stat.st_mode & (unsigned short) S_IEXEC) != 0;
  309. }
  310. }
  311. return executable;
  312. }
  313. bool ls::std::io::File::_isFile(const ::std::string &_path)
  314. {
  315. bool match{};
  316. struct stat _stat{};
  317. if (stat(_path.c_str(), &_stat) == 0)
  318. {
  319. match = _stat.st_mode & (unsigned) S_IFREG;
  320. }
  321. return match;
  322. }
  323. #if defined(unix) || defined(__APPLE__)
  324. bool ls::std::io::File::_isReadableUnix(const ::std::string &_path)
  325. {
  326. bool readable{};
  327. if (ls::std::io::File::_exists(_path))
  328. {
  329. struct stat _stat{};
  330. if (stat(_path.c_str(), &_stat) == 0)
  331. {
  332. readable = (_stat.st_mode & (unsigned) S_IREAD) != 0;
  333. }
  334. }
  335. else
  336. {
  337. throw ls::std::core::FileOperationException{};
  338. }
  339. return readable;
  340. }
  341. #endif
  342. #ifdef _WIN32
  343. bool ls::std::io::File::_isReadableWindows(const ::std::string &_path)
  344. {
  345. bool readable;
  346. WIN32_FIND_DATA data{};
  347. HANDLE handleFind = FindFirstFile(_path.c_str(), &data);
  348. if (handleFind != INVALID_HANDLE_VALUE)
  349. {
  350. readable = GetFileAttributes(data.cFileName) & (unsigned) FILE_ATTRIBUTE_READONLY;
  351. }
  352. else
  353. {
  354. throw ls::std::core::FileOperationException{};
  355. }
  356. return readable;
  357. }
  358. #endif
  359. bool ls::std::io::File::_isWritable(const ::std::string &_path)
  360. {
  361. bool writable{};
  362. if (ls::std::io::File::_exists(_path))
  363. {
  364. struct stat _stat{};
  365. if (stat(_path.c_str(), &_stat) == 0)
  366. {
  367. writable = (_stat.st_mode & (unsigned) S_IWRITE) != 0;
  368. }
  369. }
  370. return writable;
  371. }
  372. time_t ls::std::io::File::_lastModified(const ::std::string &_path)
  373. {
  374. time_t lastModifiedTimeStamp{};
  375. struct stat _stat{};
  376. if (stat(_path.c_str(), &_stat) == 0)
  377. {
  378. lastModifiedTimeStamp = _stat.st_mtime;
  379. }
  380. return lastModifiedTimeStamp;
  381. }
  382. ::std::list<::std::string> ls::std::io::File::_list(const ::std::string &_path)
  383. {
  384. ::std::list<::std::string> filesInDirectory{};
  385. #if defined(unix) || defined(__APPLE__)
  386. filesInDirectory = ls::std::io::File::_listUnix(_path, true);
  387. #endif
  388. #ifdef _WIN32
  389. filesInDirectory = ls::std::io::File::_listWindows(_path, true);
  390. #endif
  391. return filesInDirectory;
  392. }
  393. ::std::list<::std::string> ls::std::io::File::_listFiles(const ::std::string &_path)
  394. {
  395. ::std::list<::std::string> filesInDirectory{};
  396. #if defined(unix) || defined(__APPLE__)
  397. filesInDirectory = ls::std::io::File::_listUnix(_path, false);
  398. #endif
  399. #ifdef _WIN32
  400. filesInDirectory = ls::std::io::File::_listWindows(_path, false);
  401. #endif
  402. return filesInDirectory;
  403. }
  404. #if defined(unix) || defined(__APPLE__)
  405. ::std::list<::std::string> ls::std::io::File::_listUnix(const ::std::string &_path, bool withDirectories)
  406. {
  407. ::std::list<::std::string> filesInDirectory{};
  408. DIR *directory = opendir(_path.c_str());
  409. struct dirent *directoryEntity;
  410. ::std::string absolutePath{};
  411. while ((directoryEntity = readdir(directory)) != nullptr)
  412. {
  413. ls::std::io::File::_addToFileListUnix(_path, withDirectories, directoryEntity, filesInDirectory);
  414. }
  415. closedir(directory);
  416. return filesInDirectory;
  417. }
  418. #endif
  419. #ifdef _WIN32
  420. ::std::list<::std::string> ls::std::io::File::_listWindows(const ::std::string &_path, bool withDirectories)
  421. {
  422. ::std::list<::std::string> filesInDirectory{};
  423. WIN32_FIND_DATA data{};
  424. HANDLE hFind;
  425. ::std::string pattern{_path + ls::std::io::FilePathSeparator::get() + "*"};
  426. if ((hFind = FindFirstFile(pattern.c_str(), &data)) != INVALID_HANDLE_VALUE)
  427. {
  428. do
  429. {
  430. ls::std::io::File::_addToFileListWindows(_path, withDirectories, data, filesInDirectory);
  431. } while (FindNextFile(hFind, &data) != 0);
  432. FindClose(hFind);
  433. }
  434. return filesInDirectory;
  435. }
  436. #endif
  437. int ls::std::io::File::_mkdir(const ::std::string &_path)
  438. {
  439. int result;
  440. #ifdef _WIN32
  441. result = mkdir(_path.c_str());
  442. #endif
  443. #if defined(unix) || defined(__APPLE__)
  444. result = mkdir(_path.c_str(), 0777);
  445. #endif
  446. return result;
  447. }
  448. ::std::string ls::std::io::File::_normalizePath(::std::string _path)
  449. {
  450. _path = ls::std::io::File::_replaceWrongSeparator(_path);
  451. _path = ls::std::io::File::_reduceSeparators(_path);
  452. return _path;
  453. }
  454. ::std::string ls::std::io::File::_reduceSeparators(const ::std::string &_path)
  455. {
  456. static const char separator = {ls::std::io::FilePathSeparator::get()};
  457. ::std::string normalizedPath{};
  458. int index{};
  459. while (index < _path.size())
  460. {
  461. if (_path[index] == separator)
  462. {
  463. normalizedPath += _path[index];
  464. do
  465. {
  466. index++;
  467. } while (_path[index] == separator);
  468. }
  469. else
  470. {
  471. normalizedPath += _path[index];
  472. index++;
  473. }
  474. }
  475. return normalizedPath;
  476. }
  477. void ls::std::io::File::_remove(const ::std::string &_path)
  478. {
  479. #if defined(unix) || defined(__APPLE__)
  480. ls::std::io::File::_removeUnix(_path);
  481. #endif
  482. #ifdef _WIN32
  483. ls::std::io::File::_removeWindows(_path);
  484. #endif
  485. }
  486. #if defined(unix) || defined(__APPLE__)
  487. void ls::std::io::File::_removeUnix(const ::std::string &_path)
  488. {
  489. rmdir(_path.c_str());
  490. }
  491. #endif
  492. #ifdef _WIN32
  493. void ls::std::io::File::_removeWindows(const ::std::string &_path)
  494. {
  495. _rmdir(_path.c_str());
  496. }
  497. #endif
  498. bool ls::std::io::File::_renameTo(const ::std::string &_oldName, const ::std::string &_newName)
  499. {
  500. return ::std::rename(_oldName.c_str(), _newName.c_str()) == 0;
  501. }
  502. ::std::string ls::std::io::File::_replaceWrongSeparator(::std::string _path)
  503. {
  504. static const char unixSeparator = ls::std::io::FilePathSeparator::getUnixFilePathSeparator();
  505. static const char windowsSeparator = ls::std::io::FilePathSeparator::getWindowsFilePathSeparator();
  506. #if defined(unix) || defined(__APPLE__)
  507. ::std::replace(_path.begin(), _path.end(), windowsSeparator, unixSeparator);
  508. #endif
  509. #ifdef _WIN32
  510. ::std::replace(_path.begin(), _path.end(), unixSeparator, windowsSeparator);
  511. #endif
  512. return _path;
  513. }
  514. ::std::vector<::std::string> ls::std::io::File::_splitIntoSubDirectoryNames(const ::std::string &_path)
  515. {
  516. ::std::vector<::std::string> subDirectoryNames{};
  517. ::std::stringstream _stream{_path};
  518. ::std::string subDirectoryName{};
  519. const char separator = ls::std::io::FilePathSeparator::get();
  520. while (::std::getline(_stream, subDirectoryName, separator))
  521. {
  522. subDirectoryNames.push_back(subDirectoryName);
  523. }
  524. return subDirectoryNames;
  525. }