differential code coverage report with master
Current view: top level - source/utils - path.cpp (source / functions) Coverage Total Hit UBC CBC
Current: coverage.info Lines: 82.9 % 140 116 24 116
Current Date: 2025-12-15 23:01:28 Functions: 87.9 % 33 29 4 29
Baseline: coverage_master.info Branches: 70.2 % 131 92 78 184
Baseline Date: 2025-12-15 23:01:27

             Branch data    TLA  Line data    Source code
       1                 :                : // Copyright (c) 2014-2022 Thomas Fussell
       2                 :                : // Copyright (c) 2024-2025 xlnt-community
       3                 :                : //
       4                 :                : // Permission is hereby granted, free of charge, to any person obtaining a copy
       5                 :                : // of this software and associated documentation files (the "Software"), to deal
       6                 :                : // in the Software without restriction, including without limitation the rights
       7                 :                : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8                 :                : // copies of the Software, and to permit persons to whom the Software is
       9                 :                : // furnished to do so, subject to the following conditions:
      10                 :                : //
      11                 :                : // The above copyright notice and this permission notice shall be included in
      12                 :                : // all copies or substantial portions of the Software.
      13                 :                : //
      14                 :                : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15                 :                : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16                 :                : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17                 :                : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18                 :                : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19                 :                : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20                 :                : // THE SOFTWARE
      21                 :                : //
      22                 :                : // @license: http://www.opensource.org/licenses/mit-license.php
      23                 :                : // @author: see AUTHORS file
      24                 :                : 
      25                 :                : #include <algorithm>
      26                 :                : #include <cassert>
      27                 :                : #include <fstream>
      28                 :                : #include <iterator>
      29                 :                : #include <sstream>
      30                 :                : #include <sys/stat.h>
      31                 :                : 
      32                 :                : #ifdef __APPLE__
      33                 :                : #include <mach-o/dyld.h>
      34                 :                : #elif defined(__linux)
      35                 :                : #include <linux/limits.h>
      36                 :                : #include <sys/types.h>
      37                 :                : #include <unistd.h>
      38                 :                : #endif
      39                 :                : 
      40                 :                : #include <xlnt/utils/path.hpp>
      41                 :                : #include <detail/external/include_windows.hpp>
      42                 :                : #include <detail/utils/string_helpers.hpp>
      43                 :                : 
      44                 :                : namespace {
      45                 :                : 
      46                 :                : #ifdef WIN32
      47                 :                : 
      48                 :                : char system_separator()
      49                 :                : {
      50                 :                :     return '\\';
      51                 :                : }
      52                 :                : 
      53                 :                : bool is_drive_letter(char letter)
      54                 :                : {
      55                 :                :     return letter >= 'A' && letter <= 'Z';
      56                 :                : }
      57                 :                : 
      58                 :                : bool is_root(const std::string &part)
      59                 :                : {
      60                 :                :     if (part.size() == 1 && part[0] == '/') return true;
      61                 :                :     if (part.size() != 3) return false;
      62                 :                : 
      63                 :                :     return is_drive_letter(part[0]) && part[1] == ':' && (part[2] == '\\' || part[2] == '/');
      64                 :                : }
      65                 :                : 
      66                 :                : bool is_absolute(const std::string &part)
      67                 :                : {
      68                 :                :     if (!part.empty() && part[0] == '/') return true;
      69                 :                :     if (part.size() < 3) return false;
      70                 :                : 
      71                 :                :     return is_root(part.substr(0, 3));
      72                 :                : }
      73                 :                : 
      74                 :                : #else
      75                 :                : 
      76                 :CBC       39674 : char system_separator()
      77                 :                : {
      78                 :          39674 :     return '/';
      79                 :                : }
      80                 :                : 
      81                 :           2315 : bool is_root(const std::string &part)
      82                 :                : {
      83                 :           2315 :     return part == "/";
      84                 :                : }
      85                 :                : 
      86                 :           4647 : bool is_absolute(const std::string &part)
      87                 :                : {
      88   [ +  +  +  + ]:           4647 :     return !part.empty() && part[0] == '/';
      89                 :                : }
      90                 :                : 
      91                 :                : #endif
      92                 :                : 
      93                 :          13824 : std::vector<std::string> split_path(const std::string &path, char delim)
      94                 :                : {
      95                 :          13824 :     std::vector<std::string> split;
      96                 :          13824 :     std::string::size_type previous_index = 0;
      97                 :          13824 :     auto separator_index = path.find(delim);
      98                 :                : 
      99         [ +  + ]:          30332 :     while (separator_index != std::string::npos)
     100                 :                :     {
     101            [ + ]:          16508 :         auto part = path.substr(previous_index, separator_index - previous_index);
     102            [ + ]:          16508 :         split.push_back(part);
     103                 :                : 
     104                 :          16508 :         previous_index = separator_index + 1;
     105                 :          16508 :         separator_index = path.find(delim, previous_index);
     106                 :          16508 :     }
     107                 :                : 
     108                 :                :     // Don't add trailing slash
     109         [ +  + ]:          13824 :     if (previous_index < path.size())
     110                 :                :     {
     111         [ +  + ]:          12773 :         split.push_back(path.substr(previous_index));
     112                 :                :     }
     113                 :                : 
     114                 :          13824 :     return split;
     115                 :UBC           0 : }
     116                 :                : 
     117                 :                : #ifdef _MSC_VER
     118                 :                : bool file_exists(const std::wstring &path)
     119                 :                : {
     120                 :                :     struct _stat info;
     121                 :                :     return _wstat(path.c_str(), &info) == 0 && (info.st_mode & S_IFREG);
     122                 :                : }
     123                 :                : 
     124                 :                : bool directory_exists(const std::wstring &path)
     125                 :                : {
     126                 :                :     struct _stat info;
     127                 :                :     return _wstat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
     128                 :                : }
     129                 :                : #else
     130                 :CBC           5 : bool file_exists(const std::string &path)
     131                 :                : {
     132                 :                :     struct stat info;
     133   [ +  +  +  - ]:              5 :     return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFREG);
     134                 :                : }
     135                 :                : 
     136                 :              2 : bool directory_exists(const std::string &path)
     137                 :                : {
     138                 :                :     struct stat info;
     139   [ -  +  -  - ]:              2 :     return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
     140                 :                : }
     141                 :                : #endif
     142                 :                : 
     143                 :                : } // namespace
     144                 :                : 
     145                 :                : namespace xlnt {
     146                 :                : 
     147                 :          39674 : char path::system_separator()
     148                 :                : {
     149                 :          39674 :     return ::system_separator();
     150                 :                : }
     151                 :                : 
     152                 :          15229 : path::path()
     153                 :                : {
     154                 :          15229 : }
     155                 :                : 
     156                 :          49956 : path::path(const std::string &path_string)
     157                 :                : {
     158         [ +  + ]:          49956 :     std::remove_copy(path_string.begin(), path_string.end(), std::back_inserter(internal_), '\"');
     159                 :          49956 : }
     160                 :                : 
     161                 :UBC           0 : path::path(const std::string &path_string, char sep)
     162                 :              0 :     : internal_(path_string)
     163                 :                : {
     164            [ # ]:              0 :     char curr_sep = guess_separator();
     165         [ #  # ]:              0 :     if (curr_sep != sep)
     166                 :                :     {
     167         [ #  # ]:              0 :         for (char &c : internal_) // simple find and replace
     168                 :                :         {
     169         [ #  # ]:              0 :             if (c == curr_sep)
     170                 :                :             {
     171                 :              0 :                 c = sep;
     172                 :                :             }
     173                 :                :         }
     174                 :                :     }
     175                 :              0 : }
     176                 :                : 
     177                 :                : #if XLNT_HAS_FEATURE(U8_STRING_VIEW)
     178                 :CBC           7 : path::path(std::u8string_view path_string)
     179         [ +  + ]:              7 :     : path(detail::to_string_copy(path_string))
     180                 :                : {
     181                 :                : 
     182                 :              7 : }
     183                 :                : 
     184                 :UBC           0 : path::path(std::u8string_view path_string, char sep)
     185         [ #  # ]:              0 :     : path(detail::to_string_copy(path_string), sep)
     186                 :                : {
     187                 :                : 
     188                 :              0 : }
     189                 :                : #endif
     190                 :                : 
     191                 :                : 
     192                 :                : // general attributes
     193                 :                : 
     194                 :CBC        2530 : bool path::is_relative() const
     195                 :                : {
     196                 :           2530 :     return !is_absolute();
     197                 :                : }
     198                 :                : 
     199                 :           4647 : bool path::is_absolute() const
     200                 :                : {
     201                 :           4647 :     return ::is_absolute(internal_);
     202                 :                : }
     203                 :                : 
     204                 :           2315 : bool path::is_root() const
     205                 :                : {
     206                 :           2315 :     return ::is_root(internal_);
     207                 :                : }
     208                 :                : 
     209                 :           2315 : path path::parent() const
     210                 :                : {
     211   [ +  +  +  + ]:           2315 :     if (is_root()) return *this;
     212                 :                : 
     213            [ + ]:           2174 :     auto split_path = split();
     214                 :                : 
     215                 :           2174 :     split_path.pop_back();
     216                 :                : 
     217         [ +  + ]:           2174 :     if (split_path.empty())
     218                 :                :     {
     219         [ +  + ]:            306 :         return path("");
     220                 :                :     }
     221                 :                : 
     222                 :           2021 :     path result;
     223                 :                : 
     224         [ +  + ]:           4481 :     for (const auto &component : split_path)
     225                 :                :     {
     226            [ + ]:           2460 :         result = result.append(component);
     227                 :                :     }
     228                 :                : 
     229                 :           2021 :     return result;
     230                 :           2174 : }
     231                 :                : 
     232                 :           1471 : std::string path::filename() const
     233                 :                : {
     234            [ + ]:           1471 :     auto split_path = split();
     235   [ -  +  -  +  :           2942 :     return split_path.empty() ? "" : split_path.back();
             -  +  -  - ]
     236                 :           1471 : }
     237                 :                : 
     238                 :            160 : std::string path::extension() const
     239                 :                : {
     240            [ + ]:            160 :     auto base = filename();
     241                 :            160 :     auto last_dot = base.find_last_of('.');
     242                 :                : 
     243   [ -  +  -  +  :            320 :     return last_dot == std::string::npos ? "" : base.substr(last_dot + 1);
             -  +  -  - ]
     244                 :            160 : }
     245                 :                : 
     246                 :              8 : std::pair<std::string, std::string> path::split_extension() const
     247                 :                : {
     248            [ + ]:              8 :     auto base = filename();
     249                 :              8 :     auto last_dot = base.find_last_of('.');
     250                 :                : 
     251         [ +  + ]:             16 :     return {base.substr(0, last_dot), base.substr(last_dot + 1)};
     252                 :              8 : }
     253                 :                : 
     254                 :                : // conversion
     255                 :                : 
     256                 :          48644 : const std::string &path::string() const
     257                 :                : {
     258                 :          48644 :     return internal_;
     259                 :                : }
     260                 :                : 
     261                 :                : #ifdef _MSC_VER
     262                 :                : std::wstring path::wstring() const
     263                 :                : {
     264                 :                :     const std::string &path_str = string();
     265                 :                :     const int allocated_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path_str.c_str(), static_cast<int>(path_str.length()), nullptr, 0);
     266                 :                : 
     267                 :                :     if (allocated_size > 0)
     268                 :                :     {
     269                 :                :         std::wstring path_converted(allocated_size, L'\0');
     270                 :                :         const int actual_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path_str.c_str(), static_cast<int>(path_str.length()), &path_converted.at(0), allocated_size);
     271                 :                :         assert(allocated_size == actual_size); // unless a serious error happened, this MUST always be true!
     272                 :                :         return path_converted;
     273                 :                :     }
     274                 :                :     else
     275                 :                :     {
     276                 :                :         return {};
     277                 :                :     }
     278                 :                : }
     279                 :                : #endif
     280                 :                : 
     281                 :          13824 : std::vector<std::string> path::split() const
     282                 :                : {
     283                 :          13824 :     return split_path(internal_, guess_separator());
     284                 :                : }
     285                 :                : 
     286                 :           1222 : path path::resolve(const path &base_path) const
     287                 :                : {
     288         [ +  + ]:           1222 :     if (is_absolute())
     289                 :                :     {
     290            [ + ]:            313 :         return *this;
     291                 :                :     }
     292                 :                : 
     293            [ + ]:            909 :     path copy(base_path.internal_);
     294                 :                : 
     295      [ +  +  + ]:           2810 :     for (const auto &part : split())
     296                 :                :     {
     297      [ +  +  + ]:           1901 :         if (part == "..")
     298                 :                :         {
     299            [ + ]:              2 :             copy = copy.parent();
     300                 :              2 :             continue;
     301                 :                :         }
     302                 :                : 
     303            [ + ]:           1899 :         copy = copy.append(part);
     304                 :            909 :     }
     305                 :                : 
     306                 :            909 :     return copy;
     307                 :            909 : }
     308                 :                : 
     309                 :                : // filesystem attributes
     310                 :                : 
     311                 :              5 : bool path::exists() const
     312                 :                : {
     313   [ +  +  -  + ]:              5 :     return is_file() || is_directory();
     314                 :                : }
     315                 :                : 
     316                 :              2 : bool path::is_directory() const
     317                 :                : {
     318                 :                : #ifdef _MSC_VER
     319                 :                :     return directory_exists(wstring());
     320                 :                : #else
     321                 :              2 :     return directory_exists(string());
     322                 :                : #endif
     323                 :                : }
     324                 :                : 
     325                 :              5 : bool path::is_file() const
     326                 :                : {
     327                 :                : #ifdef _MSC_VER
     328                 :                :     return file_exists(wstring());
     329                 :                : #else
     330                 :              5 :     return file_exists(string());
     331                 :                : #endif
     332                 :                : }
     333                 :                : 
     334                 :                : // filesystem
     335                 :                : 
     336                 :UBC           0 : std::string path::read_contents() const
     337                 :                : {
     338                 :                : #ifdef _MSC_VER
     339                 :                :     std::ifstream f(wstring());
     340                 :                : #else
     341            [ # ]:              0 :     std::ifstream f(string());
     342                 :                : #endif
     343            [ # ]:              0 :     std::ostringstream ss;
     344            [ # ]:              0 :     ss << f.rdbuf();
     345                 :                : 
     346            [ # ]:              0 :     return ss.str();
     347                 :              0 : }
     348                 :                : 
     349                 :                : // append
     350                 :                : 
     351                 :CBC       22084 : path path::append(const std::string &to_append) const
     352                 :                : {
     353                 :          22084 :     path copy(internal_);
     354                 :                : 
     355   [ +  +  +  +  :          22084 :     if (!internal_.empty() && internal_.back() != guess_separator())
                +  +  + ]
     356                 :                :     {
     357         [ +  + ]:          12423 :         copy.internal_.push_back(guess_separator());
     358                 :                :     }
     359                 :                : 
     360            [ + ]:          22084 :     copy.internal_.append(to_append);
     361                 :                : 
     362                 :          22084 :     return copy;
     363                 :UBC           0 : }
     364                 :                : 
     365                 :                : #if XLNT_HAS_FEATURE(U8_STRING_VIEW)
     366                 :CBC           1 : path path::append(std::u8string_view to_append) const
     367                 :                : {
     368         [ +  + ]:              1 :     return append(detail::to_string_copy(to_append));
     369                 :                : }
     370                 :                : #endif
     371                 :                : 
     372                 :           3886 : path path::append(const path &to_append) const
     373                 :                : {
     374                 :           3886 :     path copy(internal_);
     375                 :                : 
     376      [ +  +  + ]:          10794 :     for (const auto &component : to_append.split())
     377                 :                :     {
     378            [ + ]:           6908 :         copy = copy.append(component);
     379                 :           3886 :     }
     380                 :                : 
     381                 :           3886 :     return copy;
     382                 :UBC           0 : }
     383                 :                : 
     384                 :CBC       39674 : char path::guess_separator() const
     385                 :                : {
     386   [ -  +  -  -  :          39674 :     if (system_separator() == '/' || internal_.empty() || internal_.front() == '/') return '/';
             -  -  +  - ]
     387         [ #  # ]:UBC           0 :     if (is_absolute()) return internal_.at(2);
     388         [ #  # ]:              0 :     return internal_.find('\\') != std::string::npos ? '\\' : '/';
     389                 :                : }
     390                 :                : 
     391                 :CBC        2530 : path path::relative_to(const path &base_path) const
     392                 :                : {
     393   [ +  +  +  + ]:           2530 :     if (is_relative()) return *this;
     394                 :                : 
     395            [ + ]:           1433 :     auto base_split = base_path.split();
     396            [ + ]:           1433 :     auto this_split = split();
     397                 :           1433 :     auto index = std::size_t(0);
     398                 :                : 
     399   [ +  +  +  -  :           3434 :     while (index < base_split.size() && index < this_split.size() && base_split[index] == this_split[index])
             +  +  +  + ]
     400                 :                :     {
     401                 :           2001 :         index++;
     402                 :                :     }
     403                 :                : 
     404                 :           1433 :     auto result = path();
     405                 :                : 
     406         [ +  + ]:           3993 :     for (auto i = index; i < this_split.size(); i++)
     407                 :                :     {
     408            [ + ]:           2560 :         result = result.append(this_split[i]);
     409                 :                :     }
     410                 :                : 
     411                 :           1433 :     return result;
     412                 :           1433 : }
     413                 :                : 
     414                 :          22787 : bool path::operator==(const path &other) const
     415                 :                : {
     416                 :          22787 :     return internal_ == other.internal_;
     417                 :                : }
     418                 :                : 
     419                 :UBC           0 : bool path::operator!=(const path &other) const
     420                 :                : {
     421                 :              0 :     return !operator==(other);
     422                 :                : }
     423                 :                : 
     424                 :                : } // namespace xlnt
        

Generated by: LCOV version 2.3.1-beta