add new zip class to repo

This commit is contained in:
Thomas Fussell 2014-08-01 09:44:21 -04:00
parent ec1d9f7ecb
commit 57aa270f25
9 changed files with 1160 additions and 453 deletions

View File

@ -1,134 +1,115 @@
// Copyright (c) 2014 Thomas Fussell
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, WRISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE
//
// @license: http://www.opensource.org/licenses/mit-license.php
// @author: see AUTHORS file
#pragma once
#include <cstdint>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#define MINIZ_HEADER_FILE_ONLY
#include <miniz.c>
struct mz_zip_archive_tag;
namespace xlnt {
/// <summary>
/// Defines constants for read, write, or read/write access to a file.
/// </summary>
enum class file_access
struct zip_info
{
/// <summary>
/// Read access to the file. Data can only be read from the file. Combine with write for read/write access.
/// </summary>
read = 0x01,
/// <summary>
/// Read and write access to the file. Data can be both written to and read from the file.
/// </summary>
read_write = 0x03,
/// <summary>
/// Write access to the file. Data can only be written to the file. Combine with read for read/write access.
/// </summary>
write = 0x02
std::string filename;
struct
{
int year;
int month;
int day;
int hours;
int minutes;
int seconds;
} date_time;
std::string comment;
std::string extra;
uint16_t create_system;
uint16_t create_version;
uint16_t extract_version;
uint16_t flag_bits;
std::size_t volume;
uint32_t internal_attr;
uint32_t external_attr;
std::size_t header_offset;
uint32_t crc;
std::size_t compress_size;
std::size_t file_size;
};
/// <summary>
/// Specifies how the operating system should open a file.
/// </summary>
enum class file_mode
{
/// <summary>
/// Opens the file if it exists and seeks to the end of the file, or creates a new file.This requires FileIOPermissionAccess.Append permission.file_mode.Append can be used only in conjunction with file_access.Write.Trying to seek to a position before the end of the file throws an IOException exception, and any attempt to read fails and throws a NotSupportedException exception.
/// </summary>
append,
/// <summary>
/// Specifies that the operating system should create a new file. If the file already exists, it will be overwritten. This requires FileIOPermissionAccess.Write permission. file_mode.Create is equivalent to requesting that if the file does not exist, use CreateNew; otherwise, use Truncate. If the file already exists but is a hidden file, an UnauthorizedAccessException exception is thrown.
/// </summary>
create,
/// <summary>
/// Specifies that the operating system should create a new file. This requires FileIOPermissionAccess.Write permission. If the file already exists, an IOException exception is thrown.
/// </summary>
create_new,
/// <summary>
/// Specifies that the operating system should open an existing file. The ability to open the file is dependent on the value specified by the file_access enumeration. A System.IO.FileNotFoundException exception is thrown if the file does not exist.
/// </summary>
open,
/// <summary>
/// Specifies that the operating system should open a file if it exists; otherwise, a new file should be created. If the file is opened with file_access.Read, FileIOPermissionAccess.Read permission is required. If the file access is file_access.Write, FileIOPermissionAccess.Write permission is required. If the file is opened with file_access.ReadWrite, both FileIOPermissionAccess.Read and FileIOPermissionAccess.Write permissions are required.
/// </summary>
open_or_create,
/// <summary>
/// Specifies that the operating system should open an existing file. When the file is opened, it should be truncated so that its size is zero bytes. This requires FileIOPermissionAccess.Write permission. Attempts to read from a file opened with file_mode.Truncate cause an ArgumentException exception.
/// </summary>
truncate
};
class zip_file
{
private:
enum class state
{
read,
write,
closed
};
public:
zip_file(const std::string &filename, file_mode mode, file_access access = file_access::read);
zip_file();
zip_file(const std::string &filename);
zip_file(const std::vector<unsigned char> &bytes);
zip_file(std::istream &stream);
~zip_file();
// to/from file
void load(const std::string &filename);
void save(const std::string &filename);
// to/from byte vector
void load(const std::vector<unsigned char> &bytes);
void save(std::vector<unsigned char> &bytes);
// to/from iostream
void load(std::istream &stream);
void save(std::ostream &stream);
void reset();
zip_info getinfo(const std::string &name);
std::vector<zip_info> infolist();
std::vector<std::string> namelist();
std::string get_file_contents(const std::string &filename) const;
std::ostream &open(const std::string &name);
std::ostream &open(const zip_info &name);
void set_file_contents(const std::string &filename, const std::string &contents);
void extract(const std::string &name);
void extract(const std::string &name, const std::string &path);
void extract(const zip_info &name);
void extract(const zip_info &name, const std::string &path);
void delete_file(const std::string &filename);
void extractall();
void extractall(const std::string &path);
void extractall(const std::string &path, const std::vector<std::string> &members);
void extractall(const std::string &path, const std::vector<zip_info> &members);
bool has_file(const std::string &filename);
void printdir();
void printdir(std::ostream &stream);
void flush(bool force_write = false);
std::string read(const std::string &name);
std::string read(const zip_info &name);
std::pair<bool, std::string> testzip();
void write(const std::string &filename);
void write(const std::string &filename, const std::string &arcname);
void writestr(const std::string &arcname, const std::string &bytes);
void writestr(const zip_info &arcname, const std::string &bytes);
std::string get_filename() const { return filename_; }
std::string comment;
private:
void read_all();
void write_all();
std::string read_from_zip(const std::string &filename);
void write_to_zip(const std::string &filename, const std::string &content, bool append = true);
void write_directory_to_zip(const std::string &name, bool append = true);
void change_state(state new_state, bool append = true);
static bool file_exists(const std::string& name);
void start_read();
void stop_read();
void start_write(bool append);
void stop_write();
void start_write();
mz_zip_archive zip_file_;
state current_state_;
std::string filename_;
std::unordered_map<std::string, std::string> files_;
bool modified_;
file_access access_;
std::vector<std::string> directories_;
};
} // namespace xlnt
void append_comment();
void remove_comment();
zip_info getinfo(int index);
std::unique_ptr<mz_zip_archive_tag> archive_;
std::vector<char> buffer_;
std::stringstream open_stream_;
std::string filename_;
};
} // namespace xlnt

View File

