LCOV - code coverage report
Current view: top level - source/lib/file - file_system.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 62 97 63.9 %
Date: 2023-01-19 00:18:29 Functions: 8 12 66.7 %

          Line data    Source code
       1             : /* Copyright (C) 2022 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : /*
      24             :  * higher-level interface on top of sysdep/filesystem.h
      25             :  */
      26             : 
      27             : #include "precompiled.h"
      28             : 
      29             : #include "lib/file/file_system.h"
      30             : 
      31             : #include "lib/sysdep/filesystem.h"
      32             : 
      33             : #include <boost/filesystem.hpp>
      34             : #include <memory>
      35             : 
      36         224 : bool DirectoryExists(const OsPath& path)
      37             : {
      38         224 :     WDIR* dir = wopendir(path);
      39         224 :     if(dir)
      40             :     {
      41          99 :         wclosedir(dir);
      42          99 :         return true;
      43             :     }
      44         125 :     return false;
      45             : }
      46             : 
      47             : 
      48           0 : bool FileExists(const OsPath& pathname)
      49             : {
      50             :     struct stat s;
      51           0 :     const bool exists = wstat(pathname, &s) == 0;
      52           0 :     return exists;
      53             : }
      54             : 
      55             : 
      56           0 : u64 FileSize(const OsPath& pathname)
      57             : {
      58             :     struct stat s;
      59           0 :     ENSURE(wstat(pathname, &s) == 0);
      60           0 :     return s.st_size;
      61             : }
      62             : 
      63             : 
      64           1 : Status GetFileInfo(const OsPath& pathname, CFileInfo* pPtrInfo)
      65             : {
      66           1 :     errno = 0;
      67             :     struct stat s;
      68           1 :     memset(&s, 0, sizeof(s));
      69           1 :     if(wstat(pathname, &s) != 0)
      70           0 :         WARN_RETURN(StatusFromErrno());
      71             : 
      72           1 :     *pPtrInfo = CFileInfo(pathname.Filename(), s.st_size, s.st_mtime);
      73           1 :     return INFO::OK;
      74             : }
      75             : 
      76             : 
      77             : struct DirDeleter
      78             : {
      79         767 :     void operator()(WDIR* osDir) const
      80             :     {
      81         767 :         const int ret = wclosedir(osDir);
      82         767 :         ENSURE(ret == 0);
      83         767 :     }
      84             : };
      85             : 
      86         771 : Status GetDirectoryEntries(const OsPath& path, CFileInfos* files, DirectoryNames* subdirectoryNames)
      87             : {
      88             :     // open directory
      89         771 :     errno = 0;
      90         771 :     WDIR* pDir = wopendir(path);
      91         771 :     if(!pDir)
      92           4 :         return StatusFromErrno();   // NOWARN
      93        1534 :     std::shared_ptr<WDIR> osDir(pDir, DirDeleter());
      94             : 
      95             :     for(;;)
      96             :     {
      97        5427 :         errno = 0;
      98        5427 :         struct wdirent* osEnt = wreaddir(osDir.get());
      99        5427 :         if(!osEnt)
     100             :         {
     101             :             // no error, just no more entries to return
     102         767 :             if(!errno)
     103        1534 :                 return INFO::OK;
     104           0 :             WARN_RETURN(StatusFromErrno());
     105             :         }
     106             : 
     107       51623 :         for(size_t i = 0; osEnt->d_name[i] != '\0'; i++)
     108       46963 :             RETURN_STATUS_IF_ERR(Path::Validate(osEnt->d_name[i]));
     109        9320 :         const OsPath name(osEnt->d_name);
     110             : 
     111             :         // get file information (mode, size, mtime)
     112             :         struct stat s;
     113             : #if OS_WIN
     114             :         // .. return wdirent directly (much faster than calling stat).
     115             :         RETURN_STATUS_IF_ERR(wreaddir_stat_np(osDir.get(), &s));
     116             : #else
     117             :         // .. call regular stat().
     118        4660 :         errno = 0;
     119        9320 :         const OsPath pathname = path / name;
     120        4660 :         if(wstat(pathname, &s) != 0)
     121           0 :             WARN_RETURN(StatusFromErrno());
     122             : #endif
     123             : 
     124        4660 :         if(files && S_ISREG(s.st_mode))
     125        2409 :             files->push_back(CFileInfo(name, s.st_size, s.st_mtime));
     126        2251 :         else if(subdirectoryNames && S_ISDIR(s.st_mode) && name != L"." && name != L"..")
     127         717 :             subdirectoryNames->push_back(name);
     128        4660 :     }
     129             : }
     130             : 
     131             : 
     132         308 : Status CreateDirectories(const OsPath& path, mode_t mode, bool breakpoint)
     133             : {
     134         308 :     if(path.empty())
     135           0 :         return INFO::OK;
     136             : 
     137             :     struct stat s;
     138         308 :     if(wstat(path, &s) == 0)
     139             :     {
     140         102 :         if(!S_ISDIR(s.st_mode)) // encountered a file
     141           0 :             WARN_RETURN(ERR::FAIL);
     142         102 :         return INFO::OK;
     143             :     }
     144             : 
     145             :     // If we were passed a path ending with '/', strip the '/' now so that
     146             :     // we can consistently use Parent to find parent directory names
     147         206 :     if(path.IsDirectory())
     148          83 :         return CreateDirectories(path.Parent(), mode, breakpoint);
     149             : 
     150         123 :     RETURN_STATUS_IF_ERR(CreateDirectories(path.Parent(), mode));
     151             : 
     152         123 :     errno = 0;
     153         123 :     if(wmkdir(path, mode) != 0)
     154             :     {
     155           0 :         debug_printf("CreateDirectories: failed to mkdir %s (mode %d)\n", path.string8().c_str(), mode);
     156           0 :         if (breakpoint)
     157           0 :             WARN_RETURN(StatusFromErrno());
     158             :         else
     159           0 :             return StatusFromErrno();
     160             :     }
     161             : 
     162         123 :     return INFO::OK;
     163             : }
     164             : 
     165             : 
     166         243 : Status DeleteDirectory(const OsPath& path)
     167             : {
     168             :     // note: we have to recursively empty the directory before it can
     169             :     // be deleted (required by Windows and POSIX rmdir()).
     170             : 
     171         486 :     CFileInfos files; DirectoryNames subdirectoryNames;
     172         243 :     RETURN_STATUS_IF_ERR(GetDirectoryEntries(path, &files, &subdirectoryNames));
     173             : 
     174             :     // delete files
     175         333 :     for(size_t i = 0; i < files.size(); i++)
     176             :     {
     177         188 :         const OsPath pathname = path / files[i].Name();
     178          94 :         errno = 0;
     179          94 :         if(wunlink(pathname) != 0)
     180           0 :             WARN_RETURN(StatusFromErrno());
     181             :     }
     182             : 
     183             :     // recurse over subdirectoryNames
     184         388 :     for(size_t i = 0; i < subdirectoryNames.size(); i++)
     185         149 :         RETURN_STATUS_IF_ERR(DeleteDirectory(path / subdirectoryNames[i]));
     186             : 
     187         239 :     errno = 0;
     188         239 :     if(wrmdir(path) != 0)
     189           0 :         WARN_RETURN(StatusFromErrno());
     190             : 
     191         239 :     return INFO::OK;
     192             : }
     193             : 
     194           0 : Status RenameFile(const OsPath& path, const OsPath& newPath)
     195             : {
     196           0 :     if (path.empty())
     197           0 :         return INFO::OK;
     198             : 
     199             :     try
     200             :     {
     201           0 :         fs::rename(fs::path(path.string()), fs::path(newPath.string()));
     202             :     }
     203           0 :     catch (fs::filesystem_error& err)
     204             :     {
     205           0 :         debug_printf("RenameFile: failed to rename %s to %s.\n%s\n", path.string8().c_str(), path.string8().c_str(), err.what());
     206           0 :         return ERR::EXCEPTION;
     207             :     }
     208             : 
     209           0 :     return INFO::OK;
     210             : 
     211             : }
     212             : 
     213           0 : Status CopyFile(const OsPath& path, const OsPath& newPath, bool override_if_exists/* = false*/)
     214             : {
     215           0 :     if(path.empty())
     216           0 :         return INFO::OK;
     217             : 
     218             :     try
     219             :     {
     220           0 :         if(override_if_exists)
     221           0 :             fs::copy_file(fs::path(path.string()), fs::path(newPath.string()), boost::filesystem::copy_option::overwrite_if_exists);
     222             :         else
     223           0 :             fs::copy_file(fs::path(path.string()), fs::path(newPath.string()));
     224             :     }
     225           0 :     catch(fs::filesystem_error& err)
     226             :     {
     227           0 :         debug_printf("CopyFile: failed to copy %s to %s.\n%s\n", path.string8().c_str(), path.string8().c_str(), err.what());
     228           0 :         return ERR::EXCEPTION;
     229             :     }
     230             : 
     231           0 :     return INFO::OK;
     232           3 : }

Generated by: LCOV version 1.13