From d696f0cfcb882f864ce8b3d72b164c345ceba7a1 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Sun, 7 May 2023 15:19:27 -0700 Subject: [PATCH] Bug 798885 - Accented character in folder name on Account Export (bis) Pass a boost::filesystem's c_str() rv to the ofstream constructor to keep libstdc++ from transcoding it back to UTF8 and creating a broken name or failing to match the directory name. Implemented in gnc-filepath-utils to avoid spreading the boost::filesystem dependency throughout the code base. See https://github.com/boostorg/filesystem/issues/181 for why other approaches don't work. --- gnucash/gnucash-commands.cpp | 3 ++- .../csv-exp/csv-transactions-export.cpp | 5 ++--- .../import-export/csv-exp/csv-tree-export.cpp | 3 ++- libgnucash/core-utils/gnc-filepath-utils.cpp | 7 +++++++ libgnucash/core-utils/gnc-filepath-utils.h | 19 ++++++++++++++++++- 5 files changed, 31 insertions(+), 6 deletions(-) diff --git a/gnucash/gnucash-commands.cpp b/gnucash/gnucash-commands.cpp index acbf9fb712..661a2b51c1 100644 --- a/gnucash/gnucash-commands.cpp +++ b/gnucash/gnucash-commands.cpp @@ -32,6 +32,7 @@ #include "gnucash-commands.hpp" #include "gnucash-core-app.hpp" +#include #include #include #include @@ -107,7 +108,7 @@ static inline void write_report_file (const char *html, const char* file) { if (!file || !html || !*html) return; - std::ofstream ofs{file}; + auto ofs{gnc_open_filestream(file)}; if (!ofs) { std::cerr << "Failed to open file " << file << " for writing\n"; diff --git a/gnucash/import-export/csv-exp/csv-transactions-export.cpp b/gnucash/import-export/csv-exp/csv-transactions-export.cpp index bec67baf95..a73b605cec 100644 --- a/gnucash/import-export/csv-exp/csv-transactions-export.cpp +++ b/gnucash/import-export/csv-exp/csv-transactions-export.cpp @@ -32,6 +32,7 @@ #include #include +#include #include "gnc-commodity.h" #include "gnc-ui-util.h" #include "Query.h" @@ -350,9 +351,6 @@ void csv_transactions_export (CsvExportInfo *info) ENTER(""); DEBUG("File name is : %s", info->file_name); - /* Open File for writing */ - auto ss{std::ofstream (info->file_name, std::ofstream::out)}; - StringVec headers; bool num_action = qof_book_use_split_action_for_num_field (gnc_get_current_book()); @@ -398,6 +396,7 @@ void csv_transactions_export (CsvExportInfo *info) }; /* Write header line */ + auto ss{gnc_open_filestream(info->file_name)}; info->failed = !gnc_csv_add_line (ss, headers, info->use_quotes, info->separator_str); /* Go through list of accounts */ diff --git a/gnucash/import-export/csv-exp/csv-tree-export.cpp b/gnucash/import-export/csv-exp/csv-tree-export.cpp index 9b8810ec9b..6def848937 100644 --- a/gnucash/import-export/csv-exp/csv-tree-export.cpp +++ b/gnucash/import-export/csv-exp/csv-tree-export.cpp @@ -32,6 +32,7 @@ #include #include +#include #include "gnc-commodity.h" #include "gnc-ui-util.h" #include "csv-tree-export.h" @@ -52,7 +53,7 @@ csv_tree_export (CsvExportInfo *info) DEBUG("File name is : %s", info->file_name); /* Open File for writing */ - auto ss{std::ofstream (info->file_name, std::ofstream::out)}; + auto ss{gnc_open_filestream(info->file_name)}; /* Header string */ StringVec headervec = { diff --git a/libgnucash/core-utils/gnc-filepath-utils.cpp b/libgnucash/core-utils/gnc-filepath-utils.cpp index 5de8c436d6..1fdf64942a 100644 --- a/libgnucash/core-utils/gnc-filepath-utils.cpp +++ b/libgnucash/core-utils/gnc-filepath-utils.cpp @@ -1347,4 +1347,11 @@ gboolean gnc_filename_is_datafile (const char *filename) std::regex_match (filename, datafile_regex); } +std::ofstream +gnc_open_filestream(const char* path) +{ + bfs::path bfs_path(path, cvt); + bfs_path.imbue(bfs_locale); + return std::ofstream(bfs_path.c_str()); +} /* =============================== END OF FILE ========================== */ diff --git a/libgnucash/core-utils/gnc-filepath-utils.h b/libgnucash/core-utils/gnc-filepath-utils.h index fd10e186be..761573139d 100644 --- a/libgnucash/core-utils/gnc-filepath-utils.h +++ b/libgnucash/core-utils/gnc-filepath-utils.h @@ -29,6 +29,8 @@ #ifndef GNC_FILEPATH_UTILS_H #define GNC_FILEPATH_UTILS_H +#include + #ifdef __cplusplus extern "C" { #endif @@ -199,7 +201,22 @@ gboolean gnc_filename_is_backup (const char *filename); gboolean gnc_filename_is_datafile (const char *filename); #ifdef __cplusplus -} +} //extern "C" + +#include + +/** Open std::ofstream from a UTF-8 encoded path. This is harder than + * it should because std::ofstream's constructor needs to be tricked + * into taking a wchar_t filename: Simply converting path to a + * wchar_t* with g_utf8_to_utf16() wouldn't compile. The workaround + * came from https://github.com/boostorg/filesystem/issues/181. As + * noted there passing the boost path directly to + * boost::filesystem::fstream doesn't work either. + * @param path UTF-8 path to the file + * @return a std::ofstream on the stack. + */ +std::ofstream gnc_open_filestream(const char *path); + #endif #endif /* GNC_FILEPATH_UTILS_H */