@ -43,17 +43,17 @@ public:
static const std::string CentralDirectorySignature;
static std::string repair_central_directory(const std::string &original);
static void fast_parse(worksheet ws, std::istream &xml_source, const std::vector<std::string> &shared_string, const std::vector<style> &style_table, std::size_t color_index);
static std::vector<relationship> read_relationships(const zip_file &content, const std::string &filename);
static std::vector<std::pair<std::string, std::string>> read_content_types(const zip_file &archive);
static std::vector<relationship> read_relationships(zip_file &content, const std::string &filename);
static std::vector<std::pair<std::string, std::string>> read_content_types(zip_file &archive);
static std::string determine_document_type(const std::vector<std::pair<std::string, std::string>> &override_types);
static worksheet read_worksheet(std::istream &handle, workbook &wb, const std::string &title, const std::vector<std::string> &string_table);
static void read_worksheet(worksheet ws, const std::string &xml_string, const std::vector<std::string> &string_table, const std::vector<int> &number_format_ids);
static std::vector<std::string> read_shared_string(const std::string &xml_string);
static std::string read_dimension(const std::string &xml_string);
static document_properties read_properties_core(const std::string &xml_string);
static std::vector<std::pair<std::string,std::string>> read_sheets(const zip_file &archive);
static std::vector<std::pair<std::string,std::string>> read_sheets(zip_file &archive);
static workbook load_workbook(const std::string &filename, bool guess_types = false, bool data_only = false);
static std::vector<std::pair<std::string, std::string>> detect_worksheets(const zip_file &archive);
static std::vector<std::pair<std::string, std::string>> detect_worksheets(zip_file &archive);
};
} // namespace xlnt

View File

