mirror of
https://github.com/troldal/OpenXLSX.git
synced 2025-05-09 02:11:09 +08:00
2025-05-04 XLDocument: remove a dependency on boost::nowide
This commit is contained in:
parent
e460477e74
commit
3ed972e1e0
119
OpenXLSX/headers/detail/OpenXLSXFileSystemTools.hpp
Normal file
119
OpenXLSX/headers/detail/OpenXLSXFileSystemTools.hpp
Normal file
@ -0,0 +1,119 @@
|
||||
#ifndef OPENXLSX_TOOLS_H
|
||||
#define OPENXLSX_TOOLS_H
|
||||
|
||||
#include <random> // std::random_device, std::mt19937, std::uniform_int_distribution
|
||||
#include <string> // std::string
|
||||
#include <sys/stat.h> // for stat, to test if a file exists and whether a file is a directory
|
||||
|
||||
// don't use "stat" directly because windows has compatibility-breaking defines
|
||||
#if defined(_WIN32) // moved below includes to make it absolutely clear that this is module-local
|
||||
# define STAT _stat // _stat should be available in standard environment on Windows
|
||||
# define STATSTRUCT struct _stat // struct _stat also exists - split the two names in case the struct _stat must not be used on windows
|
||||
#else
|
||||
# define STAT stat
|
||||
# define STATSTRUCT struct stat
|
||||
#endif
|
||||
|
||||
namespace OpenXLSX
|
||||
{
|
||||
/* 2025-05-04: moved a set of file system related utility functions to this new header file so that Unicode support on windows (nowide)
|
||||
* can be implemented in a central location */
|
||||
|
||||
/**
|
||||
* @brief Test if path exists as either a file or a directory
|
||||
* @param path Check for existence of this
|
||||
* @return true if path exists as a file or directory
|
||||
*/
|
||||
inline bool pathExists(const std::string& path)
|
||||
{
|
||||
STATSTRUCT info;
|
||||
if (STAT(path.c_str(), &info ) == 0) // test if path exists
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#ifdef __GNUC__ // conditionally enable GCC specific pragmas to suppress unused function warning
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif // __GNUC__
|
||||
/**
|
||||
* @brief Test if fileName exists and is not a directory
|
||||
* @param fileName The path to check for existence (as a file)
|
||||
* @return true if fileName exists and is a file, otherwise false
|
||||
*/
|
||||
inline bool fileExists(const std::string& fileName)
|
||||
{
|
||||
STATSTRUCT info;
|
||||
if (STAT(fileName.c_str(), &info ) == 0) // test if path exists
|
||||
if ((info.st_mode & S_IFDIR) == 0) // test if it is NOT a directory
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
inline bool isDirectory(const std::string& fileName)
|
||||
{
|
||||
STATSTRUCT info;
|
||||
if (STAT(fileName.c_str(), &info ) == 0) // test if path exists
|
||||
if ((info.st_mode & S_IFDIR) != 0) // test if it is a directory
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#ifdef __GNUC__ // conditionally enable GCC specific pragmas to suppress unused function warning
|
||||
# pragma GCC diagnostic pop
|
||||
#endif // __GNUC__
|
||||
|
||||
/**
|
||||
* @brief Generates a random filename, which is used to generate a temporary archive when modifying and saving
|
||||
* archive files.
|
||||
* @param length The length of the filename to create.
|
||||
* @return Returns the generated filename, appended with '.tmp'.
|
||||
*/
|
||||
inline std::string GenerateRandomName(int length)
|
||||
{
|
||||
std::string letters = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
std::random_device rand_dev;
|
||||
std::mt19937 generator(rand_dev());
|
||||
std::uniform_int_distribution<int> distr(0, letters.size() - 1);
|
||||
|
||||
std::string result;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
result += letters[distr(generator)];
|
||||
}
|
||||
|
||||
return result + ".tmp";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates a random filename with a call to GenerateRandomName, prepending the same path in which filename is located
|
||||
* @param filename determine "same path" from this file
|
||||
* @param length The length of the filename to create.
|
||||
* @return Returns the generated random filename in the same path as filename, appended with '.tmp'.
|
||||
* @note this function accounts for amigaos and windows specific drive / directory separators
|
||||
*/
|
||||
inline std::string GenerateRandomNameInSamePath(std::string filename, int length)
|
||||
{
|
||||
if (filename.empty()) filename = "./"; // local folder
|
||||
|
||||
# ifdef _WIN32
|
||||
std::replace( filename.begin(), filename.end(), '\\', '/' ); // pull request #210, alternate fix: fopen etc work fine with forward slashes
|
||||
# endif
|
||||
|
||||
// ===== Determine path of the current file
|
||||
size_t pathPos = filename.rfind('/');
|
||||
|
||||
// pull request #191, support AmigaOS style paths
|
||||
# ifdef __amigaos__
|
||||
constexpr const char * localFolder = ""; // local folder on AmigaOS can not be explicitly expressed in a path
|
||||
if (pathPos == std::string::npos) pathPos = filename.rfind(':'); // if no '/' found, attempt to find amiga drive root path
|
||||
# else
|
||||
constexpr const char * localFolder = "./"; // local folder on _WIN32 && __linux__ is .
|
||||
# endif
|
||||
std::string tempPath{};
|
||||
if (pathPos != std::string::npos) tempPath = filename.substr(0, pathPos + 1);
|
||||
else tempPath = localFolder; // prepend explicit identification of local folder in case path did not contain a folder
|
||||
|
||||
// ===== Generate a random file name with the same path as the current file
|
||||
return tempPath + GenerateRandomName(length);
|
||||
}
|
||||
} // namespace OpenXLSX
|
||||
|
||||
#endif // OPENXLSX_TOOLS_H
|
@ -15,6 +15,8 @@
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#include "detail/OpenXLSXFileSystemTools.hpp" // OpenXLSX::GenerateRandomNameInSamePath
|
||||
|
||||
// 2024-09-15: moved Zippy exceptions to top of module to be able to use them earlier - forward declaration didn't seem
|
||||
// to work
|
||||
namespace Zippy
|
||||
@ -59,31 +61,6 @@ namespace Zippy
|
||||
|
||||
} // namespace Zippy
|
||||
|
||||
namespace Zippy::Impl
|
||||
{
|
||||
/**
|
||||
* @brief Generates a random filename, which is used to generate a temporary archive when modifying and saving
|
||||
* archive files.
|
||||
* @param length The length of the filename to create.
|
||||
* @return Returns the generated filenamen, appended with '.tmp'.
|
||||
*/
|
||||
inline std::string GenerateRandomName(int length)
|
||||
{
|
||||
std::string letters = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
|
||||
std::random_device rand_dev;
|
||||
std::mt19937 generator(rand_dev());
|
||||
std::uniform_int_distribution<int> distr(0, letters.size() - 1);
|
||||
|
||||
std::string result;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
result += letters[distr(generator)];
|
||||
}
|
||||
|
||||
return result + ".tmp";
|
||||
}
|
||||
|
||||
} // namespace Zippy::Impl
|
||||
|
||||
namespace Zippy
|
||||
{
|
||||
@ -1073,26 +1050,8 @@ namespace Zippy
|
||||
if (filename.empty()) {
|
||||
filename = m_ArchivePath;
|
||||
}
|
||||
# ifdef _WIN32
|
||||
std::replace( filename.begin(), filename.end(), '\\', '/' ); // pull request #210, alternate fix: fopen etc work fine with forward slashes
|
||||
# endif
|
||||
|
||||
// ===== Determine path of the current file
|
||||
size_t pathPos = filename.rfind('/');
|
||||
|
||||
// pull request #191, support AmigaOS style paths
|
||||
# ifdef __amigaos__
|
||||
constexpr const char * localFolder = ""; // local folder on AmigaOS can not be explicitly expressed in a path
|
||||
if (pathPos == std::string::npos) pathPos = filename.rfind(':'); // if no '/' found, attempt to find amiga drive root path
|
||||
# else
|
||||
constexpr const char * localFolder = "./"; // local folder on _WIN32 && __linux__ is .
|
||||
# endif
|
||||
std::string tempPath{};
|
||||
if (pathPos != std::string::npos) tempPath = filename.substr(0, pathPos + 1);
|
||||
else tempPath = localFolder; // prepend explicit identification of local folder in case path did not contain a folder
|
||||
|
||||
// ===== Generate a random file name with the same path as the current file
|
||||
tempPath = tempPath + Impl::GenerateRandomName(20);
|
||||
std::string tempPath = OpenXLSX::GenerateRandomNameInSamePath(filename, 20);
|
||||
|
||||
// ===== Prepare an temporary archive file with the random filename;
|
||||
mz_zip_archive tempArchive = mz_zip_archive();
|
||||
|
@ -45,14 +45,11 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM.
|
||||
|
||||
// ===== External Includes ===== //
|
||||
#include <algorithm>
|
||||
#ifdef ENABLE_NOWIDE
|
||||
# include <nowide/fstream.hpp>
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
# include <random>
|
||||
#endif
|
||||
#include <pugixml.hpp>
|
||||
#include <sys/stat.h> // for stat, to test if a file exists and if a file is a directory
|
||||
#include <unistd.h> // unlink
|
||||
#include <vector> // std::vector
|
||||
|
||||
// ===== OpenXLSX Includes ===== //
|
||||
@ -60,17 +57,9 @@ YM M9 MM MM MM MM MM d' `MM. MM MM d' `MM.
|
||||
#include "XLDocument.hpp"
|
||||
#include "XLSheet.hpp"
|
||||
#include "XLStyles.hpp"
|
||||
#include "detail/OpenXLSXFileSystemTools.hpp" // pathExists, GenerateRandomNameInSamePath
|
||||
#include "utilities/XLUtilities.hpp"
|
||||
|
||||
// don't use "stat" directly because windows has compatibility-breaking defines
|
||||
#if defined(_WIN32) // moved below includes to make it absolutely clear that this is module-local
|
||||
# define STAT _stat // _stat should be available in standard environment on Windows
|
||||
# define STATSTRUCT struct _stat // struct _stat also exists - split the two names in case the struct _stat must not be used on windows
|
||||
#else
|
||||
# define STAT stat
|
||||
# define STATSTRUCT struct stat
|
||||
#endif
|
||||
|
||||
using namespace OpenXLSX;
|
||||
|
||||
namespace
|
||||
@ -429,7 +418,6 @@ namespace
|
||||
0x6b, 0x62, 0x6f, 0x6f, 0x6b, 0x2e, 0x78, 0x6d, 0x6c, 0x2e, 0x72, 0x65, 0x6c, 0x73, 0x50, 0x4b, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0a, 0x00, 0x0a, 0x00, 0x80, 0x02, 0x00, 0x00, 0x8c, 0x1b, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
XLDocument::XLDocument(const IZipArchive& zipArchive) : m_xmlSavingDeclaration{}, m_archive(zipArchive) {}
|
||||
@ -623,48 +611,6 @@ void XLDocument::open(const std::string& fileName)
|
||||
m_styles = XLStyles(getXmlData("xl/styles.xml"), m_suppressWarnings); // 2024-10-14: forward supress warnings setting to XLStyles
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**
|
||||
* @brief Test if path exists as either a file or a directory
|
||||
* @param path Check for existence of this
|
||||
* @return true if path exists as a file or directory
|
||||
*/
|
||||
bool pathExists(const std::string& path)
|
||||
{
|
||||
STATSTRUCT info;
|
||||
if (STAT(path.c_str(), &info ) == 0) // test if path exists
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#ifdef __GNUC__ // conditionally enable GCC specific pragmas to suppress unused function warning
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif // __GNUC__
|
||||
/**
|
||||
* @brief Test if fileName exists and is not a directory
|
||||
* @param fileName The path to check for existence (as a file)
|
||||
* @return true if fileName exists and is a file, otherwise false
|
||||
*/
|
||||
bool fileExists(const std::string& fileName)
|
||||
{
|
||||
STATSTRUCT info;
|
||||
if (STAT(fileName.c_str(), &info ) == 0) // test if path exists
|
||||
if ((info.st_mode & S_IFDIR) == 0) // test if it is NOT a directory
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool isDirectory(const std::string& fileName)
|
||||
{
|
||||
STATSTRUCT info;
|
||||
if (STAT(fileName.c_str(), &info ) == 0) // test if path exists
|
||||
if ((info.st_mode & S_IFDIR) != 0) // test if it is a directory
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#ifdef __GNUC__ // conditionally enable GCC specific pragmas to suppress unused function warning
|
||||
# pragma GCC diagnostic pop
|
||||
#endif // __GNUC__
|
||||
} // anonymous namespace
|
||||
|
||||
/**
|
||||
* @details Create a new document. This is done by saving the data in XLTemplate.h in binary format.
|
||||
@ -672,24 +618,28 @@ namespace {
|
||||
void XLDocument::create(const std::string& fileName, bool forceOverwrite)
|
||||
{
|
||||
// 2024-07-26: prevent silent overwriting of existing files
|
||||
if (!forceOverwrite && pathExists(fileName)) {
|
||||
if (!forceOverwrite && pathExists(fileName)) { // 2025-05-04 TODO TBD: does pathExists even work with windows / unicode filenames?
|
||||
using namespace std::literals::string_literals;
|
||||
throw XLException("XLDocument::create: refusing to overwrite existing file "s + fileName);
|
||||
}
|
||||
|
||||
std::string tempFileName = GenerateRandomNameInSamePath(fileName, 20);
|
||||
|
||||
// ===== Create a temporary output file stream.
|
||||
#ifdef ENABLE_NOWIDE
|
||||
nowide::ofstream outfile(fileName, std::ios::binary);
|
||||
#else
|
||||
std::ofstream outfile(fileName, std::ios::binary);
|
||||
#endif
|
||||
std::ofstream outfile(tempFileName, std::ios::binary);
|
||||
|
||||
// ===== Stream the binary data for an empty workbook to the output file.
|
||||
// ===== Casting, in particular reinterpret_cast, is discouraged, but in this case it is unfortunately unavoidable.
|
||||
outfile.write(reinterpret_cast<const char*>(templateData), templateSize); // NOLINT
|
||||
outfile.close();
|
||||
|
||||
open(fileName);
|
||||
open(tempFileName); // open the template archive from the temporary file
|
||||
m_filePath = fileName; // re-configure the document file path to point to the desired fileName
|
||||
|
||||
// 2025-05-04: the created (empty) archive is no longer saved implicitly, to remove the XLDocument dependency on nowide::ofstream
|
||||
// Instead, OpenXLSX shall rely on the underlying zip implementation (Zippy.hpp or LibZip.hpp) to support Unicode filenames
|
||||
// NOTE: LibZip currently does not support Unicode filenames on Windows (no use of nowide), status of miniz is unknown
|
||||
unlink(tempFileName.c_str()); // delete the temporary file used for archive creation
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,6 +7,10 @@ Microsoft Excel® files, with the .xlsx format.
|
||||
|
||||
As the heading says - the latest "Release" that is shown on https://github.com/troldal/OpenXLSX/releases is from 2021-11-06, and severely outdated - please pull / download the latest SW version directly from the repository in its current state. Link for those that do not want to use ```git```: https://github.com/troldal/OpenXLSX/archive/refs/heads/master.zip
|
||||
|
||||
## (aral-matrix) 04 May 2025 - moved file system access functions (shared by zip implementations and XLDocument) to detail/OpenXLSXFileSystemTools.hpp
|
||||
* in preparation for localizing (if not removing) the boost::nowide dependency, the new header file ```detail/OpenXLSXFileSystemTools.hpp``` now comprises all functions where unicode (filename) support might be relevant, to be implemented centrally - currently on the To-Do list
|
||||
* ```XLDocument::create```: creating a new document no longer creates a file under that name until an explicit call of ```XLDocument::save``` or ```::saveAs```. This is to remove a dependency of XLDocument on boost::nowide on Windows.
|
||||
|
||||
## (aral-matrix) 01 May 2025 - replaced zippy implementation with the underlying miniz library (cmake) and added support for libzip (cmake, GNU make)
|
||||
* @troldal replaced the zippy implementation with a smaller zippy-wrapper for the actual dependency, the miniz library, and added the cmake support for automatically pulling in the dependency from the miniz repository
|
||||
* ```OpenXLSX/headers/detail/```: new headers ```LipZip.hpp``` (libzip wrapper) and ```Zippy.hpp``` (miniz wrapper)
|
||||
|
Loading…
x
Reference in New Issue
Block a user