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

xlnt-community / xlnt / 1d4ee6f6-21a4-4379-9731-a97ba8c57175

25 Jan 2026 09:50AM UTC coverage: 83.928% (+1.1%) from 82.793%
1d4ee6f6-21a4-4379-9731-a97ba8c57175

Pull #87

circleci

doomlaur
Improve docuentation on exceptions thrown for invalid worksheet titles
Pull Request #87: Improve documentation when exceptions are thrown (fixes #81)

15263 of 19954 branches covered (76.49%)

722 of 901 new or added lines in 37 files covered. (80.13%)

15 existing lines in 5 files now uncovered.

12491 of 14883 relevant lines covered (83.93%)

12216.5 hits per line

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

90.3
./source/detail/serialization/zstream.cpp
1
/*
2
PARTIO SOFTWARE
3
Copyright 2010 Disney Enterprises, Inc. All rights reserved
4

5
Redistribution and use in source and binary forms, with or without
6
modification, are permitted provided that the following conditions are
7
met:
8

9
* Redistributions of source code must retain the above copyright
10
notice, this list of conditions and the following disclaimer.
11

12
* Redistributions in binary form must reproduce the above copyright
13
notice, this list of conditions and the following disclaimer in
14
the documentation and/or other materials provided with the
15
distribution.
16

17
* The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
18
Studios" or the names of its contributors may NOT be used to
19
endorse or promote products derived from this software without
20
specific prior written permission from Walt Disney Pictures.
21

22
Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25
FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26
IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34
*/
35

36
#include <algorithm>
37
#include <array>
38
#include <cassert>
39
#include <cstring>
40
#include <iostream>
41
#include <iterator> // for std::back_inserter
42
#include <string>
43
#include <miniz.h>
44

45
#include <xlnt/utils/exceptions.hpp>
46
#include <detail/serialization/vector_streambuf.hpp>
47
#include <detail/serialization/zstream.hpp>
48