@ -46,9 +46,9 @@ std::string reader::repair_central_directory(const std::string &original)
return original;
}
std::vector<std::pair<std::string, std::string>> reader::read_sheets(const zip_file &archive)
std::vector<std::pair<std::string, std::string>> reader::read_sheets(zip_file &archive)
{
auto xml_source = archive.get_file_contents("xl/workbook.xml");
auto xml_source = archive.read("xl/workbook.xml");
pugi::xml_document doc;
doc.load(xml_source.c_str());
@ -144,7 +144,7 @@ std::string reader::read_dimension(const std::string &xml_string)
return dimension;
}
std::vector<relationship> reader::read_relationships(const zip_file &archive, const std::string &filename)
std::vector<relationship> reader::read_relationships(zip_file &archive, const std::string &filename)
{
auto filename_separator_index = filename.find_last_of('/');
auto basename = filename.substr(filename_separator_index + 1);
@ -152,7 +152,7 @@ std::vector<relationship> reader::read_relationships(const zip_file &archive, co
auto rels_filename = dirname + "/_rels/" + basename + ".rels";
pugi::xml_document doc;
auto content = archive.get_file_contents(rels_filename);
auto content = archive.read(rels_filename);
doc.load(content.c_str());
auto root_node = doc.child("Relationships");
@ -181,13 +181,13 @@ std::vector<relationship> reader::read_relationships(const zip_file &archive, co
return relationships;
}
std::vector<std::pair<std::string, std::string>> reader::read_content_types(const zip_file &archive)
std::vector<std::pair<std::string, std::string>> reader::read_content_types(zip_file &archive)
{
pugi::xml_document doc;
try
{
doc.load(archive.get_file_contents("[Content_Types].xml").c_str());
doc.load(archive.read("[Content_Types].xml").c_str());
}
catch(std::out_of_range)
{
@ -411,7 +411,7 @@ workbook reader::load_workbook(const std::string &filename, bool guess_types, bo
return wb;
}
std::vector<std::pair<std::string, std::string>> reader::detect_worksheets(const zip_file &archive)
std::vector<std::pair<std::string, std::string>> reader::detect_worksheets(zip_file &archive)
{
static const std::string ValidWorksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml";

View File

@ -285,7 +285,7 @@ bool workbook::load(const std::vector<unsigned char> &data)
bool workbook::load(const std::string &filename)
{
zip_file f(filename, file_mode::open);
zip_file f(filename);
//auto core_properties = read_core_properties();
//auto app_properties = read_app_properties();
auto content_types = reader::read_content_types(f);
@ -307,7 +307,7 @@ bool workbook::load(const std::string &filename)
}
pugi::xml_document doc;
doc.load(f.get_file_contents("xl/workbook.xml").c_str());
doc.load(f.read("xl/workbook.xml").c_str());
auto root_node = doc.child("workbook");
@ -317,17 +317,20 @@ bool workbook::load(const std::string &filename)
auto sheets_node = root_node.child("sheets");
std::vector<std::string> shared_strings;
if(f.has_file("xl/sharedStrings.xml"))
auto infolist = f.infolist();
auto shared_strings_info = std::find_if(infolist.begin(), infolist.end(), [](const zip_info &info) { return info.filename == "xl/sharedStrings.xml"; });
if(shared_strings_info != infolist.end())
{
shared_strings = xlnt::reader::read_shared_string(f.get_file_contents("xl/sharedStrings.xml"));
shared_strings = xlnt::reader::read_shared_string(f.read(*shared_strings_info));
}
std::vector<int> number_format_ids;
if(f.has_file("xl/styles.xml"))
auto styles_info = std::find_if(infolist.begin(), infolist.end(), [](const zip_info &info) { return info.filename == "xl/styles.xml"; });
if(shared_strings_info != infolist.end())
{
pugi::xml_document styles_doc;
styles_doc.load(f.get_file_contents("xl/styles.xml").c_str());
styles_doc.load(f.read(*styles_info).c_str());
auto stylesheet_node = styles_doc.child("styleSheet");
auto cell_xfs_node = stylesheet_node.child("cellXfs");
@ -342,7 +345,7 @@ bool workbook::load(const std::string &filename)
std::string relation_id = sheet_node.attribute("r:id").as_string();
auto ws = create_sheet(sheet_node.attribute("name").as_string());
auto sheet_filename = get_relationship(relation_id).get_target_uri();
xlnt::reader::read_worksheet(ws, f.get_file_contents(sheet_filename).c_str(), shared_strings, number_format_ids);
xlnt::reader::read_worksheet(ws, f.read(sheet_filename).c_str(), shared_strings, number_format_ids);
}
return true;
@ -509,12 +512,12 @@ bool workbook::save(std::vector<unsigned char> &data)
bool workbook::save(const std::string &filename)
{
zip_file f(filename, file_mode::create, file_access::write);
zip_file f;
f.set_file_contents("[Content_Types].xml", writer::write_content_types(*this));
f.writestr("[Content_Types].xml", writer::write_content_types(*this));
f.set_file_contents("docProps/app.xml", writer::write_properties_app(*this));
f.set_file_contents("docProps/core.xml", writer::write_properties_core(get_properties()));
f.writestr("docProps/app.xml", writer::write_properties_app(*this));
f.writestr("docProps/core.xml", writer::write_properties_core(get_properties()));
std::set<std::string> shared_strings_set;
@ -533,15 +536,15 @@ bool workbook::save(const std::string &filename)
}
std::vector<std::string> shared_strings(shared_strings_set.begin(), shared_strings_set.end());
f.set_file_contents("xl/sharedStrings.xml", writer::write_shared_strings(shared_strings));
f.writestr("xl/sharedStrings.xml", writer::write_shared_strings(shared_strings));
f.set_file_contents("xl/theme/theme1.xml", writer::write_theme());
f.set_file_contents("xl/styles.xml", style_writer(*this).write_table());
f.writestr("xl/theme/theme1.xml", writer::write_theme());
f.writestr("xl/styles.xml", style_writer(*this).write_table());
f.set_file_contents("_rels/.rels", writer::write_root_rels());
f.set_file_contents("xl/_rels/workbook.xml.rels", writer::write_workbook_rels(*this));
f.writestr("_rels/.rels", writer::write_root_rels());
f.writestr("xl/_rels/workbook.xml.rels", writer::write_workbook_rels(*this));
f.set_file_contents("xl/workbook.xml", writer::write_workbook(*this));
f.writestr("xl/workbook.xml", writer::write_workbook(*this));
for(auto relationship : d_->relationships_)
{
@ -551,7 +554,7 @@ bool workbook::save(const std::string &filename)
std::size_t sheet_index = std::stoi(sheet_index_string.substr(0, sheet_index_string.find('.'))) - 1;
std::string sheet_uri = "xl/" + relationship.get_target_uri();
auto ws = get_sheet_by_index(sheet_index);
f.set_file_contents(sheet_uri, writer::write_worksheet(ws, shared_strings));
f.writestr(sheet_uri, writer::write_worksheet(ws, shared_strings));
}
}

View File

@ -1,362 +1,680 @@
#include <array>
#include <fstream>
#define MINIZ_HEADER_FILE_ONLY
#include "miniz.c"
#ifdef _WIN32
#include <Windows.h>
#else
#endif
#include "common/zip_file.hpp"
#include "common/exceptions.hpp"
namespace xlnt {
namespace {
zip_file::zip_file(const std::string &filename, file_mode mode, file_access access)
: current_state_(state::closed),
filename_(filename),
modified_(false),
// mode_(mode),
access_(access)
std::string get_working_directory()
{
switch(mode)
#ifdef _WIN32
TCHAR buffer[MAX_PATH];
GetCurrentDirectory(MAX_PATH, buffer);
return buffer;
#endif
return "";
}
#ifdef _WIN32
char directory_separator = '\\';
#else
char directory_separator = '/';
#endif
std::string join_path(const std::vector<std::string> &parts)
{
std::string joined;
std::size_t i = 0;
for(auto part : parts)
{
case file_mode::open:
read_all();
break;
case file_mode::open_or_create:
if(file_exists(filename))
{
read_all();
}
else
{
flush(true);
}
break;
case file_mode::create:
flush(true);
break;
case file_mode::create_new:
if(file_exists(filename))
{
throw std::runtime_error("file exists");
}
flush(true);
break;
case file_mode::truncate:
if((int)access & (int)file_access::read)
{
throw std::runtime_error("cannot read from file opened with file_mode truncate");
}
flush(true);
break;
case file_mode::append:
read_all();
break;
joined.append(part);
if(i++ != parts.size() - 1)
{
joined.append(1, directory_separator);
}
}
return joined;
}
std::vector<std::string> split_path(const std::string &path)
{
std::vector<std::string> split;
std::string::size_type previous_index = 0;
auto separator_index = path.find(directory_separator);
while(separator_index != std::string::npos)
{
split.push_back(path.substr(previous_index, separator_index));
previous_index = separator_index + 1;
separator_index = path.find(previous_index, directory_separator);
}
split.push_back(path.substr(previous_index));
return split;
}
uint32_t crc32buf(const char *buf, std::size_t len)
{
uint32_t oldcrc32 = 0xFFFFFFFF;
uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
#define UPDC32(octet,crc) (crc_32_tab[((crc)\
^ ((uint8_t)octet)) & 0xff] ^ ((crc) >> 8))
for ( ; len; --len, ++buf)
{
oldcrc32 = UPDC32(*buf, oldcrc32);
}
return ~oldcrc32;
}
} // namespace
namespace xlnt {
zip_file::zip_file() : archive_(new mz_zip_archive())
{
reset();
}
zip_file::zip_file(const std::string &filename) : zip_file()
{
load(filename);
}
zip_file::zip_file(std::istream &stream) : zip_file()
{
load(stream);
}
zip_file::zip_file(const std::vector<unsigned char> &bytes) : zip_file()
{
load(bytes);
}
zip_file::~zip_file()
{
change_state(state::closed);
reset();
}
std::string zip_file::get_file_contents(const std::string &filename) const
void zip_file::load(std::istream &stream)
{
return files_.at(filename);
reset();
buffer_.assign(std::istreambuf_iterator<char>(stream), {});
remove_comment();
start_read();
}
std::string dirname(const std::string &filename)
void zip_file::load(const std::string &filename)
{
auto last_sep_index = filename.find_last_of('/');
filename_ = filename;
std::ifstream stream(filename, std::ios::binary);
load(stream);
}
if(last_sep_index != std::string::npos)
void zip_file::load(const std::vector<unsigned char> &bytes)
{
reset();
buffer_.assign(bytes.begin(), bytes.end());
remove_comment();
start_read();
}
void zip_file::save(const std::string &filename)
{
filename_ = filename;
std::ofstream stream(filename, std::ios::binary);
save(stream);
}
void zip_file::save(std::ostream &stream)
{
if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING)
{
return filename.substr(0, last_sep_index + 1);
mz_zip_writer_finalize_archive(archive_.get());
}
if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)
{
mz_zip_writer_end(archive_.get());
}
return "";
if(archive_->m_zip_mode == MZ_ZIP_MODE_INVALID)
{
start_read();
}
append_comment();
stream.write(buffer_.data(), buffer_.size());
}
void zip_file::set_file_contents(const std::string &filename, const std::string &contents)
void zip_file::save(std::vector<unsigned char> &bytes)
{
if(!has_file(filename) || files_[filename] != contents)
if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING)
{
modified_ = true;
mz_zip_writer_finalize_archive(archive_.get());
}
auto dir = dirname(filename);
while(dir != "")
if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)
{
auto matching_directory = std::find(directories_.begin(), directories_.end(), dir);
mz_zip_writer_end(archive_.get());
}
if(archive_->m_zip_mode == MZ_ZIP_MODE_INVALID)
{
start_read();
}
append_comment();
bytes.assign(buffer_.begin(), buffer_.end());
}
if(matching_directory == directories_.end())
{
directories_.push_back(dir);
}
dir = dirname(dir.substr(0, dir.length() - 1));
}
files_[filename] = contents;
}
void zip_file::delete_file(const std::string &filename)
{
files_.erase(filename);
}
bool zip_file::has_file(const std::string &filename)
{
return files_.find(filename) != files_.end();
}
void zip_file::flush(bool force_write)
{
if(modified_ || force_write)
{
write_all();
}
}
void zip_file::read_all()
{
if(!((int)access_ & (int)file_access::read))
{
throw std::runtime_error("don't have read access");
}
change_state(state::read);
auto num_files = zip_file_.m_total_files;
std::size_t i = 0;
for(;i < num_files; i++)
{
mz_zip_archive_file_stat file_info;
if(!mz_zip_reader_file_stat(&zip_file_, i, &file_info))
{
throw std::runtime_error("stat failed");
}
std::string current_filename(file_info.m_filename, file_info.m_filename + strlen(file_info.m_filename));
if(mz_zip_reader_is_file_a_directory(&zip_file_, i))
{
directories_.push_back(current_filename);
continue;
}
files_[current_filename] = read_from_zip(current_filename);
}
}
void zip_file::write_all()
{
if(!((int)access_ & (int)file_access::write))
{
throw std::runtime_error("don't have write access");
}
change_state(state::write, false);
for(auto directory : directories_)
{
write_directory_to_zip(directory, true);
}
for(auto file : files_)
{
write_to_zip(file.first, file.second, true);
}
modified_ = false;
}
std::string zip_file::read_from_zip(const std::string &filename)
{
if(!((int)access_ & (int)file_access::read))
{
throw std::runtime_error("don't have read access");
}
change_state(state::read);
auto num_files = (std::size_t)mz_zip_reader_get_num_files(&zip_file_);
std::size_t i = 0;
for(;i < num_files; i++)
{
mz_zip_archive_file_stat file_info;
if(!mz_zip_reader_file_stat(&zip_file_, i, &file_info))
{
throw std::runtime_error("stat failed");
}
std::string current_filename(file_info.m_filename, file_info.m_filename + strlen(file_info.m_filename));
if(filename == current_filename)
void zip_file::append_comment()
{
if(!comment.empty())
{
uint16_t comment_length = comment.length();
buffer_[buffer_.size() - 2] = comment_length & 0xFF;
buffer_[buffer_.size() - 1] = comment_length >> 8 & 0xFF;
std::copy(comment.begin(), comment.end(), std::back_inserter(buffer_));
}
}
void zip_file::remove_comment()
{
if(buffer_.empty()) return;
std::size_t position = buffer_.size() - 1;
for(; position >= 3; position--)
{
if(buffer_[position - 3] == 'P'
&& buffer_[position - 2] == 'K'
&& buffer_[position - 1] == '\x05'
&& buffer_[position] == '\x06')
{
position = position + 17;
break;
}
}
if(i == num_files)
if(position == 3)
{
throw std::runtime_error("didn't find end of central directory signature");
}
uint16_t length = buffer_[position + 1];
length = (length << 8) + buffer_[position];
position += 2;
if(length != 0)
{
comment = std::string(buffer_.data() + position, buffer_.data() + position + length);
buffer_.resize(buffer_.size() - length);
buffer_[buffer_.size() - 1] = 0;
buffer_[buffer_.size() - 2] = 0;
}
}
void zip_file::reset()
{
switch(archive_->m_zip_mode)
{
case MZ_ZIP_MODE_READING:
mz_zip_reader_end(archive_.get());
break;
case MZ_ZIP_MODE_WRITING:
mz_zip_writer_finalize_archive(archive_.get());
mz_zip_writer_end(archive_.get());
break;
case MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED:
mz_zip_writer_end(archive_.get());
break;
case MZ_ZIP_MODE_INVALID:
break;
}
if(archive_->m_zip_mode != MZ_ZIP_MODE_INVALID)
{
throw std::runtime_error("");
}
buffer_.clear();
comment.clear();
start_write();
mz_zip_writer_finalize_archive(archive_.get());
mz_zip_writer_end(archive_.get());
}
zip_info zip_file::getinfo(const std::string &name)
{
if(archive_->m_zip_mode != MZ_ZIP_MODE_READING)
{
start_read();
}
int index = mz_zip_reader_locate_file(archive_.get(), name.c_str(), nullptr, 0);
if(index == -1)
{
throw std::runtime_error("not found");
}
if(mz_zip_reader_is_file_a_directory(&zip_file_, i))
{
directories_.push_back(filename);
return "";
}
std::size_t uncomp_size = 0;
char archive_filename[260];
std::fill(archive_filename, archive_filename + 260, '\0');
std::copy(filename.begin(), filename.begin() + filename.length(), archive_filename);
char *data = (char *)mz_zip_reader_extract_file_to_heap(&zip_file_, archive_filename, &uncomp_size, 0);
if(data == nullptr)
{
throw std::runtime_error("extract failed");
}
std::string content(data, data + uncomp_size);
mz_free(data);
return content;
return getinfo(index);
}
void zip_file::write_directory_to_zip(const std::string &name, bool append)
zip_info zip_file::getinfo(int index)
{
if(!((int)access_ & (int)file_access::write))
if(archive_->m_zip_mode != MZ_ZIP_MODE_READING)
{
throw std::runtime_error("don't have write access");
start_read();
}
change_state(state::write, append);
if(!mz_zip_writer_add_mem(&zip_file_, name.c_str(), nullptr, 0, MZ_BEST_COMPRESSION))
{
throw std::runtime_error("write directory failed");
}
}
mz_zip_archive_file_stat stat;
mz_zip_reader_file_stat(archive_.get(), index, &stat);
void zip_file::write_to_zip(const std::string &filename, const std::string &content, bool append)
{
if(!((int)access_ & (int)file_access::write))
{
throw std::runtime_error("don't have write access");
}
change_state(state::write, append);
auto status = mz_zip_writer_add_mem(&zip_file_, filename.c_str(), content.c_str(), content.length(), MZ_BEST_COMPRESSION);
if(!status)
{
throw std::runtime_error("write failed");
}
}
zip_info result;
void zip_file::change_state(state new_state, bool append)
{
if(new_state == current_state_ && append)
{
return;
}
result.filename = std::string(stat.m_filename, stat.m_filename + std::strlen(stat.m_filename));
result.comment = std::string(stat.m_comment, stat.m_comment + stat.m_comment_size);
result.compress_size = (std::size_t)stat.m_comp_size;
result.file_size = (std::size_t)stat.m_uncomp_size;
result.header_offset = (std::size_t)stat.m_local_header_ofs;
result.crc = stat.m_crc32;
tm *time = localtime(&stat.m_time);
result.date_time.year = 1900 + time->tm_year;
result.date_time.month = 1 + time->tm_mon;
result.date_time.day = time->tm_mday;
result.date_time.hours = time->tm_hour;
result.date_time.minutes = time->tm_min;
result.date_time.seconds = time->tm_sec;
result.flag_bits = stat.m_bit_flag;
result.internal_attr = stat.m_internal_attr;
result.external_attr = stat.m_external_attr;
result.extract_version = stat.m_version_needed;
result.create_version = stat.m_version_made_by;
result.volume = stat.m_file_index;
result.create_system = stat.m_method;
switch(new_state)
{
case state::closed:
if(current_state_ == state::write)
{
stop_write();
}
else if(current_state_ == state::read)
{
stop_read();
}
break;
case state::read:
if(current_state_ == state::write)
{
stop_write();
}
start_read();
break;
case state::write:
if(current_state_ == state::read)
{
stop_read();
}
if(current_state_ != state::write)
{
start_write(append);
}
break;
default:
throw std::runtime_error("bad enum");
}
current_state_ = new_state;
}
bool zip_file::file_exists(const std::string& name)
{
std::ifstream f(name.c_str());
return f.good();
return result;
}
void zip_file::start_read()
{
if(!mz_zip_reader_init_file(&zip_file_, filename_.c_str(), 0))
if(archive_->m_zip_mode == MZ_ZIP_MODE_READING) return;
if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING)
{
throw invalid_file_exception(filename_);
}
}
void zip_file::stop_read()
{
if(!mz_zip_reader_end(&zip_file_))
{
throw std::runtime_error("");
}
}
void zip_file::start_write(bool append)
{
if(append && !file_exists(filename_))
{
throw std::runtime_error("can't append to non-existent file");
mz_zip_writer_finalize_archive(archive_.get());
}
if(!mz_zip_writer_init_file(&zip_file_, filename_.c_str(), 0))
if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)
{
if(append)
mz_zip_writer_end(archive_.get());
}
if(!mz_zip_reader_init_mem(archive_.get(), buffer_.data(), buffer_.size(), 0))
{
throw std::runtime_error("bad zip");
}
}
std::size_t write_callback(void *opaque, mz_uint64 file_ofs, const void *pBuf, std::size_t n)
{
auto buffer = (std::vector<char> *)opaque;
if(file_ofs + n > buffer->size())
{
buffer->resize(file_ofs + n);
}
for(std::size_t i = 0; i < n; i++)
{
(*buffer)[(std::size_t)file_ofs + i] = ((const char *)pBuf)[i];
}
return n;
}
void zip_file::start_write()
{
if(archive_->m_zip_mode == MZ_ZIP_MODE_WRITING) return;
switch(archive_->m_zip_mode)
{
case MZ_ZIP_MODE_READING:
{
throw std::runtime_error("couldn't append to zip file");
mz_zip_archive archive_copy;
std::memset(&archive_copy, 0, sizeof(mz_zip_archive));
std::vector<char> buffer_copy(buffer_.begin(), buffer_.end());
if(!mz_zip_reader_init_mem(&archive_copy, buffer_copy.data(), buffer_copy.size(), 0))
{
throw std::runtime_error("bad zip");
}
mz_zip_reader_end(archive_.get());
archive_->m_pWrite = &write_callback;
archive_->m_pIO_opaque = &buffer_;
buffer_ = std::vector<char>();
if(!mz_zip_writer_init(archive_.get(), 0))
{
throw std::runtime_error("bad zip");
}
for(int i = 0; i < (int)archive_copy.m_total_files; i++)
{
if(!mz_zip_writer_add_from_zip_reader(archive_.get(), &archive_copy, i))
{
throw std::runtime_error("fail");
}
}
mz_zip_reader_end(&archive_copy);
return;
}
else
case MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED:
mz_zip_writer_end(archive_.get());
break;
default:
break;
}
archive_->m_pWrite = &write_callback;
archive_->m_pIO_opaque = &buffer_;
if(!mz_zip_writer_init(archive_.get(), 0))
{
throw std::runtime_error("bad zip");
}
}
void zip_file::write(const std::string &filename)
{
auto split = split_path(filename);
if(split.size() > 1)
{
split.erase(split.begin());
}
auto arcname = join_path(split);
write(filename, arcname);
}
void zip_file::write(const std::string &filename, const std::string &arcname)
{
std::fstream file(filename, std::ios::binary | std::ios::in);
std::stringstream ss;
ss << file.rdbuf();
std::string bytes = ss.str();
writestr(arcname, bytes);
}
void zip_file::writestr(const std::string &arcname, const std::string &bytes)
{
if(archive_->m_zip_mode != MZ_ZIP_MODE_WRITING)
{
start_write();
}
if(!mz_zip_writer_add_mem(archive_.get(), arcname.c_str(), bytes.data(), bytes.size(), MZ_DEFAULT_COMPRESSION))
{
throw std::runtime_error("write error");
}
}
void zip_file::writestr(const zip_info &info, const std::string &bytes)
{
if(info.filename.empty() || info.date_time.year < 1980)
{
throw std::runtime_error("must specify a filename and valid date (year >= 1980");
}
if(archive_->m_zip_mode != MZ_ZIP_MODE_WRITING)
{
start_write();
}
auto crc = crc32buf(bytes.c_str(), bytes.size());
if(!mz_zip_writer_add_mem_ex(archive_.get(), info.filename.c_str(), bytes.data(), bytes.size(), info.comment.c_str(), (mz_uint16)info.comment.size(), MZ_DEFAULT_COMPRESSION, 0, crc))
{
throw std::runtime_error("write error");
}
}
std::string zip_file::read(const zip_info &info)
{
std::size_t size;
char *data = (char *)mz_zip_reader_extract_file_to_heap(archive_.get(), info.filename.c_str(), &size, 0);
if(data == nullptr)
{
throw std::runtime_error("file couldn't be read");
}
std::string extracted(data, data + size);
mz_free(data);
return extracted;
}
std::string zip_file::read(const std::string &name)
{
return read(getinfo(name));
}
std::vector<zip_info> zip_file::infolist()
{
if(archive_->m_zip_mode != MZ_ZIP_MODE_READING)
{
start_read();
}
std::vector<zip_info> info;
for(std::size_t i = 0; i < mz_zip_reader_get_num_files(archive_.get()); i++)
{
info.push_back(getinfo((int)i));
}
return info;
}
std::vector<std::string> zip_file::namelist()
{
std::vector<std::string> names;
for(auto &info : infolist())
{
names.push_back(info.filename);
}
return names;
}
std::ostream &zip_file::open(const std::string &name)
{
return open(getinfo(name));
}
std::ostream &zip_file::open(const zip_info &name)
{
auto data = read(name);
std::string data_string(data.begin(), data.end());
open_stream_ << data_string;
return open_stream_;
}
void zip_file::extract(const std::string &member)
{
extract(member, get_working_directory());
}
void zip_file::extract(const std::string &member, const std::string &path)
{
std::fstream stream(join_path({path, member}), std::ios::binary | std::ios::out);
stream << open(member).rdbuf();
}
void zip_file::extract(const zip_info &member)
{
extract(member, get_working_directory());
}
void zip_file::extract(const zip_info &member, const std::string &path)
{
std::fstream stream(join_path({path, member.filename}), std::ios::binary | std::ios::out);
stream << open(member).rdbuf();
}
void zip_file::extractall(const std::string &path)
{
extractall(path, infolist());
}
void zip_file::extractall(const std::string &path, const std::vector<std::string> &members)
{
for(auto &member : members)
{
extract(member, path);
}
}
void zip_file::extractall(const std::string &path, const std::vector<zip_info> &members)
{
for(auto &member : members)
{
extract(member, path);
}
}
void zip_file::printdir()
{
printdir(std::cout);
}
void zip_file::printdir(std::ostream &stream)
{
stream << " Length " << " " << " " << "Date" << " " << " " << "Time " << " " << "Name" << std::endl;
stream << "--------- ---------- ----- ----" << std::endl;
std::size_t sum_length = 0;
std::size_t file_count = 0;
for(auto &member : infolist())
{
sum_length += member.file_size;
file_count++;
std::string length_string = std::to_string(member.file_size);
while(length_string.length() < 9)
{
throw std::runtime_error("couldn't create zip file");
length_string = " " + length_string;
}
stream << length_string;
stream << " ";
stream << (member.date_time.month < 10 ? "0" : "") << member.date_time.month;
stream << "/";
stream << (member.date_time.day < 10 ? "0" : "") << member.date_time.day;
stream << "/";
stream << member.date_time.year;
stream << " ";
stream << (member.date_time.hours < 10 ? "0" : "") << member.date_time.hours;
stream << ":";
stream << (member.date_time.minutes < 10 ? "0" : "") << member.date_time.minutes;
stream << " ";
stream << member.filename;
stream << std::endl;
}
stream << "--------- -------" << std::endl;
std::string length_string = std::to_string(sum_length);
while(length_string.length() < 9)
{
length_string = " " + length_string;
}
stream << length_string << " " << file_count << " " << (file_count == 1 ? "file" : "files");
stream << std::endl;
}
std::pair<bool, std::string> zip_file::testzip()
{
if(archive_->m_zip_mode == MZ_ZIP_MODE_INVALID)
{
throw std::runtime_error("not open");
}
for(auto &file : infolist())
{
auto content = read(file);
auto crc = crc32buf(content.c_str(), content.size());
if(crc != file.crc)
{
return {false, file.filename};
}
}
return {true, ""};
}
void zip_file::stop_write()
{
flush();
if(!mz_zip_writer_finalize_archive(&zip_file_))
{
throw std::runtime_error("");
}
if(!mz_zip_writer_end(&zip_file_))
{
throw std::runtime_error("");
}
}
}
} // namespace xlnt

