• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xlnt-community / xlnt / aa719a12-330c-47b2-9460-5b2d61336ff6

03 Feb 2025 05:39PM UTC coverage: 81.978% (-1.5%) from 83.482%
aa719a12-330c-47b2-9460-5b2d61336ff6

push

circleci

web-flow
Use C++23 for generating coverage report. (#57)

Using C++23 for coverage reporting, prepares the library for coverage
testing of future post-C++11 features.

This PR makes as few changes as possible to ensure all coverage changes
are due to the changed C++/compiler version used for generating the
coverage report.

Temporarily disable warnings as errors for coverage reporting until
better C++20/23 support is added in PR #55.

11395 of 13900 relevant lines covered (81.98%)

1202684.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.09
./source/utils/path.cpp
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

43
namespace {
44

45
#ifdef WIN32
46

47
char system_separator()
48
{
49
    return '\\';
50
}
51

52
bool is_drive_letter(char letter)
53
{
54
    return letter >= 'A' && letter <= 'Z';
55
}
56

57
bool is_root(const std::string &part)
58
{
59
    if (part.size() == 1 && part[0] == '/') return true;
60
    if (part.size() != 3) return false;
61

62
    return is_drive_letter(part[0]) && part[1] == ':' && (part[2] == '\\' || part[2] == '/');
63
}
64

65
bool is_absolute(const std::string &part)
66
{
67
    if (!part.empty() && part[0] == '/') return true;
68
    if (part.size() < 3) return false;
69

70
    return is_root(part.substr(0, 3));
71
}
72

73
#else
74

75
char system_separator()
286,474✔
76
{
77
    return '/';
286,474✔
78
}
79

80
bool is_root(const std::string &part)
2,973✔
81
{
82
    return part == "/";
2,973✔
83
}
84

85
bool is_absolute(const std::string &part)
5,382✔
86
{
87
    return !part.empty() && part[0] == '/';
5,382✔
88
}
89

90
#endif
91

92
std::vector<std::string> split_path(const std::string &path, char delim)
96,158✔
93
{
94
    std::vector<std::string> split;
96,158✔
95
    std::string::size_type previous_index = 0;
96,158✔
96
    auto separator_index = path.find(delim);
96,158✔
97

98
    while (separator_index != std::string::npos)
195,021✔
99
    {
100
        auto part = path.substr(previous_index, separator_index - previous_index);
98,863✔
101
        split.push_back(part);
98,863✔
102

103
        previous_index = separator_index + 1;
98,863✔
104
        separator_index = path.find(delim, previous_index);
98,863✔
105
    }
98,863✔
106

107
    // Don't add trailing slash
108
    if (previous_index < path.size())
96,158✔
109
    {
110
        split.push_back(path.substr(previous_index));
95,026✔
111
    }
112

113
    return split;
96,158✔
114
}
×
115

116
bool file_exists(const std::string &path)
4✔
117
{
118
    struct stat info;
119
    return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFREG);
4✔
120
}
121

122
bool directory_exists(const std::string path)
3✔
123
{
124
    struct stat info;
125
    return stat(path.c_str(), &info) == 0 && (info.st_mode & S_IFDIR);
3✔
126
}
127

128
} // namespace
129

130
namespace xlnt {
131

132
char path::system_separator()
286,474✔
133
{
134
    return ::system_separator();
286,474✔
135
}
136

137
path::path()
97,215✔
138
{
139
}
97,215✔
140

141
path::path(const std::string &path_string)
297,186✔
142
{
143
    std::remove_copy(path_string.begin(), path_string.end(), std::back_inserter(internal_), '\"');
297,186✔
144
}
297,186✔
145

146
path::path(const std::string &path_string, char sep)
×
147
    : internal_(path_string)
×
148
{
149
    char curr_sep = guess_separator();
×
150
    if (curr_sep != sep)
×
151
    {
152
        for (char &c : internal_) // simple find and replace
×
153
        {
154
            if (c == curr_sep)
×
155
            {
156
                c = sep;
×
157
            }
158
        }
159
    }
160
}
×
161

162
// general attributes
163

164
bool path::is_relative() const
2,725✔
165
{
166
    return !is_absolute();
2,725✔
167
}
168

169
bool path::is_absolute() const
5,382✔
170
{
171
    return ::is_absolute(internal_);
5,382✔
172
}
173

174
bool path::is_root() const
2,973✔
175
{
176
    return ::is_root(internal_);
2,973✔
177
}
178

179
path path::parent() const
2,973✔
180
{
181
    if (is_root()) return *this;
2,973✔
182

183
    auto split_path = split();
2,775✔
184

185
    split_path.pop_back();
2,775✔
186

187
    if (split_path.empty())
2,775✔
188
    {
189
        return path("");
328✔
190
    }
191

192
    path result;
2,611✔
193

194
    for (const auto &component : split_path)
5,715✔
195
    {
196
        result = result.append(component);
3,104✔
197
    }
198

199
    return result;
2,611✔
200
}
2,775✔
201

202
std::string path::filename() const
1,714✔
203
{
204
    auto split_path = split();
1,714✔
205
    return split_path.empty() ? "" : split_path.back();
3,428✔
206
}
1,714✔
207

208
std::string path::extension() const
160✔
209
{
210
    auto base = filename();
160✔
211
    auto last_dot = base.find_last_of('.');
160✔
212

213
    return last_dot == std::string::npos ? "" : base.substr(last_dot + 1);
320✔
214
}
160✔
215

216
std::pair<std::string, std::string> path::split_extension() const
8✔
217
{
218
    auto base = filename();
8✔
219
    auto last_dot = base.find_last_of('.');
8✔
220

221
    return {base.substr(0, last_dot), base.substr(last_dot + 1)};
16✔
222
}
8✔
223

224
// conversion
225

226
const std::string &path::string() const
336,608✔
227
{
228
    return internal_;
336,608✔
229
}
230

231
#ifdef _MSC_VER
232
std::wstring path::wstring() const
233
{
234
    const std::string &path_str = string();
235
    const int allocated_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, path_str.c_str(), static_cast<int>(path_str.length()), nullptr, 0);
236

237
    if (allocated_size > 0)
238
    {
239
        std::wstring path_converted(allocated_size, L'\0');
240
        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);
241
        assert(allocated_size == actual_size); // unless a serious error happened, this MUST always be true!
242
        return path_converted;
243
    }