49
// NOTE: the OOXML specification (ECMA-376) explicitly uses the following ZIP specification:
50
// .ZIP File Format Specification from PKWARE, Inc., version 6.2.0 (2004), as specified in
51
// http://www.pkware.com/documents/APPNOTE/APPNOTE_6.2.0.txt
52
namespace {
53

54
template <class T>
55
T read_int(std::istream &stream)
39,266✔
56
{
57
    T value;
58
    stream.read(reinterpret_cast<char *>(&value), sizeof(T));
39,266✔
59

60
    return value;
39,266✔
61
}
62

63
template <class T>
64
void write_int(std::ostream &stream, T value)
48,441✔
65
{
66
    stream.write(reinterpret_cast<char *>(&value), sizeof(T));
48,441✔
67
}
48,441✔
68

69
xlnt::detail::zheader read_header(std::istream &istream, const bool global)
2,746✔
70
{
71
    xlnt::detail::zheader header;
2,746✔
72

73
    auto sig = read_int<std::uint32_t>(istream);
2,746✔
74

75
    // read and check for local/global magic
76
    if (global)
2,746✔
77
    {
78
        if (sig != 0x02014b50)
1,370!
79
        {
NEW
80
            throw xlnt::invalid_file("missing global header signature (signature " + std::to_string(sig) + ")");
×
81
        }
82

83
        header.version = read_int<std::uint16_t>(istream);
1,370✔
84
    }
85
    else if (sig != 0x04034b50)
1,376!
86
    {
NEW
87
        throw xlnt::invalid_file("missing local header signature (signature " + std::to_string(sig) + ")");
×
88
    }
89

90
    // Read rest of header
91
    header.version = read_int<std::uint16_t>(istream);
2,746✔
92
    header.flags = read_int<std::uint16_t>(istream);
2,746✔
93
    header.compression_type = read_int<std::uint16_t>(istream);
2,746✔
94
    header.stamp_date = read_int<std::uint16_t>(istream);
2,746✔
95
    header.stamp_time = read_int<std::uint16_t>(istream);
2,746✔
96
    header.crc = read_int<std::uint32_t>(istream);
2,746✔
97
    header.compressed_size = read_int<std::uint32_t>(istream);
2,746✔
98
    header.uncompressed_size = read_int<std::uint32_t>(istream);
2,746✔
99

100
    auto filename_length = read_int<std::uint16_t>(istream);
2,746✔
101
    auto extra_length = read_int<std::uint16_t>(istream);
2,746✔
102

103
    std::uint16_t comment_length = 0;
2,746✔
104

105
    if (global)
2,746✔
106
    {
107
        comment_length = read_int<std::uint16_t>(istream);
1,370✔
108
        /*std::uint16_t disk_number_start = */ read_int<std::uint16_t>(istream);
1,370✔
109
        /*std::uint16_t int_file_attrib = */ read_int<std::uint16_t>(istream);
1,370✔
110
        /*std::uint32_t ext_file_attrib = */ read_int<std::uint32_t>(istream);
1,370✔
111
        header.header_offset = read_int<std::uint32_t>(istream);
1,370✔
112
    }
113

114
    header.filename.resize(filename_length, '\0');
2,746✔
115
    istream.read(&header.filename[0], filename_length);
2,746✔
116

117
    header.extra.resize(extra_length, 0);
2,746✔
118
    istream.read(reinterpret_cast<char *>(header.extra.data()), extra_length);
2,746✔
119

120
    if (global)
2,746✔
121
    {
122
        header.comment.resize(comment_length, '\0');
1,370✔
123
        istream.read(&header.comment[0], comment_length);
1,370✔
124
    }
125

126
    return header;
2,746✔
127
}
×
128

129
void write_header(const xlnt::detail::zheader &header, std::ostream &ostream, const bool global)
1,506✔
130
{
131
    if (global)
1,506✔
132
    {
133
        write_int(ostream, static_cast<std::uint32_t>(0x02014b50)); // header sig
502✔
134
        write_int(ostream, static_cast<std::uint16_t>(20)); // version made by
502✔
135
    }
136
    else
137
    {
138
        write_int(ostream, static_cast<std::uint32_t>(0x04034b50));
1,004✔
139
    }
140

141
    write_int(ostream, header.version);
1,506✔
142
    write_int(ostream, header.flags);
1,506✔
143
    write_int(ostream, header.compression_type);
1,506✔
144
    write_int(ostream, header.stamp_date);
1,506✔
145
    write_int(ostream, header.stamp_time);
1,506✔
146
    write_int(ostream, header.crc);
1,506✔
147
    write_int(ostream, header.compressed_size);
1,506✔
148
    write_int(ostream, header.uncompressed_size);
1,506✔
149
    write_int(ostream, static_cast<std::uint16_t>(header.filename.length()));
1,506✔
150
    write_int(ostream, static_cast<std::uint16_t>(0)); // extra lengthx
1,506✔
151

152
    if (global)
1,506✔
153
    {
154
        write_int(ostream, static_cast<std::uint16_t>(0)); // filecomment
502✔
155
        write_int(ostream, static_cast<std::uint16_t>(0)); // disk# start
502✔
156
        write_int(ostream, static_cast<std::uint16_t>(0)); // internal file
502✔
157
        write_int(ostream, static_cast<std::uint32_t>(0)); // ext final
502✔
158
        write_int(ostream, static_cast<std::uint32_t>(header.header_offset)); // rel offset
502✔
159
    }
160

161
    for (auto c : header.filename)
30,009✔
162
    {
163
        write_int(ostream, c);
28,503✔
164
    }
165
}
1,506✔
166

167
} // namespace
168