View File

@ -1443,5 +1443,138 @@ public:
void runTest() { suite_test_write.test_write_images(); }
} testDescription_suite_test_write_test_write_images;
#include "/Users/thomas/Development/xlnt/tests/test_zip_file.hpp"
static test_zip_file suite_test_zip_file;
static CxxTest::List Tests_test_zip_file = { 0, 0 };
CxxTest::StaticSuiteDescription suiteDescription_test_zip_file( "../../tests/test_zip_file.hpp", 9, "test_zip_file", suite_test_zip_file, Tests_test_zip_file );
static class TestDescription_suite_test_zip_file_test_load_file : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_load_file() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 53, "test_load_file" ) {}
void runTest() { suite_test_zip_file.test_load_file(); }
} testDescription_suite_test_zip_file_test_load_file;
static class TestDescription_suite_test_zip_file_test_load_stream : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_load_stream() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 62, "test_load_stream" ) {}
void runTest() { suite_test_zip_file.test_load_stream(); }
} testDescription_suite_test_zip_file_test_load_stream;
static class TestDescription_suite_test_zip_file_test_load_bytes : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_load_bytes() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 75, "test_load_bytes" ) {}
void runTest() { suite_test_zip_file.test_load_bytes(); }
} testDescription_suite_test_zip_file_test_load_bytes;
static class TestDescription_suite_test_zip_file_test_reset : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_reset() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 99, "test_reset" ) {}
void runTest() { suite_test_zip_file.test_reset(); }
} testDescription_suite_test_zip_file_test_reset;
static class TestDescription_suite_test_zip_file_test_getinfo : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_getinfo() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 128, "test_getinfo" ) {}
void runTest() { suite_test_zip_file.test_getinfo(); }
} testDescription_suite_test_zip_file_test_getinfo;
static class TestDescription_suite_test_zip_file_test_infolist : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_infolist() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 135, "test_infolist" ) {}
void runTest() { suite_test_zip_file.test_infolist(); }
} testDescription_suite_test_zip_file_test_infolist;
static class TestDescription_suite_test_zip_file_test_namelist : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_namelist() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 141, "test_namelist" ) {}
void runTest() { suite_test_zip_file.test_namelist(); }
} testDescription_suite_test_zip_file_test_namelist;
static class TestDescription_suite_test_zip_file_test_open_by_name : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_open_by_name() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 147, "test_open_by_name" ) {}
void runTest() { suite_test_zip_file.test_open_by_name(); }
} testDescription_suite_test_zip_file_test_open_by_name;
static class TestDescription_suite_test_zip_file_test_open_by_info : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_open_by_info() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 156, "test_open_by_info" ) {}
void runTest() { suite_test_zip_file.test_open_by_info(); }
} testDescription_suite_test_zip_file_test_open_by_info;
static class TestDescription_suite_test_zip_file_test_extract_current_directory : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_extract_current_directory() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 165, "test_extract_current_directory" ) {}
void runTest() { suite_test_zip_file.test_extract_current_directory(); }
} testDescription_suite_test_zip_file_test_extract_current_directory;
static class TestDescription_suite_test_zip_file_test_extract_path : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_extract_path() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 170, "test_extract_path" ) {}
void runTest() { suite_test_zip_file.test_extract_path(); }
} testDescription_suite_test_zip_file_test_extract_path;
static class TestDescription_suite_test_zip_file_test_extractall_current_directory : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_extractall_current_directory() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 175, "test_extractall_current_directory" ) {}
void runTest() { suite_test_zip_file.test_extractall_current_directory(); }
} testDescription_suite_test_zip_file_test_extractall_current_directory;
static class TestDescription_suite_test_zip_file_test_extractall_path : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_extractall_path() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 180, "test_extractall_path" ) {}
void runTest() { suite_test_zip_file.test_extractall_path(); }
} testDescription_suite_test_zip_file_test_extractall_path;
static class TestDescription_suite_test_zip_file_test_extractall_members_name : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_extractall_members_name() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 185, "test_extractall_members_name" ) {}
void runTest() { suite_test_zip_file.test_extractall_members_name(); }
} testDescription_suite_test_zip_file_test_extractall_members_name;
static class TestDescription_suite_test_zip_file_test_extractall_members_info : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_extractall_members_info() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 190, "test_extractall_members_info" ) {}
void runTest() { suite_test_zip_file.test_extractall_members_info(); }
} testDescription_suite_test_zip_file_test_extractall_members_info;
static class TestDescription_suite_test_zip_file_test_printdir : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_printdir() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 195, "test_printdir" ) {}
void runTest() { suite_test_zip_file.test_printdir(); }
} testDescription_suite_test_zip_file_test_printdir;
static class TestDescription_suite_test_zip_file_test_read : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_read() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 204, "test_read" ) {}
void runTest() { suite_test_zip_file.test_read(); }
} testDescription_suite_test_zip_file_test_read;
static class TestDescription_suite_test_zip_file_test_testzip : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_testzip() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 211, "test_testzip" ) {}
void runTest() { suite_test_zip_file.test_testzip(); }
} testDescription_suite_test_zip_file_test_testzip;
static class TestDescription_suite_test_zip_file_test_write : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_write() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 217, "test_write" ) {}
void runTest() { suite_test_zip_file.test_write(); }
} testDescription_suite_test_zip_file_test_write;
static class TestDescription_suite_test_zip_file_test_writestr : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_writestr() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 234, "test_writestr" ) {}
void runTest() { suite_test_zip_file.test_writestr(); }
} testDescription_suite_test_zip_file_test_writestr;
static class TestDescription_suite_test_zip_file_test_comment : public CxxTest::RealTestDescription {
public:
TestDescription_suite_test_zip_file_test_comment() : CxxTest::RealTestDescription( Tests_test_zip_file, suiteDescription_test_zip_file, 252, "test_comment" ) {}
void runTest() { suite_test_zip_file.test_comment(); }
} testDescription_suite_test_zip_file_test_comment;
#include <cxxtest/Root.cpp>
const char* CxxTest::RealWorldDescription::_worldName = "cxxtest";

