Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #ifndef STAPPLER_FILESYSTEM_SPFILESYSTEM_H_
25 : #define STAPPLER_FILESYSTEM_SPFILESYSTEM_H_
26 :
27 : #include "SPIO.h"
28 : #include "SPTime.h"
29 : #include "SPFilepath.h"
30 :
31 : namespace STAPPLER_VERSIONIZED stappler::filesystem {
32 :
33 : enum Access {
34 : Exists,
35 : Read,
36 : Write,
37 : Execute
38 : };
39 :
40 : struct Stat {
41 : size_t size = 0;
42 : Time atime;
43 : Time ctime;
44 : Time mtime;
45 : bool isDir = false;
46 : };
47 :
48 : class File {
49 : public:
50 : enum class Flags {
51 : None,
52 : DelOnClose
53 : };
54 :
55 : using traits_type = std::char_traits<char>;
56 : using streamsize = std::streamsize;
57 : using int_type = typename traits_type::int_type;
58 :
59 : static File open_tmp(StringView prefix, bool delOnClose = true);
60 :
61 : File();
62 : explicit File(FILE *, Flags = Flags::None);
63 : explicit File(void *);
64 : explicit File(void *, size_t);
65 :
66 : ~File();
67 :
68 : File(File &&);
69 : File & operator=(File &&);
70 :
71 : File(const File &) = delete;
72 : File & operator=(const File &) = delete;
73 :
74 : size_t read(uint8_t *buf, size_t nbytes);
75 : size_t seek(int64_t offset, io::Seek s);
76 :
77 : size_t tell() const;
78 : size_t size() const;
79 :
80 : int_type xsgetc();
81 : int_type xsputc(int_type c);
82 :
83 : streamsize xsputn(const char* s, streamsize n);
84 : streamsize xsgetn(char* s, streamsize n);
85 :
86 : bool eof() const;
87 : void close();
88 : void close_remove();
89 :
90 : bool close_rename(StringView);
91 :
92 : bool is_open() const;
93 20465 : explicit operator bool() const { return is_open(); }
94 :
95 : const char *path() const;
96 :
97 : protected:
98 : void set_tmp_path(const char *);
99 :
100 : bool _isBundled = false;
101 : size_t _size = 0;
102 : Flags _flags = Flags::None;
103 : char _buf[256] = { 0 };
104 : union {
105 : FILE *_nativeFile;
106 : void *_platformFile;
107 : };
108 : };
109 :
110 : // Check if file at path exists
111 : bool exists(StringView path);
112 :
113 : bool stat(StringView path, Stat &);
114 :
115 : // create dir at path (just mkdir, not mkdir -p)
116 : bool mkdir(StringView path);
117 :
118 : // mkdir -p (
119 : bool mkdir_recursive(StringView path, bool appWide = true);
120 :
121 : // touch (set mtime to now) file
122 : bool touch(StringView path);
123 :
124 : // move file from source to dest (tries to rename file, then copy-remove, rename will be successful only if both path is on single physical drive)
125 : bool move(StringView source, StringView dest);
126 :
127 : // copy file or directory to dest; use ftw_b for dirs, no directory tree check
128 : bool copy(StringView source, StringView dest, bool stopOnError = true);
129 :
130 : // remove file or directory
131 : // if not recursive, only single file or empty dir will be removed
132 : // if withDirs == false, only file s in directory tree will be removed
133 : bool remove(StringView path, bool recursive = false, bool withDirs = false);
134 :
135 : // file-tree-walk, walk across directory tree at path, callback will be called for each file or directory
136 : // path in callback is absolute
137 : // depth = -1 - unlimited
138 : // dirFirst == true - directory will be shown before files inside them, useful for listings and copy
139 : // dirFirst == false - directory will be shown after files, useful for remove
140 : void ftw(StringView path, const Callback<void(StringView path, bool isFile)> &, int depth = -1, bool dirFirst = false);
141 :
142 : // same as ftw, but iteration can be stopped by returning false from callback
143 : bool ftw_b(StringView path, const Callback<bool(StringView path, bool isFile)> &, int depth = -1, bool dirFirst = false);
144 :
145 : // returns application writable path (or path inside writable dir, if path is set
146 : // if relative == false - do not merge paths, if provided path is absolute
147 : //
148 : // Writable path should be used for sharable, but not valuable contents,
149 : // or caches, that should not be removed, when application is running or in background
150 : // On android, writable path is on same drive or device, that used for application file
151 : // This library use writable path to store fonts, icons caches and assets
152 : template <typename Interface>
153 : auto writablePath(StringView path = StringView(), bool relative = false) -> typename Interface::StringType;
154 :
155 : template <typename Interface>
156 : auto writablePathReadOnly(StringView path = StringView(), bool relative = false) -> typename Interface::StringType;
157 :
158 : // returns application documents path (or path inside documents dir, if path is set
159 : // if relative == false - do not merge paths, if provided path is absolute
160 : //
161 : // Documents path should be used for valuable data, like documents, created by user,
162 : // or content, that will be hard to recreate
163 : // This library stores StoreKit and purchases data in documents dir
164 : template <typename Interface>
165 : auto documentsPath(StringView path = StringView(), bool relative = false) -> typename Interface::StringType;
166 :
167 : template <typename Interface>
168 : auto documentsPathReadOnly(StringView path = StringView(), bool relative = false) -> typename Interface::StringType;
169 :
170 : // returns application current work dir from getcwd (or path inside current dir, if path is set
171 : // if relative == false - do not merge paths, if provided path is absolute
172 : //
173 : // Current work dir is technical concept. Use it only if there is good reason for it
174 : template <typename Interface>
175 : auto currentDir(StringView path = StringView(), bool relative = false) -> typename Interface::StringType;
176 :
177 : // returns application caches dir (or path inside caches dir, if path is set
178 : // if relative == false - do not merge paths, if provided path is absolute
179 : //
180 : // Caches dir used to store caches or content, that can be easily recreated,
181 : // and that can be removed/erased, when application is active or in background
182 : // On android, caches will be placed on SD card, if it's available
183 : template <typename Interface>
184 : auto cachesPath(StringView path = StringView(), bool relative = false) -> typename Interface::StringType;
185 :
186 : template <typename Interface>
187 : auto cachesPathReadOnly(StringView path = StringView(), bool relative = false) -> typename Interface::StringType;
188 :
189 : // write data into file on path
190 : bool write(StringView path, const uint8_t *data, size_t len);
191 :
192 : template <typename BytesView>
193 175 : inline bool write(StringView path, const BytesView &view) {
194 175 : return write(path, reinterpret_cast<const uint8_t *>(view.data()), size_t(view.size()));
195 : }
196 :
197 : File openForReading(StringView path);
198 :
199 : // read file to string (if it was a binary file, string will be invalid)
200 : template <typename Interface>
201 : auto readTextFile(StringView path) -> typename Interface::StringType;
202 :
203 : bool readIntoBuffer(uint8_t *buf, StringView path, size_t off = 0, size_t size = maxOf<size_t>());
204 : bool readWithConsumer(const io::Consumer &stream, uint8_t *buf, size_t bsize, StringView path, size_t off, size_t size);
205 :
206 : template <size_t Buffer = 1_KiB>
207 : bool readWithConsumer(const io::Consumer &stream, StringView path,
208 : size_t off = 0, size_t size = maxOf<size_t>()) {
209 : uint8_t b[Buffer];
210 : return readWithConsumer(stream, b, Buffer, path, off, size);
211 : }
212 :
213 : template <typename Interface>
214 17815 : auto readIntoMemory(StringView ipath, size_t off = 0, size_t size = maxOf<size_t>()) -> typename Interface::BytesType {
215 17815 : auto f = openForReading(ipath);
216 17815 : if (f) {
217 17715 : auto fsize = f.size();
218 17715 : if (fsize <= off) {
219 0 : f.close();
220 0 : return typename Interface::BytesType();
221 : }
222 17715 : if (fsize - off < size) {
223 17715 : size = fsize - off;
224 : }
225 17715 : typename Interface::BytesType ret; ret.resize(size);
226 17715 : f.seek(off, io::Seek::Set);
227 17715 : f.read(ret.data(), size);
228 17715 : f.close();
229 17715 : return ret;
230 17715 : }
231 100 : return typename Interface::BytesType();
232 17815 : }
233 :
234 : StringView detectMimeType(StringView path);
235 :
236 : }
237 :
238 :
239 : namespace STAPPLER_VERSIONIZED stappler::filesystem::platform {
240 :
241 : #if ANDROID
242 : void Android_initializeFilesystem(void *assetManager, StringView filesDir, StringView cachesDir, StringView apkPath);
243 : void Android_terminateFilesystem();
244 : StringView Android_getApkPath();
245 : #endif
246 :
247 : template <typename Interface>
248 : auto _getApplicationPath() -> typename Interface::StringType;
249 :
250 : template <typename Interface>
251 : auto _getWritablePath(bool readOnly) -> typename Interface::StringType;
252 :
253 : template <typename Interface>
254 : auto _getDocumentsPath(bool readOnly) -> typename Interface::StringType;
255 :
256 : template <typename Interface>
257 : auto _getCachesPath(bool readOnly) -> typename Interface::StringType;
258 :
259 : bool _exists(StringView path, bool assetsRoot = true);
260 : bool _stat(StringView path, Stat &, bool assetsRoot = true);
261 :
262 : File _openForReading(StringView);
263 : size_t _read(void *, uint8_t *buf, size_t nbytes);
264 : size_t _seek(void *, int64_t offset, io::Seek s);
265 : size_t _tell(void *);
266 : bool _eof(void *);
267 : void _close(void *);
268 :
269 : void _ftw(StringView path, const Callback<void(StringView path, bool isFile)> &, int depth, bool dirFirst, bool assetsRoot = true);
270 :
271 : bool _ftw_b(StringView path, const Callback<bool(StringView path, bool isFile)> &, int depth, bool dirFirst, bool assetsRoot = true);
272 :
273 : }
274 :
275 :
276 : // functions to access native filesystem directly
277 : // libstappler uses posix path scheme, it should be transformed when transferred from/to other apps/libraries
278 : // no transformation required within libstappler itself
279 : namespace STAPPLER_VERSIONIZED stappler::filesystem::native {
280 :
281 : // C:\dirname\filename -> /c/dirname/filename
282 : template <typename Interface>
283 : auto nativeToPosix(StringView path) -> typename Interface::StringType;
284 :
285 : // /c/dirname/filename -> C:\dirname\filename
286 : // be sure that path is absolute
287 : template <typename Interface>
288 : auto posixToNative(StringView path) -> typename Interface::StringType;
289 :
290 : template <typename Interface>
291 : auto getcwd_fn() -> typename Interface::StringType;
292 :
293 : bool remove_fn(StringView path);
294 : bool unlink_fn(StringView path);
295 : bool mkdir_fn(StringView path);
296 :
297 : bool access_fn(StringView path, Access mode);
298 :
299 : bool stat_fn(StringView path, Stat &);
300 :
301 : bool touch_fn(StringView path);
302 :
303 : void ftw_fn(StringView path, const Callback<void(StringView path, bool isFile)> &, int depth, bool dirFirst);
304 : bool ftw_b_fn(StringView path, const Callback<bool(StringView path, bool isFile)> &, int depth, bool dirFirst);
305 :
306 : bool rename_fn(StringView source, StringView dest);
307 :
308 : FILE *fopen_fn(StringView, StringView mode);
309 :
310 : bool write_fn(StringView, const unsigned char *data, size_t len);
311 :
312 : }
313 :
314 : namespace STAPPLER_VERSIONIZED stappler::filesystem {
315 :
316 : template <typename Interface>
317 272 : auto writablePath(StringView path, bool relative) -> typename Interface::StringType {
318 272 : if (!path.empty() && !relative && filepath::isAbsolute(path)) {
319 50 : return path.str<Interface>();
320 : }
321 222 : auto wpath = filesystem::platform::_getWritablePath<Interface>(true);
322 222 : if (path.empty()) {
323 100 : return wpath;
324 : }
325 122 : return filepath::merge<Interface>(wpath, path);
326 222 : }
327 :
328 : template <typename Interface>
329 0 : auto writablePathReadOnly(StringView path, bool relative) -> typename Interface::StringType {
330 0 : if (!path.empty() && !relative && filepath::isAbsolute(path)) {
331 0 : return path.str<Interface>();
332 : }
333 0 : auto wpath = filesystem::platform::_getWritablePath<Interface>(false);
334 0 : if (path.empty()) {
335 0 : return wpath;
336 : }
337 0 : return filepath::merge<Interface>(wpath, path);
338 0 : }
339 :
340 : template <typename Interface>
341 226 : auto cachesPath(StringView path, bool relative) -> typename Interface::StringType {
342 226 : if (!path.empty() && !relative && filepath::isAbsolute(path)) {
343 100 : return path.str<Interface>();
344 : }
345 126 : auto cpath = filesystem::platform::_getCachesPath<Interface>(true);
346 126 : if (path.empty()) {
347 42 : return cpath;
348 : }
349 84 : return filepath::merge<Interface>(cpath, path);
350 126 : }
351 :
352 : template <typename Interface>
353 0 : auto cachesPathReadOnly(StringView path, bool relative) -> typename Interface::StringType {
354 0 : if (!path.empty() && !relative && filepath::isAbsolute(path)) {
355 0 : return path.str<Interface>();
356 : }
357 0 : auto cpath = filesystem::platform::_getCachesPath<Interface>(false);
358 0 : if (path.empty()) {
359 0 : return cpath;
360 : }
361 0 : return filepath::merge<Interface>(cpath, path);
362 0 : }
363 :
364 : template <typename Interface>
365 0 : auto documentsPath(StringView path, bool relative) -> typename Interface::StringType {
366 0 : if (!path.empty() && !relative && filepath::isAbsolute(path)) {
367 0 : return path.str<Interface>();
368 : }
369 0 : auto dpath = filesystem::platform::_getDocumentsPath<Interface>(true);
370 0 : if (path.empty()) {
371 0 : return dpath;
372 : }
373 0 : return filepath::merge<Interface>(dpath, path);
374 0 : }
375 :
376 : template <typename Interface>
377 0 : auto documentsPathReadOnly(StringView path, bool relative) -> typename Interface::StringType {
378 0 : if (!path.empty() && !relative && filepath::isAbsolute(path)) {
379 0 : return path.str<Interface>();
380 : }
381 0 : auto dpath = filesystem::platform::_getDocumentsPath<Interface>(false);
382 0 : if (path.empty()) {
383 0 : return dpath;
384 : }
385 0 : return filepath::merge<Interface>(dpath, path);
386 0 : }
387 :
388 : template <typename Interface>
389 3409 : auto currentDir(StringView path, bool relative) -> typename Interface::StringType {
390 3409 : if (!path.empty() && !relative && filepath::isAbsolute(path)) {
391 0 : return path.str<Interface>();
392 : }
393 3409 : auto cwd = filesystem::native::getcwd_fn<Interface>();
394 3409 : if (!cwd.empty()) {
395 3409 : if (path.empty()) {
396 75 : return cwd;
397 : } else {
398 3334 : return filepath::merge<Interface>(cwd, path);
399 : }
400 : }
401 0 : return typename Interface::StringType();
402 3409 : }
403 :
404 : template <typename Interface>
405 1450 : auto readTextFile(StringView ipath) -> typename Interface::StringType {
406 1450 : auto f = openForReading(ipath);
407 1450 : if (f) {
408 1425 : auto fsize = f.size();
409 1425 : typename Interface::StringType ret; ret.resize(fsize);
410 1425 : f.read((uint8_t *)ret.data(), fsize);
411 1425 : f.close();
412 1425 : return ret;
413 1425 : }
414 25 : return typename Interface::StringType();
415 1450 : }
416 :
417 : }
418 :
419 : namespace STAPPLER_VERSIONIZED stappler::io {
420 :
421 : template <>
422 : struct ProducerTraits<filesystem::File> {
423 : using type = filesystem::File;
424 641269 : static size_t ReadFn(void *ptr, uint8_t *buf, size_t nbytes) {
425 641269 : return ((type *)ptr)->read(buf, nbytes);
426 : }
427 :
428 5844 : static size_t SeekFn(void *ptr, int64_t offset, Seek s) {
429 5844 : return ((type *)ptr)->seek(offset, s);
430 : }
431 0 : static size_t TellFn(void *ptr) {
432 0 : return ((type *)ptr)->tell();
433 : }
434 : };
435 :
436 : }
437 :
438 : #endif /* STAPPLER_FILESYSTEM_SPFILESYSTEM_H_ */
|