244
    else
245
    {
246
        return {};
247
    }
248
}
249
#endif
250

251
std::vector<std::string> path::split() const
96,158✔
252
{
253
    return split_path(internal_, guess_separator());
96,158✔
254
}
255

256
path path::resolve(const path &base_path) const
1,568✔
257
{
258
    if (is_absolute())
1,568✔
259
    {
260
        return *this;
627✔
261
    }
262

263
    path copy(base_path.internal_);
941✔
264

265
    for (const auto &part : split())
2,906✔
266
    {
267
        if (part == "..")
1,965✔
268
        {
269
            copy = copy.parent();
2✔
270
            continue;
2✔
271
        }
272

273
        copy = copy.append(part);
1,963✔
274
    }
941✔
275

276
    return copy;
941✔
277
}
941✔
278

279
// filesystem attributes
280

281
bool path::exists() const
4✔
282
{
283
    return is_file() || is_directory();
4✔
284
}
285

286
bool path::is_directory() const
3✔
287
{
288
    return directory_exists(string());
3✔
289
}
290

291
bool path::is_file() const
4✔
292
{
293
    return file_exists(string());
4✔
294
}
295

296
// filesystem
297

298
std::string path::read_contents() const
×
299
{
300
    std::ifstream f(string());
×
301
    std::ostringstream ss;
×
302
    ss << f.rdbuf();
×
303

304
    return ss.str();
×
305
}
×
306

307
// append
308

309
path path::append(const std::string &to_append) const
185,950✔
310
{
311
    path copy(internal_);
185,950✔
312

313
    if (!internal_.empty() && internal_.back() != guess_separator())
185,950✔
314
    {
315
        copy.internal_.push_back(guess_separator());
94,633✔
316
    }
317

318
    copy.internal_.append(to_append);
185,950✔
319

320
    return copy;
185,950✔
321
}
×
322

323
path path::append(const path &to_append) const
44,780✔
324
{
325
    path copy(internal_);
44,780✔
326

327
    for (const auto &component : to_append.split())
133,230✔
328
    {
329
        copy = copy.append(component);
88,450✔
330
    }
44,780✔
331

332
    return copy;
44,780✔
333
}
×
334

335
char path::guess_separator() const
286,474✔
336
{
337
    if (system_separator() == '/' || internal_.empty() || internal_.front() == '/') return '/';
286,474✔
338
    if (is_absolute()) return internal_.at(2);
×
339
    return internal_.find('\\') != std::string::npos ? '\\' : '/';
×
340
}
341

342
path path::relative_to(const path &base_path) const
2,725✔
343
{
344
    if (is_relative()) return *this;
2,725✔
345

346
    auto base_split = base_path.split();
1,487✔
347
    auto this_split = split();
1,487✔
348
    auto index = std::size_t(0);
1,487✔
349

350
    while (index < base_split.size() && index < this_split.size() && base_split[index] == this_split[index])
3,560✔
351
    {
352
        index++;
2,073✔
353
    }
354

355
    auto result = path();
1,487✔
356

357
    for (auto i = index; i < this_split.size(); i++)
4,144✔
358
    {
359
        result = result.append(this_split[i]);
2,657✔
360
    }
361

362
    return result;
1,487✔
363
}
1,487✔
364

365
bool path::operator==(const path &other) const
146,040✔
366
{
367
    return internal_ == other.internal_;
146,040✔
368
}
369

370
bool path::operator!=(const path &other) const
×
371
{
372
    return !operator==(other);
×
373
}
374

375
} // namespace xlnt
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc