fileAPI.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2020 to 2022 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #include <fstream>
  24. #include <cstdlib>
  25. #include "fileAPI.h"
  26. #include "bufferAPI.h"
  27. #ifdef __cpp_lib_filesystem
  28. #include <filesystem>
  29. #endif
  30. namespace dsr {
  31. // If porting to a new operating system that is not following Posix standard, list how the file system works here.
  32. // * pathSeparator is the token used to separate folders in the system, expressed as a UTF-32 string literal.
  33. // * accessFile is the function for opening a file using the UTF-32 filename, for reading or writing.
  34. // The C API is used for access, because some C++ standard library implementations don't support wide strings for MS-Windows.
  35. #if defined(WIN32) || defined(_WIN32)
  36. #include <windows.h>
  37. static const char32_t* pathSeparator = U"\\";
  38. static const CharacterEncoding nativeEncoding = CharacterEncoding::BOM_UTF16LE;
  39. #define FILE_ACCESS_FUNCTION _wfopen
  40. #define FILE_ACCESS_SELECTION (write ? L"wb" : L"rb")
  41. #else
  42. static const char32_t* pathSeparator = U"/";
  43. static const CharacterEncoding nativeEncoding = CharacterEncoding::BOM_UTF8;
  44. #define FILE_ACCESS_FUNCTION fopen
  45. #define FILE_ACCESS_SELECTION (write ? "wb" : "rb")
  46. #endif
  47. static const NativeChar* toNativeString(const ReadableString &filename, Buffer &buffer) {
  48. buffer = string_saveToMemory(filename, nativeEncoding, LineEncoding::CrLf, false, true);
  49. return (const NativeChar*)buffer_dangerous_getUnsafeData(buffer);
  50. }
  51. static String fromNativeString(NativeChar *text) {
  52. return string_dangerous_decodeFromData(text, nativeEncoding);
  53. }
  54. static FILE* accessFile(const ReadableString &filename, bool write) {
  55. Buffer buffer;
  56. return FILE_ACCESS_FUNCTION(toNativeString(filename, buffer), FILE_ACCESS_SELECTION);
  57. }
  58. Buffer file_loadBuffer(const ReadableString& filename, bool mustExist) {
  59. FILE *file = accessFile(filename, false);
  60. if (file != nullptr) {
  61. // Get the file's size by going to the end, measuring, and going back
  62. fseek(file, 0L, SEEK_END);
  63. int64_t fileSize = ftell(file);
  64. rewind(file);
  65. // Allocate a buffer of the file's size
  66. Buffer buffer = buffer_create(fileSize);
  67. fread((void*)buffer_dangerous_getUnsafeData(buffer), fileSize, 1, file);
  68. fclose(file);
  69. return buffer;
  70. } else {
  71. if (mustExist) {
  72. throwError(U"The file ", filename, U" could not be opened for reading.\n");
  73. }
  74. // If the file cound not be found and opened, an empty buffer is returned
  75. return Buffer();
  76. }
  77. }
  78. void file_saveBuffer(const ReadableString& filename, Buffer buffer) {
  79. if (!buffer_exists(buffer)) {
  80. throwError(U"buffer_save: Cannot save a buffer that don't exist to a file.\n");
  81. } else {
  82. FILE *file = accessFile(filename, true);
  83. if (file != nullptr) {
  84. fwrite((void*)buffer_dangerous_getUnsafeData(buffer), buffer_getSize(buffer), 1, file);
  85. fclose(file);
  86. } else {
  87. throwError("Failed to save ", filename, ".\n");
  88. }
  89. }
  90. }
  91. const char32_t* file_separator() {
  92. return pathSeparator;
  93. }
  94. ReadableString file_getPathlessName(const ReadableString &path) {
  95. int lastSeparator = -1;
  96. for (int64_t i = string_length(path) - 1; i >= 0; i--) {
  97. DsrChar c = path[i];
  98. if (c == U'\\' || c == U'/') {
  99. lastSeparator = i;
  100. break;
  101. }
  102. }
  103. return string_after(path, lastSeparator);
  104. }
  105. List<String> file_convertInputArguments(int argn, NativeChar **argv) {
  106. List<String> result;
  107. result.reserve(argn);
  108. for (int a = 0; a < argn; a++) {
  109. result.push(fromNativeString(argv[a]));
  110. }
  111. return result;
  112. }
  113. #ifdef __cpp_lib_filesystem
  114. static String fromU32String(std::u32string text) {
  115. dsr::String result;
  116. string_reserve(result, text.length());
  117. int i = 0;
  118. while (true) {
  119. DsrChar c = text[i];
  120. if (c == '\0') {
  121. break;
  122. } else {
  123. string_appendChar(result, c);
  124. }
  125. i++;
  126. }
  127. return result;
  128. }
  129. #define FROM_PATH(CONTENT) fromU32String((CONTENT).generic_u32string())
  130. // Macros for creating wrapper functions
  131. #define DEF_FUNC_VOID_TO_STRING(FUNC_NAME, CALL_NAME) String FUNC_NAME() { return FROM_PATH(CALL_NAME()); }
  132. #define DEF_FUNC_STRING_TO_STRING(FUNC_NAME, ARG_NAME, CALL_NAME) String FUNC_NAME(const ReadableString& ARG_NAME) { Buffer buffer; return FROM_PATH(CALL_NAME(toNativeString(ARG_NAME, buffer))); }
  133. #define DEF_FUNC_STRING_TO_OTHER(FUNC_NAME, RETURN_TYPE, ARG_NAME, CALL_NAME) RETURN_TYPE FUNC_NAME(const ReadableString& ARG_NAME) { Buffer buffer; return CALL_NAME(toNativeString(ARG_NAME, buffer)); }
  134. // Wrapper function implementations
  135. DEF_FUNC_VOID_TO_STRING(file_getCurrentPath, std::filesystem::current_path)
  136. DEF_FUNC_STRING_TO_STRING(file_getAbsolutePath, path, std::filesystem::absolute)
  137. DEF_FUNC_STRING_TO_STRING(file_getCanonicalPath, path, std::filesystem::canonical)
  138. DEF_FUNC_STRING_TO_OTHER(file_exists, bool, path, std::filesystem::exists)
  139. DEF_FUNC_STRING_TO_OTHER(file_getSize, int64_t, path, std::filesystem::file_size)
  140. DEF_FUNC_STRING_TO_OTHER(file_remove, bool, path, std::filesystem::remove)
  141. DEF_FUNC_STRING_TO_OTHER(file_removeRecursively, bool, path, std::filesystem::remove_all)
  142. DEF_FUNC_STRING_TO_OTHER(file_createFolder, bool, path, std::filesystem::create_directory)
  143. void file_getFolderContent(const ReadableString& folderPath, std::function<void(ReadableString, EntryType)> action) {
  144. Buffer buffer;
  145. for (auto const& entry : std::filesystem::directory_iterator{toNativeString(folderPath, buffer)}) {
  146. EntryType entryType = EntryType::Unknown;
  147. if (entry.is_directory()) {
  148. entryType = EntryType::Folder;
  149. } else if (entry.is_regular_file()) {
  150. entryType = EntryType::File;
  151. } else if (entry.is_symlink()) {
  152. entryType = EntryType::SymbolicLink;
  153. }
  154. action(FROM_PATH(entry.path()), entryType);
  155. }
  156. }
  157. #endif
  158. }