File.cpp 15 KB

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