fileAPI.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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 "fileAPI.h"
  24. #ifdef USE_MICROSOFT_WINDOWS
  25. #include <windows.h>
  26. #else
  27. #include <unistd.h>
  28. #endif
  29. #include <fstream>
  30. #include <cstdlib>
  31. #include "bufferAPI.h"
  32. namespace dsr {
  33. // If porting to a new operating system that is not following Posix standard, list how the file system works here.
  34. // * pathSeparator is the token used to separate folders in the system, expressed as a UTF-32 string literal.
  35. // * accessFile is the function for opening a file using the UTF-32 filename, for reading or writing.
  36. // The C API is used for access, because some C++ standard library implementations don't support wide strings for MS-Windows.
  37. #ifdef USE_MICROSOFT_WINDOWS
  38. using NativeChar = wchar_t; // UTF-16
  39. static const char32_t* pathSeparator = U"\\";
  40. static const CharacterEncoding nativeEncoding = CharacterEncoding::BOM_UTF16LE;
  41. #define FILE_ACCESS_FUNCTION _wfopen
  42. #define FILE_ACCESS_SELECTION (write ? L"wb" : L"rb")
  43. List<String> file_impl_getInputArguments() {
  44. // Get a pointer to static memory with the command
  45. LPWSTR cmd = GetCommandLineW();
  46. // Split the arguments into an array of arguments
  47. int argc = 0;
  48. LPWSTR *argv = CommandLineToArgvW(cmd, &argc);
  49. // Convert the arguments into dsr::List<dsr::String>
  50. List<String> args = file_impl_convertInputArguments(argc, (void**)argv);
  51. // Free the native list of arguments
  52. LocalFree(argv);
  53. return args;
  54. }
  55. #else
  56. using NativeChar = char; // UTF-8
  57. static const char32_t* pathSeparator = U"/";
  58. static const CharacterEncoding nativeEncoding = CharacterEncoding::BOM_UTF8;
  59. #define FILE_ACCESS_FUNCTION fopen
  60. #define FILE_ACCESS_SELECTION (write ? "wb" : "rb")
  61. List<String> file_impl_getInputArguments() { return List<String>(); }
  62. #endif
  63. // Length of fixed size buffers.
  64. const int maxLength = 512;
  65. static const NativeChar* toNativeString(const ReadableString &filename, Buffer &buffer) {
  66. buffer = string_saveToMemory(filename, nativeEncoding, LineEncoding::CrLf, false, true);
  67. return (const NativeChar*)buffer_dangerous_getUnsafeData(buffer);
  68. }
  69. static String fromNativeString(const NativeChar *text) {
  70. return string_dangerous_decodeFromData(text, nativeEncoding);
  71. }
  72. List<String> file_impl_convertInputArguments(int argn, void **argv) {
  73. List<String> result;
  74. result.reserve(argn);
  75. for (int a = 0; a < argn; a++) {
  76. result.push(fromNativeString((NativeChar*)(argv[a])));
  77. }
  78. return result;
  79. }
  80. static FILE* accessFile(const ReadableString &filename, bool write) {
  81. Buffer buffer;
  82. return FILE_ACCESS_FUNCTION(toNativeString(filename, buffer), FILE_ACCESS_SELECTION);
  83. }
  84. Buffer file_loadBuffer(const ReadableString& filename, bool mustExist) {
  85. String modifiedFilename = file_optimizePath(filename);
  86. FILE *file = accessFile(modifiedFilename, false);
  87. if (file != nullptr) {
  88. // Get the file's size by going to the end, measuring, and going back
  89. fseek(file, 0L, SEEK_END);
  90. int64_t fileSize = ftell(file);
  91. rewind(file);
  92. // Allocate a buffer of the file's size
  93. Buffer buffer = buffer_create(fileSize);
  94. fread((void*)buffer_dangerous_getUnsafeData(buffer), fileSize, 1, file);
  95. fclose(file);
  96. return buffer;
  97. } else {
  98. if (mustExist) {
  99. throwError(U"Failed to load ", filename, " which was optimized into ", modifiedFilename, ".\n");
  100. }
  101. // If the file cound not be found and opened, an empty buffer is returned
  102. return Buffer();
  103. }
  104. }
  105. void file_saveBuffer(const ReadableString& filename, Buffer buffer) {
  106. String modifiedFilename = file_optimizePath(filename);
  107. if (!buffer_exists(buffer)) {
  108. throwError(U"buffer_save: Cannot save a buffer that don't exist to a file.\n");
  109. } else {
  110. FILE *file = accessFile(modifiedFilename, true);
  111. if (file != nullptr) {
  112. fwrite((void*)buffer_dangerous_getUnsafeData(buffer), buffer_getSize(buffer), 1, file);
  113. fclose(file);
  114. } else {
  115. throwError("Failed to save ", filename, " which was optimized into ", modifiedFilename, ".\n");
  116. }
  117. }
  118. }
  119. const char32_t* file_separator() {
  120. return pathSeparator;
  121. }
  122. inline bool isSeparator(DsrChar c) {
  123. return c == U'\\' || c == U'/';
  124. }
  125. // Returns the index of the last / or \ in path, or defaultIndex if none existed.
  126. static int64_t getLastSeparator(const ReadableString &path, int defaultIndex) {
  127. for (int64_t i = string_length(path) - 1; i >= 0; i--) {
  128. DsrChar c = path[i];
  129. if (isSeparator(c)) {
  130. return i;
  131. }
  132. }
  133. return defaultIndex;
  134. }
  135. String file_optimizePath(const ReadableString &path) {
  136. String result;
  137. int inputLength = string_length(path);
  138. string_reserve(result, inputLength);
  139. for (int i = 0; i < inputLength; i++) {
  140. DsrChar c = path[i];
  141. if (isSeparator(c)) {
  142. string_append(result, pathSeparator);
  143. } else {
  144. string_appendChar(result, c);
  145. }
  146. }
  147. return result;
  148. }
  149. ReadableString file_getPathlessName(const ReadableString &path) {
  150. return string_after(path, getLastSeparator(path, -1));
  151. }
  152. ReadableString file_getParentFolder(const ReadableString &path) {
  153. return string_before(path, getLastSeparator(path, 0));
  154. }
  155. bool file_hasRoot(const ReadableString &path) {
  156. #ifdef USE_MICROSOFT_WINDOWS
  157. // If a colon is found, it is a root path.
  158. return string_findFirst(path, U':') > -1;
  159. #else
  160. // If the path begins with a separator, it is the root folder in Posix systems.
  161. return path[0] == U'/';
  162. #endif
  163. }
  164. bool file_setCurrentPath(const ReadableString &path) {
  165. Buffer buffer;
  166. const NativeChar *nativePath = toNativeString(file_optimizePath(path), buffer);
  167. #ifdef USE_MICROSOFT_WINDOWS
  168. return SetCurrentDirectoryW(nativePath);
  169. #else
  170. return chdir(nativePath) == 0;
  171. #endif
  172. }
  173. String file_getCurrentPath() {
  174. #ifdef USE_MICROSOFT_WINDOWS
  175. NativeChar resultBuffer[maxLength + 1] = {0};
  176. GetCurrentDirectoryW(maxLength, resultBuffer);
  177. return fromNativeString(resultBuffer);
  178. #else
  179. NativeChar resultBuffer[maxLength + 1] = {0};
  180. getcwd(resultBuffer, maxLength);
  181. return fromNativeString(resultBuffer);
  182. #endif
  183. }
  184. #ifdef USE_MICROSOFT_WINDOWS
  185. static String file_getApplicationFilePath() {
  186. NativeChar resultBuffer[maxLength + 1] = {0};
  187. GetModuleFileNameW(nullptr, resultBuffer, maxLength);
  188. return fromNativeString(resultBuffer);
  189. }
  190. #endif
  191. String file_getApplicationFolder(bool allowFallback) {
  192. #ifdef USE_MICROSOFT_WINDOWS
  193. return file_getParentFolder(file_getApplicationFilePath());
  194. #else
  195. NativeChar resultBuffer[maxLength + 1] = {0};
  196. if (readlink("/proc/self/exe", resultBuffer, maxLength) != -1) {
  197. // Linux detected
  198. return file_getParentFolder(fromNativeString(resultBuffer));
  199. } else if (readlink("/proc/curproc/file", resultBuffer, maxLength) != -1) {
  200. // BSD detected
  201. return file_getParentFolder(fromNativeString(resultBuffer));
  202. } else if (readlink("/proc/self/path/a.out", resultBuffer, maxLength) != -1) {
  203. // Solaris detected
  204. return file_getParentFolder(fromNativeString(resultBuffer));
  205. } else if (allowFallback) {
  206. return file_getCurrentPath();
  207. } else {
  208. throwError("file_getApplicationFolder is not implemented for the current system!\n");
  209. return U"";
  210. }
  211. #endif
  212. }
  213. String file_combinePaths(const ReadableString &a, const ReadableString &b) {
  214. if (isSeparator(a[string_length(a) - 1])) {
  215. // Already ending with a separator.
  216. return string_combine(a, b);
  217. } else {
  218. // Combine using a separator.
  219. return string_combine(a, pathSeparator, b);
  220. }
  221. }
  222. String file_getAbsolutePath(const ReadableString &path) {
  223. if (file_hasRoot(path)) {
  224. return path;
  225. } else {
  226. return file_combinePaths(file_getCurrentPath(), path);
  227. }
  228. }
  229. }