reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

filesystem.cpp (4524B)


      1 /* ReaPack: Package manager for REAPER
      2  * Copyright (C) 2015-2025  Christian Fillion
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU Lesser General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU Lesser General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU Lesser General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  */
     17 
     18 #include "filesystem.hpp"
     19 
     20 #include "path.hpp"
     21 #include "win32.hpp"
     22 
     23 #include <cerrno>
     24 #include <cstdio>
     25 #include <fstream>
     26 #include <sys/stat.h>
     27 
     28 #ifdef _WIN32
     29 #  include <windows.h>
     30 #  define stat _stat
     31 #endif
     32 
     33 static auto nativePath(const Path &path)
     34 {
     35   return Win32::widen(path.prependRoot().join());
     36 }
     37 
     38 static bool stat(const Path &path, struct stat *out)
     39 {
     40 #ifdef _WIN32
     41   constexpr auto func = &_wstat;
     42 #else
     43   constexpr int(*func)(const char *, struct stat *) = &::stat;
     44 #endif
     45 
     46   return !func(nativePath(path).c_str(), out);
     47 }
     48 
     49 FILE *FS::open(const Path &path)
     50 {
     51   const auto &fullPath = nativePath(path);
     52 
     53 #ifdef _WIN32
     54   FILE *file = nullptr;
     55   _wfopen_s(&file, fullPath.c_str(), L"rb");
     56   return file;
     57 #else
     58   return fopen(fullPath.c_str(), "rb");
     59 #endif
     60 }
     61 
     62 bool FS::open(std::ifstream &stream, const Path &path)
     63 {
     64   stream.open(nativePath(path), std::ios_base::binary);
     65   return stream.good();
     66 }
     67 
     68 bool FS::open(std::ofstream &stream, const Path &path)
     69 {
     70   if(!mkdir(path.dirname()))
     71     return false;
     72 
     73   stream.open(nativePath(path), std::ios_base::binary);
     74   return stream.good();
     75 }
     76 
     77 bool FS::write(const Path &path, const std::string &contents)
     78 {
     79   std::ofstream file;
     80   if(!open(file, path))
     81     return false;
     82 
     83   file << contents;
     84   file.close();
     85 
     86   return true;
     87 }
     88 
     89 bool FS::rename(const TempPath &path)
     90 {
     91 #ifdef _WIN32
     92   remove(path.target());
     93 #endif
     94 
     95   return rename(path.temp(), path.target());
     96 }
     97 
     98 bool FS::rename(const Path &from, const Path &to)
     99 {
    100 #ifdef _WIN32
    101   const auto func = &_wrename;
    102 #else
    103   const auto func = &::rename;
    104 #endif
    105 
    106   return !func(nativePath(from).c_str(), nativePath(to).c_str());
    107 }
    108 
    109 bool FS::remove(const Path &path)
    110 {
    111   const auto &fullPath = nativePath(path);
    112 
    113 #ifdef _WIN32
    114   if(GetFileAttributes(fullPath.c_str()) == INVALID_FILE_ATTRIBUTES
    115       && GetLastError() == ERROR_FILE_NOT_FOUND)
    116     return true;
    117 
    118   if(GetFileAttributes(fullPath.c_str()) & FILE_ATTRIBUTE_DIRECTORY)
    119     return RemoveDirectory(fullPath.c_str()) != 0;
    120   else if(!_wremove(fullPath.c_str()))
    121     return true;
    122 
    123   // So the file cannot be removed (probably because it's in use)?
    124   // Then let's move it somewhere else. And delete it on next startup.
    125   // Windows is so great!
    126 
    127   Path workaroundPath = Path::DATA;
    128   workaroundPath.append("old_" + path.basename() + ".tmp");
    129 
    130   return rename(path, workaroundPath);
    131 #else
    132   return !::remove(fullPath.c_str());
    133 #endif
    134 }
    135 
    136 bool FS::removeRecursive(const Path &file)
    137 {
    138   if(!remove(file))
    139     return false;
    140 
    141   Path dir = file;
    142 
    143   // remove empty directories, but not top-level ones that were created by REAPER
    144   while(dir.size() > 2) {
    145     dir.removeLast();
    146 
    147     if(!remove(dir))
    148       break;
    149   }
    150 
    151   return true;
    152 }
    153 
    154 bool FS::mtime(const Path &path, time_t *time)
    155 {
    156   struct stat st;
    157 
    158   if(!stat(path, &st))
    159     return false;
    160 
    161   *time = st.st_mtime;
    162 
    163   return true;
    164 }
    165 
    166 bool FS::exists(const Path &path, const bool dir)
    167 {
    168   struct stat st;
    169 
    170   if(stat(path, &st))
    171     return (bool)(st.st_mode & S_IFDIR) == dir;
    172   else
    173     return false;
    174 }
    175 
    176 bool FS::mkdir(const Path &path)
    177 {
    178   if(exists(path, true))
    179     return true;
    180 
    181   Path fullPath = Path::root();
    182 
    183   for(const std::string &dir : path) {
    184     fullPath.append(dir);
    185 
    186     const auto &joined = Win32::widen(fullPath.join());
    187 
    188 #ifdef _WIN32
    189     if(!CreateDirectory(joined.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS)
    190       return false;
    191 #else
    192     if(::mkdir(joined.c_str(), 0777) && errno != EEXIST)
    193       return false;
    194 #endif
    195   }
    196 
    197   return true;
    198 }
    199 
    200 Path FS::canonical(const Path &path)
    201 {
    202 #ifdef _WIN32
    203   return path;
    204 #else
    205   char *resolved = realpath(path.join().c_str(), nullptr);
    206   if(!resolved)
    207     return path;
    208   Path out(resolved);
    209   free(resolved);
    210   return out;
    211 #endif
    212 }
    213 
    214 const char *FS::lastError()
    215 {
    216   return strerror(errno);
    217 }