View File

@ -12,8 +12,8 @@ class test_props : public CxxTest::TestSuite
public:
void test_read_properties_core()
{
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty.xlsx", xlnt::file_mode::open);
auto content = archive.get_file_contents("docProps/core.xml");
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty.xlsx");
auto content = archive.read("docProps/core.xml");
auto prop = xlnt::reader::read_properties_core(content);
TS_ASSERT_EQUALS(prop.creator, "*.*");
TS_ASSERT_EQUALS(prop.last_modified_by, "Charlie Clark");
@ -23,8 +23,8 @@ public:
void test_read_sheets_titles()
{
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty.xlsx", xlnt::file_mode::open);
auto content = archive.get_file_contents("docProps/core.xml");
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty.xlsx");
auto content = archive.read("docProps/core.xml");
std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
int i = 0;
for(auto sheet : xlnt::reader::read_sheets(archive))
@ -35,16 +35,16 @@ public:
void test_read_properties_core_libre()
{
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty_libre.xlsx", xlnt::file_mode::open);
auto content = archive.get_file_contents("docProps/core.xml");
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty_libre.xlsx");
auto content = archive.read("docProps/core.xml");
auto prop = xlnt::reader::read_properties_core(content);
TS_ASSERT_EQUALS(prop.excel_base_date, xlnt::calendar::windows_1900);
}
void test_read_sheets_titles_libre()
{
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty_libre.xlsx", xlnt::file_mode::open);
auto content = archive.get_file_contents("docProps/core.xml");
xlnt::zip_file archive(PathHelper::GetDataDirectory() + "/genuine/empty_libre.xlsx");
auto content = archive.read("docProps/core.xml");
std::vector<std::string> expected_titles = {"Sheet1 - Text", "Sheet2 - Numbers", "Sheet3 - Formulas", "Sheet4 - Dates"};
int i = 0;
for(auto sheet : xlnt::reader::read_sheets(archive))

View File

@ -303,7 +303,7 @@ public:
{
{
auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx");
xlnt::zip_file archive(path, xlnt::file_mode::open);
xlnt::zip_file archive(path);
std::vector<std::pair<std::string, std::string>> expected =
{
{"xl/worksheets/sheet1.xml", "Sheet1"}
@ -313,7 +313,7 @@ public:
{
auto path = PathHelper::GetDataDirectory("/reader/contains_chartsheets.xlsx");
xlnt::zip_file archive(path, xlnt::file_mode::open);
xlnt::zip_file archive(path);
std::vector<std::pair<std::string, std::string>> expected =
{
@ -326,7 +326,7 @@ public:
{
auto path = PathHelper::GetDataDirectory("/reader/bug304.xlsx");
xlnt::zip_file archive(path, xlnt::file_mode::open);
xlnt::zip_file archive(path);
std::vector<std::pair<std::string, std::string>> expected =
{
@ -352,7 +352,7 @@ public:
};
auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx");
xlnt::zip_file archive(path, xlnt::file_mode::open);
xlnt::zip_file archive(path);
TS_ASSERT_EQUALS(xlnt::reader::read_relationships(archive, "xl/workbook.xml"), expected);
}
@ -371,7 +371,7 @@ public:
};
auto path = PathHelper::GetDataDirectory("/reader/bug304.xlsx");
xlnt::zip_file archive(path, xlnt::file_mode::open);
xlnt::zip_file archive(path);
TS_ASSERT_EQUALS(xlnt::reader::read_relationships(archive, "xl/workbook.xml"), expected);
}
@ -398,7 +398,7 @@ public:
};
auto path = PathHelper::GetDataDirectory("/reader/contains_chartsheets.xlsx");
xlnt::zip_file f(path, xlnt::file_mode::open);
xlnt::zip_file f(path);
auto result = xlnt::reader::read_content_types(f);
if(result.size() != expected.size())
@ -417,7 +417,7 @@ public:
{
{
auto path = PathHelper::GetDataDirectory("/reader/bug137.xlsx");
xlnt::zip_file f(path, xlnt::file_mode::open);
xlnt::zip_file f(path);
auto sheets = xlnt::reader::read_sheets(f);
std::vector<std::pair<std::string, std::string>> expected =
{{"rId1", "Chart1"}, {"rId2", "Sheet1"}};
@ -426,7 +426,7 @@ public:
{
auto path = PathHelper::GetDataDirectory("/reader/bug304.xlsx");
xlnt::zip_file f(path, xlnt::file_mode::open);
xlnt::zip_file f(path);
auto sheets = xlnt::reader::read_sheets(f);
std::vector<std::pair<std::string, std::string>> expected =
{{"rId1", "Sheet1"}, {"rId2", "Sheet2"}, {"rId3", "Sheet3"}};

272
tests/test_zip_file.hpp Normal file
View File

@ -0,0 +1,272 @@
#include <cassert>
#include <fstream>
#include <cxxtest/TestSuite.h>
#include <xlnt/xlnt.hpp>
#include "helpers/path_helper.hpp"
#include "helpers/temporary_file.hpp"
class test_zip_file : public CxxTest::TestSuite
{
public:
test_zip_file()
{
existing_file = PathHelper::GetDataDirectory("/genuine/empty.xlsx");
expected_content_types_string = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"xml\" ContentType=\"application/xml\"/><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Override PartName=\"/xl/workbook.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml\"/><Override PartName=\"/xl/worksheets/sheet1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/xl/worksheets/sheet2.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/xl/worksheets/sheet3.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/xl/worksheets/sheet4.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml\"/><Override PartName=\"/xl/theme/theme1.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.theme+xml\"/><Override PartName=\"/xl/styles.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml\"/><Override PartName=\"/xl/sharedStrings.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml\"/><Override PartName=\"/xl/calcChain.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml\"/><Override PartName=\"/docProps/core.xml\" ContentType=\"application/vnd.openxmlformats-package.core-properties+xml\"/><Override PartName=\"/docProps/app.xml\" ContentType=\"application/vnd.openxmlformats-officedocument.extended-properties+xml\"/></Types>";
expected_atxt_string = "<?xml version=\"1.0\" ?>\r\n<sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"3\" uniqueCount=\"2\"><si><t>This is cell A1 in Sheet 1</t></si><si><t>This is cell G5</t></si></sst>";
expected_printdir_string = " Length Date Time Name\n--------- ---------- ----- ----\n 1704 01/01/1980 00:00 [Content_Types].xml\n 588 01/01/1980 00:00 _rels/.rels\n 1254 01/01/1980 00:00 xl/_rels/workbook.xml.rels\n 898 01/01/1980 00:00 xl/workbook.xml\n 1231 01/01/1980 00:00 xl/worksheets/sheet4.xml\n 4427 01/01/1980 00:00 xl/worksheets/sheet2.xml\n 1032 01/01/1980 00:00 xl/worksheets/sheet3.xml\n 1026 01/01/1980 00:00 xl/worksheets/sheet1.xml\n 6995 01/01/1980 00:00 xl/theme/theme1.xml\n 233 01/01/1980 00:00 xl/sharedStrings.xml\n 1724 01/01/1980 00:00 xl/styles.xml\n 169 01/01/1980 00:00 xl/calcChain.xml\n 917 01/01/1980 00:00 docProps/app.xml\n 609 01/01/1980 00:00 docProps/core.xml\n--------- -------\n 22807 14 files\n";
}
void remove_temp_file()
{
std::remove(temp_file.GetFilename().c_str());
}
void make_temp_directory()
{
}
void remove_temp_directory()
{
}
bool files_equal(const std::string &a, const std::string &b)
{
if(a == b)
{
return true;
}
std::ifstream stream_a(a, std::ios::binary), stream_b(a, std::ios::binary);
while(stream_a && stream_b)
{
if(stream_a.get() != stream_b.get())
{
return false;
}
}
return true;
}
void test_load_file()
{
remove_temp_file();
xlnt::zip_file f(existing_file);
f.save(temp_file.GetFilename());
assert(files_equal(existing_file, temp_file.GetFilename()));
remove_temp_file();
}
void test_load_stream()
{
remove_temp_file();
{
std::ifstream in_stream(existing_file, std::ios::binary);
xlnt::zip_file f(in_stream);
std::ofstream out_stream(temp_file.GetFilename(), std::ios::binary);
f.save(out_stream);
}
assert(files_equal(existing_file, temp_file.GetFilename()));
remove_temp_file();
}
void test_load_bytes()
{
remove_temp_file();
xlnt::zip_file f;
std::vector<unsigned char> source_bytes, result_bytes;
std::ifstream in_stream(existing_file, std::ios::binary);
while(in_stream)
{
source_bytes.push_back((unsigned char)in_stream.get());
}
f.load(source_bytes);
f.save(temp_file.GetFilename());
xlnt::zip_file f2;
f2.load(temp_file.GetFilename());
result_bytes = std::vector<unsigned char>();
f2.save(result_bytes);
assert(source_bytes == result_bytes);
remove_temp_file();
}
void test_reset()
{
xlnt::zip_file f(existing_file);
assert(!f.namelist().empty());
try
{
f.read("[Content_Types].xml");
}
catch(std::exception e)
{
assert(false);
}
f.reset();
assert(f.namelist().empty());
try
{
f.read("[Content_Types].xml");
assert(false);
}
catch(std::exception e)
{
}
}
void test_getinfo()
{
xlnt::zip_file f(existing_file);
auto info = f.getinfo("[Content_Types].xml");
assert(info.filename == "[Content_Types].xml");
}
void test_infolist()
{
xlnt::zip_file f(existing_file);
assert(f.infolist().size() == 14);
}
void test_namelist()
{
xlnt::zip_file f(existing_file);
assert(f.namelist().size() == 14);
}
void test_open_by_name()
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
ss << f.open("[Content_Types].xml").rdbuf();
std::string result = ss.str();
assert(result == expected_content_types_string);
}
void test_open_by_info()
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
ss << f.open("[Content_Types].xml").rdbuf();
std::string result = ss.str();
assert(result == expected_content_types_string);
}
void test_extract_current_directory()
{
xlnt::zip_file f(existing_file);
}
void test_extract_path()
{
xlnt::zip_file f(existing_file);
}
void test_extractall_current_directory()
{
xlnt::zip_file f(existing_file);
}
void test_extractall_path()
{
xlnt::zip_file f(existing_file);
}
void test_extractall_members_name()
{
xlnt::zip_file f(existing_file);
}
void test_extractall_members_info()
{
xlnt::zip_file f(existing_file);
}
void test_printdir()
{
xlnt::zip_file f(existing_file);
std::stringstream ss;
f.printdir(ss);
auto printed = ss.str();
assert(printed == expected_printdir_string);
}
void test_read()
{
xlnt::zip_file f(existing_file);
assert(f.read("[Content_Types].xml") == expected_content_types_string);
assert(f.read(f.getinfo("[Content_Types].xml")) == expected_content_types_string);
}
void test_testzip()
{
xlnt::zip_file f(existing_file);
assert(f.testzip().first);
}
void test_write()
{
remove_temp_file();
xlnt::zip_file f;
auto text_file = PathHelper::GetDataDirectory("/reader/sharedStrings.xml");
f.write(text_file);
f.write(text_file, "sharedStrings2.xml");
f.save(temp_file.GetFilename());
xlnt::zip_file f2(temp_file.GetFilename());
assert(f2.read(text_file) == expected_atxt_string);
assert(f2.read("sharedStrings2.xml") == expected_atxt_string);
remove_temp_file();
}
void test_writestr()
{
remove_temp_file();
xlnt::zip_file f;
f.writestr("a.txt", "a\na");
xlnt::zip_info info;
info.filename = "b.txt";
f.writestr(info, "b\nb");
f.save(temp_file.GetFilename());
xlnt::zip_file f2(temp_file.GetFilename());
assert(f2.read("a.txt") == "a\na");
assert(f2.read(f2.getinfo("b.txt")) == "b\nb");
remove_temp_file();
}
void test_comment()
{
remove_temp_file();
xlnt::zip_file f;
f.comment = "comment";
f.save(temp_file.GetFilename());
xlnt::zip_file f2(temp_file.GetFilename());
assert(f2.comment == "comment");
remove_temp_file();
}
private:
TemporaryFile temp_file;
std::string existing_file;
std::string expected_content_types_string;
std::string expected_atxt_string;
std::string expected_printdir_string;
};