LCOV - code coverage report
Current view: top level - core/filesystem - SPFilesystem.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 57 95 60.0 %
Date: 2024-05-12 00:16:13 Functions: 15 23 65.2 %

          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_ */

Generated by: LCOV version 1.14