169
namespace xlnt {
170
namespace detail {
171

172
static const std::size_t buffer_size = 512;
173

174
class zip_streambuf_decompress : public std::streambuf
175
{
176
    std::istream &istream;
177

178
    z_stream strm;
179
    std::array<char, buffer_size> in;
180
    std::array<char, buffer_size> out;
181
    zheader header;
182
    std::size_t total_read;
183
    std::size_t total_uncompressed;
184
    bool valid;
185
    bool compressed_data;
186

187
    static const unsigned short DEFLATE = 8;
188
    static const unsigned short UNCOMPRESSED = 0;
189

190
public:
191
    zip_streambuf_decompress(std::istream &stream, zheader central_header)
1,376✔
192
        : istream(stream), header(central_header), total_read(0), total_uncompressed(0), valid(true)
1,376✔
193
    {
194
        in.fill(0);
1,376✔
195
        out.fill(0);
1,376✔
196

197
        strm.zalloc = nullptr;
1,376✔
198
        strm.zfree = nullptr;
1,376✔
199
        strm.opaque = nullptr;
1,376✔
200
        strm.avail_in = 0;
1,376✔
201
        strm.next_in = nullptr;
1,376✔
202

203
        setg(in.data(), in.data(), in.data());
5,504✔
204
        setp(nullptr, nullptr);
1,376✔
205

206
        // skip the header
207
        read_header(istream, false);
1,376✔
208

209
        if (header.compression_type == DEFLATE)
1,376✔
210
        {
211
            compressed_data = true;
1,352✔
212
        }
213
        else if (header.compression_type == UNCOMPRESSED)
24!
214
        {
215
            compressed_data = false;
24✔
216
        }
217
        else
218
        {
219
            compressed_data = false;
×
NEW
220
            throw xlnt::unsupported("unsupported compression type " + std::to_string(header.compression_type) + ", should be DEFLATE or uncompressed");
×
221
        }
222

223
        // initialize the inflate
224
        if (compressed_data && valid)
1,376!
225
        {
226
#pragma clang diagnostic push
227
#pragma clang diagnostic ignored "-Wold-style-cast"
228
            int result = inflateInit2(&strm, -MAX_WBITS);
1,352✔
229
#pragma clang diagnostic pop
230

231
            if (result != Z_OK)
1,352!
232
            {
NEW
233
                throw xlnt::invalid_file("couldn't inflate ZIP, possibly corrupted (error code " + std::to_string(result) + ")");
×
234
            }
235
        }
236

237
        header = central_header;
1,376✔
238
    }
1,376✔
239

240
    ~zip_streambuf_decompress() override
2,752✔
241
    {
1,376✔
242
        if (compressed_data && valid)
1,376!
243
        {
244
            inflateEnd(&strm);
1,352✔
245
        }
246
    }
2,752✔
247

248
    int process()
14,426✔
249
    {
250
        if (!valid) return -1;
14,426!
251

252
        if (compressed_data)
14,426✔
253
        {
254
            strm.avail_out = buffer_size - 4;
14,069✔
255
            strm.next_out = reinterpret_cast<Bytef *>(out.data() + 4);
14,069✔
256

257
            while (strm.avail_out != 0)
29,455✔
258
            {
259
                if (strm.avail_in == 0)
17,816✔
260
                {
261
                    // buffer empty, read some more from file
262
                    istream.read(in.data(),
7,425✔
263
                        static_cast<std::streamsize>(std::min(buffer_size, header.compressed_size - total_read)));
7,425✔
264
                    strm.avail_in = static_cast<unsigned int>(istream.gcount());
7,425✔
265
                    total_read += strm.avail_in;
7,425✔
266
                    strm.next_in = reinterpret_cast<Bytef *>(in.data());
14,850✔
267
                }
268

269
                const auto ret = inflate(&strm, Z_NO_FLUSH); // decompress
17,816✔
270

271
                if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
17,816!
272
                {
NEW
273
                    throw xlnt::invalid_file("couldn't inflate ZIP, possibly corrupted (error code " + std::to_string(ret) + ")");
×
274
                }
275

276
                if (ret == Z_STREAM_END) break;
17,816✔
277
            }
278

279
            auto unzip_count = buffer_size - strm.avail_out - 4;
14,069✔
280
            total_uncompressed += unzip_count;
14,069✔
281
            return static_cast<int>(unzip_count);
14,069✔
282
        }
283

284
        // uncompressed, so just read
285
        istream.read(out.data() + 4,
357✔
286
            static_cast<std::streamsize>(std::min(buffer_size - 4, header.uncompressed_size - total_read)));
357✔
287
        auto count = istream.gcount();
357✔
288
        total_read += static_cast<std::size_t>(count);
357✔
289
        return static_cast<int>(count);
357✔
290
    }
291

292
    virtual int underflow() override
14,426✔
293
    {
294
        if (gptr() && (gptr() < egptr()))
14,426!
295
            return traits_type::to_int_type(*gptr()); // if we already have data just use it
×
296
        auto put_back_count = gptr() - eback();
14,426✔
297
        if (put_back_count > 4) put_back_count = 4;
14,426✔
298
        std::memmove(
28,852✔
299
            out.data() + (4 - put_back_count), gptr() - put_back_count, static_cast<std::size_t>(put_back_count));
14,426✔
300
        int num = process();
14,426✔
301
        setg(out.data() + 4 - put_back_count, out.data() + 4, out.data() + 4 + num);
57,704✔
302
        if (num <= 0) return EOF;
14,426✔
303
        return traits_type::to_int_type(*gptr());
13,197✔
304
    }
305

306
    virtual int overflow(int c = EOF) override;
307
};
308

309
int zip_streambuf_decompress::overflow(int)
×
310
{
311
    throw xlnt::exception("writing to read-only buffer");
×
312
}
313

314
class zip_streambuf_compress : public std::streambuf
315
{
316
    std::ostream &ostream; // owned when header==0 (when not part of zip file)
317

318
    z_stream strm;
319
    std::array<char, buffer_size> in;
320
    std::array<char, buffer_size> out;
321

322
    zheader *header;
323
    std::uint32_t uncompressed_size;
324
    std::uint32_t crc;
325

326
    bool valid;
327

328
public:
329
    zip_streambuf_compress(zheader *central_header, std::ostream &stream)
502✔
330
        : ostream(stream), header(central_header), valid(true)
502✔
331
    {
332
        strm.zalloc = nullptr;
502✔
333
        strm.zfree = nullptr;
502✔
334
        strm.opaque = nullptr;
502✔
335

336
#pragma clang diagnostic push
337
#pragma clang diagnostic ignored "-Wold-style-cast"
338
        int ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
502✔
339
#pragma clang diagnostic pop
340

341
        if (ret != Z_OK)
502!
342
        {
343
            std::cerr << "libz: failed to deflateInit" << std::endl;
×
344
            valid = false;
×
345
            return;
×
346
        }
347

348
        setg(nullptr, nullptr, nullptr);
502✔
349
        setp(in.data(), in.data() + buffer_size - 4); // we want to be 4 aligned
1,506✔
350

351
        // Write appropriate header
352
        if (header)
502!
353
        {
354
            header->header_offset = static_cast<std::uint32_t>(stream.tellp());
502✔
355
            write_header(*header, ostream, false);
502✔
356
        }
357

358
        uncompressed_size = crc = 0;
502✔
359
    }
×
360

361
    virtual ~zip_streambuf_compress() override
1,004✔
362
    {
502✔
363
        if (valid)
502!
364
        {
365
            process(true);
502✔
366
            deflateEnd(&strm);
502✔
367
            if (header)
502!
368
            {
369
                auto final_position = ostream.tellp();
502✔
370
                header->uncompressed_size = uncompressed_size;
502✔
371
                header->crc = crc;
502✔
372
                ostream.seekp(header->header_offset);
502✔
373
                write_header(*header, ostream, false);
502✔
374
                ostream.seekp(final_position);
502✔
375
            }
376
            else
377
            {
378
                write_int(ostream, crc);
×
379
                write_int(ostream, uncompressed_size);
×
380
            }
381
        }
382
        if (!header) delete &ostream;
502!
383
    }
1,004✔
384

385
protected:
386
    int process(bool flush)
6,390✔
387
    {
388
        if (!valid) return -1;
6,390!
389

390
        strm.next_in = reinterpret_cast<Bytef *>(pbase());
6,390✔
391
        strm.avail_in = static_cast<unsigned int>(pptr() - pbase());
6,390✔
392

393
        while (strm.avail_in != 0 || flush)
14,112✔
394
        {
395
            strm.avail_out = buffer_size;
8,224✔
396
            strm.next_out = reinterpret_cast<Bytef *>(out.data());
8,224✔
397

398
            int ret = deflate(&strm, flush ? Z_FINISH : Z_NO_FLUSH);
8,224✔
399

400
            if (!(ret != Z_BUF_ERROR && ret != Z_STREAM_ERROR))
8,224!
401
            {
402
                valid = false;
×
403
                std::cerr << "gzip: gzip error " << strm.msg << std::endl;
×
404
                return -1;
×
405
            }
406

407
            auto generated_output = static_cast<int>(strm.next_out - reinterpret_cast<std::uint8_t *>(out.data()));
8,224✔
408
            ostream.write(out.data(), generated_output);
16,448✔
409
            if (header) header->compressed_size += static_cast<std::uint32_t>(generated_output);
8,224!
410
            if (ret == Z_STREAM_END) break;
8,224✔
411
        }
412

413
        // update counts, crc's and buffers
414
        auto consumed_input = static_cast<std::uint32_t>(pptr() - pbase());
6,390✔
415
        uncompressed_size += consumed_input;
6,390✔
416
        crc = static_cast<std::uint32_t>(crc32(crc, reinterpret_cast<Bytef *>(in.data()), consumed_input));
12,780✔
417
        setp(pbase(), pbase() + buffer_size - 4);
6,390✔
418

419
        return 1;
6,390✔
420
    }
421

422
    virtual int sync() override
467✔
423
    {
424
        if (pptr() && pptr() > pbase()) return process(false);
467!
425
        return 0;
×
426
    }
427

428
    virtual int underflow() override
×
429
    {
430
        throw xlnt::exception("Attempt to read write only ostream");
×
431
    }
432

433
    virtual int overflow(int c = EOF) override;
434
};
435

436
int zip_streambuf_compress::overflow(int c)
5,421✔
437
{
438
    if (c != EOF)
5,421!
439
    {
440
        *pptr() = static_cast<char>(c);
5,421✔
441
        pbump(1);
5,421✔
442
    }
443
    if (process(false) == EOF) return EOF;
5,421!
444
    return c;
5,421✔
445
}
446

447
ozstream::ozstream(std::ostream &stream)
45✔
448
    : destination_stream_(stream)
45✔
449
{
450
    if (!destination_stream_)
45!
451
    {
452
        throw xlnt::exception("bad zip stream");
×
453
    }
454
}
45✔
455

456
ozstream::~ozstream()
90✔
457
{
458
    // Write all file headers
459
    auto final_position = destination_stream_.tellp();
45✔
460

461
    for (const auto &header : file_headers_)
547✔
462
    {
463
        write_header(header, destination_stream_, true);
502✔
464
    }
465

466
    auto central_end = destination_stream_.tellp();
45✔
467

468
    // Write end of central
469
    write_int(destination_stream_, static_cast<std::uint32_t>(0x06054b50)); // end of central
45✔
470
    write_int(destination_stream_, static_cast<std::uint16_t>(0)); // this disk number
45✔
471
    write_int(destination_stream_, static_cast<std::uint16_t>(0)); // this disk number
45✔
472
    write_int(destination_stream_, static_cast<std::uint16_t>(file_headers_.size())); // one entry in center in this disk
45✔
473
    write_int(destination_stream_, static_cast<std::uint16_t>(file_headers_.size())); // one entry in center
45✔
474
    write_int(destination_stream_, static_cast<std::uint32_t>(central_end - final_position)); // size of header
45✔
475
    write_int(destination_stream_, static_cast<std::uint32_t>(final_position)); // offset to header
45✔
476
    write_int(destination_stream_, static_cast<std::uint16_t>(0)); // zip comment
45✔
477
}
90✔
478

479
std::unique_ptr<std::streambuf> ozstream::open(const path &filename)
502✔
480
{
481
    zheader header;
502✔
482
    header.filename = filename.string();
502✔
483
    file_headers_.push_back(header);
502✔
484
    auto buffer = new zip_streambuf_compress(&file_headers_.back(), destination_stream_);
502!
485

486
    return std::unique_ptr<zip_streambuf_compress>(buffer);
1,004✔
487
}
502✔
488

489
izstream::izstream(std::istream &stream)
128✔
490
    : source_stream_(stream)
128✔
491
{
492
    if (!stream)
128✔
493
    {
494
        throw xlnt::invalid_file("Invalid file handle");
6✔
495
    }
496

497
    read_central_header();
126✔
498
}
128✔
499

500
izstream::~izstream()
218✔
501
{
502
}
218✔
503

504
bool izstream::read_central_header()
126✔
505
{
506
    // Find the header
507
    // NOTE: this assumes the zip file header is the last thing written to file...
508
    source_stream_.seekg(0, std::ios_base::end);
126✔
509
    auto end_position = source_stream_.tellg();
126✔
510

511
    auto max_comment_size = std::uint32_t(0xffff); // max size of header
126✔
512
    auto read_size_before_comment = std::uint32_t(22);
126✔
513

514
    std::streamoff read_start = max_comment_size + read_size_before_comment;
126✔
515

516
    if (read_start > end_position)
126✔
517
    {
518
        read_start = end_position;
124✔
519
    }
520

521
    source_stream_.seekg(end_position - read_start);
126✔
522
    std::vector<std::uint8_t> buf(static_cast<std::size_t>(read_start), '\0');
126✔
523

524
    if (read_start <= 0)
126!
525
    {
NEW
526
        throw xlnt::invalid_file("file is empty (read_start = " + std::to_string(read_start) + ")");
×
527
    }
528

529
    source_stream_.read(reinterpret_cast<char *>(buf.data()), read_start);
126✔
530

531
    if (buf[0] == 0xd0 && buf[1] == 0xcf && buf[2] == 0x11 && buf[3] == 0xe0
132!
532
        && buf[4] == 0xa1 && buf[5] == 0xb1 && buf[6] == 0x1a && buf[7] == 0xe1)
132!
533
    {
534
        throw xlnt::invalid_password("encrypted xlsx, password required");
18✔
535
    }
536

537
    auto found_header = false;
120✔
538
    std::streamoff header_index = 0;
120✔
539

540
    for (std::streamoff i = 0; i < read_start - 3; ++i)
1,893,906!
541
    {
542
        if (buf[static_cast<std::size_t>(i)] == 0x50
1,893,906✔
543
            && buf[static_cast<std::size_t>(i) + 1] == 0x4b
9,264✔
544
            && buf[static_cast<std::size_t>(i) + 2] == 0x05
2,908✔
545
            && buf[static_cast<std::size_t>(i) + 3] == 0x06)
1,903,170!
546
        {
547
            found_header = true;
120✔
548
            header_index = i;
120✔
549
            break;
120✔
550
        }
551
    }
552

553
    if (!found_header)
120!
554
    {
NEW
555
        throw xlnt::invalid_file("failed to find zip header");
×
556
    }
557

558
    // seek to end of central header and read
559
    source_stream_.seekg(end_position - (read_start - header_index));
120✔
560

561
    /*auto word = */ read_int<std::uint32_t>(source_stream_);
120✔
562
    auto disk_number1 = read_int<std::uint16_t>(source_stream_);
120✔
563
    auto disk_number2 = read_int<std::uint16_t>(source_stream_);
120✔
564

565
    if (disk_number1 != disk_number2 || disk_number1 != 0)
120!
566
    {
NEW
567
        throw xlnt::unsupported("multiple disk zip files are not supported (disk_number1 = " + std::to_string(disk_number1) + ", disk_number2 = " + std::to_string(disk_number2) + ")");
×
568
    }
569

570
    auto num_files = read_int<std::uint16_t>(source_stream_); // one entry in center in this disk
120✔
571
    auto num_files_this_disk = read_int<std::uint16_t>(source_stream_); // one entry in center
120✔
572

573
    if (num_files != num_files_this_disk)
120!
574
    {
NEW
575
        throw xlnt::unsupported("multi disk zip files are not supported (num_files = " + std::to_string(num_files) + ", num_files_this_disk = " + std::to_string(num_files_this_disk) + ")");
×
576
    }
577

578
    /*auto size_of_header = */ read_int<std::uint32_t>(source_stream_); // size of header
120✔
579
    auto header_offset = read_int<std::uint32_t>(source_stream_); // offset to header
120✔
580

581
    // go to header and read all file headers
582
    source_stream_.seekg(header_offset);
120✔
583

584
    for (std::uint16_t i = 0; i < num_files; ++i)
1,490✔
585
    {
586
        auto header = read_header(source_stream_, true);
1,370✔
587
        file_headers_[header.filename] = header;
1,370✔
588
    }
1,370✔
589

590
    return true;
120✔
591
}
126✔
592

593
std::unique_ptr<std::streambuf> izstream::open(const path &filename) const
1,376✔
594
{
595
    if (!has_file(filename))
1,376!
596
    {
NEW
597
        throw xlnt::invalid_file("file not found at path: " + filename.string());
×
598
    }
599

600
    auto header = file_headers_.at(filename.string());
1,376✔
601
    source_stream_.seekg(header.header_offset);
1,376✔
602
    auto buffer = new zip_streambuf_decompress(source_stream_, header);
1,376!
603

604
    return std::unique_ptr<zip_streambuf_decompress>(buffer);
2,752✔
605
}
1,376✔
606

607
std::string izstream::read(const path &filename) const
256✔
608
{
609
    auto buffer = open(filename);
256✔
610
    std::istream stream(buffer.get());
256✔
611
    auto bytes = to_vector(stream);
256✔
612

613
    return std::string(bytes.begin(), bytes.end());
512✔
614
}
256✔
615

616
std::vector<path> izstream::files() const
120✔
617
{
618
    std::vector<path> filenames;
120✔
619
    std::transform(file_headers_.begin(), file_headers_.end(), std::back_inserter(filenames),
120✔
620
        [](const std::pair<std::string, zheader> &h) { return path(h.first); });
1,370✔
621

622
    return filenames;
120✔
623
}
×
624

625
bool izstream::has_file(const path &filename) const
2,716✔
626
{
627
    return file_headers_.count(filename.string()) != 0;
2,716✔
628
}
629

630
} // namespace detail
631
} // 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