fileAPI.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. #ifndef DFPSR_API_FILE
  24. #define DFPSR_API_FILE
  25. #include "stringAPI.h"
  26. #include "bufferAPI.h"
  27. #include "../base/Handle.h"
  28. #include "../settings.h"
  29. // The file API exists to save and load buffers of data for any type of file.
  30. // Any file format that is implemented against the Buffer type instead of hardcoding against the file stream can easily be
  31. // reused for additional layers of processing such as checksums, archiving, compression, encryption, sending over a network...
  32. // The file API also has functions for processing paths to the filesystem and calling other programs.
  33. namespace dsr {
  34. // The PathSyntax enum allow processing theoreical paths for other operating systems than the local.
  35. enum class PathSyntax { Windows, Posix };
  36. #if defined(USE_MICROSOFT_WINDOWS)
  37. // Let the local syntax be for Windows.
  38. #define LOCAL_PATH_SYNTAX dsr::PathSyntax::Windows
  39. #elif defined(USE_POSIX)
  40. // Let the local syntax be for Posix.
  41. #define LOCAL_PATH_SYNTAX dsr::PathSyntax::Posix
  42. #else
  43. #error "The target platform was not recognized as one of the supported operating systems in the file API!\n"
  44. #endif
  45. // Define NO_IMPLICIT_PATH_SYNTAX before including the header if you want all PathSyntax arguments to be explicit.
  46. // If a function you are calling adds a new pathSyntax argument, defining NO_IMPLICIT_PATH_SYNTAX will make sure that you get a warning from the compiler after upgrading the library.
  47. #ifdef NO_IMPLICIT_PATH_SYNTAX
  48. // No deafult argument for PathSyntax input.
  49. #define IMPLICIT_PATH_SYNTAX
  50. #else
  51. // Local deafult argument for PathSyntax input.
  52. #define IMPLICIT_PATH_SYNTAX = LOCAL_PATH_SYNTAX
  53. #endif
  54. // Path-syntax: According to the local computer.
  55. // Post-condition:
  56. // Returns the content of the readable file referred to by file_optimizePath(filename).
  57. // If mustExist is true, then failure to load will throw an exception.
  58. // If mustExist is false, then failure to load will return an empty handle (returning false for buffer_exists).
  59. Buffer file_loadBuffer(const ReadableString& filename, bool mustExist = true);
  60. // Path-syntax: According to the local computer.
  61. // Side-effect: Saves buffer to file_optimizePath(filename) as a binary file.
  62. // Pre-condition: buffer exists.
  63. // If mustWork is true, then failure to load will throw an exception.
  64. // If mustWork is false, then failure to load will return false.
  65. // Post-condition: Returns true iff the buffer could be saved as a file.
  66. bool file_saveBuffer(const ReadableString& filename, Buffer buffer, bool mustWork = true);
  67. // Path-syntax: According to the local computer.
  68. // Pre-condition: file_getEntryType(path) == EntryType::SymbolicLink
  69. // Post-condition: Returns the destination of a symbolic link as an absolute path.
  70. // Shortcuts with file extensions are counted as files, not links.
  71. String file_followSymbolicLink(const ReadableString &path, bool mustExist = true);
  72. // Path-syntax: According to the local computer.
  73. // Get a path separator for the target operating system.
  74. // Can be used to construct a file path that works for both forward and backward slash separators.
  75. const char32_t* file_separator();
  76. // Path-syntax: This operation can handle separators given from any supported platform.
  77. // because separators will be corrected by file_optimizePath when used to access files.
  78. // Post-condition: Returns true iff the character C is a folder separator, currently / and \ are accepted as folder separators.
  79. bool file_isSeparator(DsrChar c);
  80. // Path-syntax: This operation can handle separators given from any supported platform.
  81. // Post-condition: Returns the base-zero index of the first path separator (as defined by file_isSeparator) starting from startIndex, or defaultIndex if not found.
  82. // If the result is not equal to defaultIndex, it is guaranteed to be equal to or greater than startIndex.
  83. // Similar to string_findFirst, but with the character to find repalced with a replacable default index.
  84. int64_t file_findFirstSeparator(const ReadableString &path, int64_t defaultIndex = -1, int64_t startIndex = 0);
  85. // Path-syntax: This operation can handle separators given from any supported platform.
  86. // Post-condition: Returns the base-zero index of the last path separator (as defined by file_isSeparator), or defaultIndex if not found.
  87. // Similar to string_findLast, but with the character to find repalced with a replacable default index.
  88. int64_t file_findLastSeparator(const ReadableString &path, int64_t defaultIndex = -1);
  89. // Returns callbacks for non-empty entries between separator characters.
  90. // This includes folder names, filenames, symbolic links, drive letter.
  91. // This excludes blank space before the first or after the last separator, so the implicit Windows drive \ and Posix system root / will not be included.
  92. // Path-syntax: This operation can handle separators given from any supported platform.
  93. // It should handle both / and \, with and without spaces in filenames.
  94. // Side-effect: Returns a callback for each selectable non-empty entry in the path, from left to right.
  95. // The first argument in the callback is the entry name, and the integers are an inclusive base-zero index range from first to last characters in path.
  96. // The first entry must be something selectable to be included. Otherwise it is ignored.
  97. // C: would be returned as an entry, because other drives can be selected.
  98. // The implicit Windows drive \ and Posix system root / will not be returned, because they are implicit and can't be replaced in the path.
  99. void file_getPathEntries(const ReadableString& path, std::function<void(ReadableString, int64_t, int64_t)> action);
  100. // Path-syntax: Depends on pathSyntax argument.
  101. // Turns / and \ into the path convention specified by pathSyntax, which is the local system's by default.
  102. // Removes redundant . and .. to reduce the risk of running out of buffer space when calling the system.
  103. String file_optimizePath(const ReadableString &path, PathSyntax pathSyntax IMPLICIT_PATH_SYNTAX);
  104. // Path-syntax: Depends on pathSyntax argument.
  105. // Combines two parts into a path and automatically adding a local separator when needed.
  106. // Can be used to get the full path of a file in a folder or add another folder to the path.
  107. // b may not begin with a separator, because only a is allowed to contain the root.
  108. String file_combinePaths(const ReadableString &a, const ReadableString &b, PathSyntax pathSyntax IMPLICIT_PATH_SYNTAX);
  109. // Extended to allow combining three or more paths.
  110. // Example:
  111. // String fullPath = file_combinePaths(myApplicationPath, U"myFolder", U"myOtherFolder, U"myFile.txt");
  112. template<typename... TAIL>
  113. inline String file_combinePaths(const ReadableString &a, const ReadableString &b, TAIL... tail) {
  114. return file_combinePaths(file_combinePaths(a, b), tail...);
  115. }
  116. // Path-syntax: Depends on pathSyntax argument.
  117. // Post-condition: Returns true iff path starts from a root, according to the path syntax.
  118. // Implicit drives on Windows using \ are treated as roots because we know that there is nothing above them.
  119. // If treatHomeFolderAsRoot is true, starting from the /home/username folder using the Posix ~ alias will be allowed as a root as well, because we can't append it behind another path.
  120. bool file_hasRoot(const ReadableString &path, bool treatHomeFolderAsRoot, PathSyntax pathSyntax IMPLICIT_PATH_SYNTAX);
  121. // Path-syntax: Depends on pathSyntax argument.
  122. // Returns true iff path is a root without any files nor folder names following.
  123. // Does not check if it actually exists, so use file_getEntryType on the actual folders and files for verifying existence.
  124. bool file_isRoot(const ReadableString &path, bool treatHomeFolderAsRoot, PathSyntax pathSyntax IMPLICIT_PATH_SYNTAX);
  125. // DSR_MAIN_CALLER is a convenient wrapper for getting input arguments as a list of portable Unicode strings.
  126. // The actual main function gets placed in DSR_MAIN_CALLER, which calls the given function.
  127. // Example:
  128. // DSR_MAIN_CALLER(dsrMain)
  129. // void dsrMain(List<String> args) {
  130. // printText("Input arguments:\n");
  131. // for (int a = 0; a < args.length(); a++) {
  132. // printText(" args[", a, "] = ", args[a], "\n");
  133. // }
  134. // }
  135. #ifdef USE_MICROSOFT_WINDOWS
  136. #define DSR_MAIN_CALLER(MAIN_NAME) \
  137. void MAIN_NAME(dsr::List<dsr::String> args); \
  138. int main() { \
  139. dsr::heap_startingApplication(); \
  140. MAIN_NAME(dsr::file_impl_getInputArguments()); \
  141. dsr::heap_terminatingApplication(); \
  142. return 0; \
  143. }
  144. #else
  145. #define DSR_MAIN_CALLER(MAIN_NAME) \
  146. void MAIN_NAME(dsr::List<dsr::String> args); \
  147. int main(int argc, char **argv) { \
  148. dsr::heap_startingApplication(); \
  149. MAIN_NAME(dsr::file_impl_convertInputArguments(argc, (void**)argv)); \
  150. dsr::heap_terminatingApplication(); \
  151. return 0; \
  152. }
  153. #endif
  154. // Helper functions have to be exposed for the macro handle your input arguments.
  155. // Do not call these yourself.
  156. List<String> file_impl_convertInputArguments(int argn, void **argv);
  157. List<String> file_impl_getInputArguments();
  158. // Path-syntax: According to the local computer.
  159. // Post-condition: Returns the current path, from where the application was called and relative paths start.
  160. String file_getCurrentPath();
  161. // Path-syntax: According to the local computer.
  162. // Side-effects: Sets the current path to file_optimizePath(path).
  163. // Post-condition: Returns Returns true on success and false on failure.
  164. bool file_setCurrentPath(const ReadableString &path);
  165. // Path-syntax: According to the local computer.
  166. // Post-condition: Returns the application's folder path, from where the application is stored.
  167. // If not implemented and allowFallback is true, the current path is returned instead as a qualified guess instead of raising an exception.
  168. String file_getApplicationFolder(bool allowFallback = true);
  169. // Path-syntax: This trivial operation should work the same independent of operating system.
  170. // Otherwise you just have to add a new argument after upgrading the static library.
  171. // Post-condition: Returns the local name of the file or folder after the last path separator, or the whole path if no separator was found.
  172. ReadableString file_getPathlessName(const ReadableString &path);
  173. // Path-syntax: This trivial operation should work the same independent of operating system.
  174. // Post-condition: Returns the filename's extension, or U"" if there is none.
  175. // This function can not tell if something is a folder or not, because there are file types on Posix systems that have no extension either.
  176. // Use file_getEntryType instead if you want to know if it's a file or folder.
  177. ReadableString file_getExtension(const String& filename);
  178. // Path-syntax: This trivial operation should work the same independent of operating system.
  179. // Post-condition: Returns filename without any extension, which is the original input if there is no extension.
  180. // Use to remove a file's type before appending a new extension.
  181. ReadableString file_getExtensionless(const String& filename);
  182. // Path-syntax: This trivial operation should work the same independent of operating system.
  183. // Post-condition: Returns true iff path has an extension, even if it's just an empty dot at the end that file_getExtension would not detect.
  184. // Useful to check if you need to add an extension or if it already exists in a full filename.
  185. bool file_hasExtension(const String& path);
  186. // Quickly gets the relative parent folder by removing the last entry from the string or appending .. at the end.
  187. // Path-syntax: Depends on pathSyntax argument.
  188. // This pure syntax function getting the parent folder does not access the system in any way.
  189. // Does not guarantee that the resulting path is usable on the system.
  190. // It allows using ~ as the root, for writing paths compatible across different user accounts pointing to different but corresponding files.
  191. // Going outside of a relative start will add .. to the path.
  192. // Depending on which current directory the result is applied to, the absolute path may end up as a root followed by multiple .. going nowhere.
  193. // Going outside of the absolute root returns U"?" as an error code.
  194. String file_getRelativeParentFolder(const ReadableString &path, PathSyntax pathSyntax IMPLICIT_PATH_SYNTAX);
  195. // Gets the canonical parent folder using the current directory.
  196. // This function for getting the parent folder treats path relative to the current directory and expands the result into an absolute path.
  197. // Make sure that current directory is where you want it when calling this function, because the current directory may change over time when calling file_setCurrentPath.
  198. // Path-syntax: According to the local computer.
  199. // Pre-conditions:
  200. // path must be valid on the local system, such that you given full permissions could read or create files there relative to the current directory when this function is called.
  201. // ~ is not allowed as the root, because the point of using ~ is to reuse the same path across different user accounts, which does not refer to an absolute home directory.
  202. // Post-condition: Returns the absolute parent to the given path, or U"?" if trying to leave the root or use a tilde home alias.
  203. String file_getAbsoluteParentFolder(const ReadableString &path);
  204. // A theoretical version of file_getAbsoluteParentFolder for evaluation on a theoretical system without actually calling file_getCurrentPath or running on the given system.
  205. // Path-syntax: Depends on pathSyntax argument.
  206. // Post-condition: Returns the absolute parent to the given path, or U"?" if trying to leave the root or use a tilde home alias.
  207. String file_getTheoreticalAbsoluteParentFolder(const ReadableString &path, const ReadableString &currentPath, PathSyntax pathSyntax IMPLICIT_PATH_SYNTAX);
  208. // Gets the canonical absolute version of the path.
  209. // Current directory is expanded, but not user accounts.
  210. // Path-syntax: According to the local computer.
  211. // Post-condition: Returns an absolute version of the path.
  212. String file_getAbsolutePath(const ReadableString &path);
  213. // A theoretical version of file_getAbsolutePath for evaluation on a theoretical system without actually calling file_getCurrentPath or running on the given system.
  214. // Current directory is expanded, but not user accounts.
  215. // Path-syntax: Depends on pathSyntax argument.
  216. // Post-condition: Returns an absolute version of the path.
  217. String file_getTheoreticalAbsolutePath(const ReadableString &path, const ReadableString &currentPath, PathSyntax pathSyntax IMPLICIT_PATH_SYNTAX);
  218. // Path-syntax: According to the local computer.
  219. // Pre-condition: filename must refer to a file so that file_getEntryType(filename) == EntryType::File.
  220. // Post-condition: Returns a structure with information about the file at file_optimizePath(filename), or -1 if no such file exists.
  221. int64_t file_getFileSize(const ReadableString& filename);
  222. // Entry types distinguish between files folders and other things in the file system.
  223. enum class EntryType { NotFound, UnhandledType, File, Folder, SymbolicLink };
  224. String& string_toStreamIndented(String& target, const EntryType& source, const ReadableString& indentation);
  225. // Path-syntax: According to the local computer.
  226. // Post-condition: Returns what the file_optimizePath(path) points to in the filesystem.
  227. // Different comparisons on the result can be used to check if something exists.
  228. // Use file_getEntryType(filename) == EntryType::File to check if a file exists.
  229. // Use file_getEntryType(folderPath) == EntryType::Folder to check if a folder exists.
  230. // Use file_getEntryType(path) != EntryType::NotFound to check if the path leads to anything.
  231. EntryType file_getEntryType(const ReadableString &path);
  232. // Path-syntax: According to the local computer.
  233. // Side-effects: Calls action with the entry's path, name and type for everything detected in folderPath.
  234. // entryPath equals file_combinePaths(folderPath, entryName), and is used for recursive calls when entryType == EntryType::Folder.
  235. // entryName equals file_getPathlessName(entryPath).
  236. // entryType equals file_getEntryType(entryPath).
  237. // Post-condition: Returns true iff the folder could be found.
  238. bool file_getFolderContent(const ReadableString& folderPath, std::function<void(const ReadableString& entryPath, const ReadableString& entryName, EntryType entryType)> action);
  239. // Path-syntax: According to the local computer.
  240. // Access permissions: Default settings according to the local operating system, either inherited from the parent folder or no specific restrictions.
  241. // Pre-condition: The parent of path must already exist, because only one folder is created.
  242. // Side-effects: Creates a folder at path.
  243. // Post-condition: Returns true iff the operation was successful.
  244. bool file_createFolder(const ReadableString &path);
  245. // Path-syntax: According to the local computer.
  246. // Pre-condition: The folder at path must be empty, so any recursion must be done manually.
  247. // Otherwise you might accidentally erase "C:\myFolder\importantDocuments" from giving C:\myFolder \junk instead of "C:\myFolder\junk" as command line arguments.
  248. // Better to implement your own recursive search that confirms for each file and folder that they are no longer needed, for such a dangerous operation.
  249. // Side-effects: Removes the folder at path.
  250. // Post-condition: Returns true iff the operation was successful.
  251. bool file_removeEmptyFolder(const ReadableString& path);
  252. // Path-syntax: According to the local computer.
  253. // Pre-condition: The file at path must exist.
  254. // Side-effects: Removes the file at path. If the same file exists at multiple paths in the system, only the link to the file will be removed.
  255. // Post-condition: Returns true iff the operation was successful.
  256. bool file_removeFile(const ReadableString& filename);
  257. // The status of a program started using file_execute.
  258. enum class DsrProcessStatus {
  259. NotStarted, // The handle was default initialized or the result of a failed call to process_execute.
  260. Running, // The process is still running.
  261. Crashed, // The process has crashed and might not have done the task you asked for.
  262. Completed // The process has terminated successfully, so you may now check if it has written any files for you.
  263. };
  264. // A reference counted handle to a process, so that multiple callers can read the status at any time.
  265. struct DsrProcessImpl;
  266. using DsrProcess = Handle<DsrProcessImpl>;
  267. // Post-condition: Returns the status of process.
  268. DsrProcessStatus process_getStatus(const DsrProcess &process);
  269. // Path-syntax: According to the local computer.
  270. // Pre-condition: The executable at path must exist and have execution rights.
  271. // Side-effects: Starts the program at programPath, with programPath given again as argv[0] and arguments to argv[1...] to the program's main function.
  272. // Post-condition: Returns a DsrProcess handle to the started process.
  273. // On failure to start, the handle is empty and process_getStatus will then return DsrProcessStatus::NotStarted from it.
  274. DsrProcess process_execute(const ReadableString& programPath, List<String> arguments);
  275. }
  276. #endif