From e0ac10e045b6d932c221c9223d88940b14e12b8b Mon Sep 17 00:00:00 2001 From: Kimball Thurston Date: Mon, 12 Aug 2019 23:50:14 +1200 Subject: [PATCH] Add mechanism for test programs to use win32 wide filename fix when manually creating std::fstreams Signed-off-by: Kimball Thurston --- OpenEXR/IlmImfFuzzTest/fuzzFile.cpp | 13 +- OpenEXR/IlmImfTest/TestUtilFStream.h | 135 ++++++++++++++++++ .../IlmImfTest/testBackwardCompatibility.cpp | 6 +- OpenEXR/IlmImfTest/testExistingStreams.cpp | 55 ++++--- OpenEXR/IlmImfTest/testMagic.cpp | 5 +- OpenEXR/IlmImfTest/testPreviewImage.cpp | 32 +++-- 6 files changed, 206 insertions(+), 40 deletions(-) create mode 100644 OpenEXR/IlmImfTest/TestUtilFStream.h diff --git a/OpenEXR/IlmImfFuzzTest/fuzzFile.cpp b/OpenEXR/IlmImfFuzzTest/fuzzFile.cpp index e7d0ff027f..6ad2f79eca 100644 --- a/OpenEXR/IlmImfFuzzTest/fuzzFile.cpp +++ b/OpenEXR/IlmImfFuzzTest/fuzzFile.cpp @@ -45,6 +45,7 @@ #include #include +#include "../IlmImfTest/TestUtilFStream.h" // Handle the case when the custom namespace is not exposed #include @@ -58,7 +59,9 @@ namespace { Int64 lengthOfFile (const char fileName[]) { - ifstream ifs (fileName, ios_base::binary); + ifstream ifs; + testutil::OpenStreamWithUTF8Name ( + ifs, fileName, ios::in | ios_base::binary); if (!ifs) return 0; @@ -80,7 +83,9 @@ fuzzFile (const char goodFile[], // Read the input file. // - ifstream ifs (goodFile, ios_base::binary); + ifstream ifs; + testutil::OpenStreamWithUTF8Name ( + ifs, goodFile, ios::in | ios_base::binary); if (!ifs) THROW_ERRNO ("Cannot open file " << goodFile << " (%T)."); @@ -110,7 +115,9 @@ fuzzFile (const char goodFile[], // Save the damaged file contents in the output file. // - ofstream ofs (brokenFile, ios_base::binary); + ofstream ofs; + testutil::OpenStreamWithUTF8Name ( + ofs, brokenFile, ios::out | ios_base::binary); if (!ofs) THROW_ERRNO ("Cannot open file " << brokenFile << " (%T)." << endl); diff --git a/OpenEXR/IlmImfTest/TestUtilFStream.h b/OpenEXR/IlmImfTest/TestUtilFStream.h new file mode 100644 index 0000000000..54af5e292d --- /dev/null +++ b/OpenEXR/IlmImfTest/TestUtilFStream.h @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: BSD-3-Clause +// Copyright Contributors to the OpenEXR Project. + +#pragma once + +#ifndef INCLUDE_TestUtilFStream_h_ +#define INCLUDE_TestUtilFStream_h_ 1 + +#include +#include + +#ifdef _WIN32 +# define VC_EXTRALEAN +# include +# include +# include +# include +# include +# include +# include +#endif + +namespace testutil +{ +#ifdef _WIN32 +inline std::wstring +WidenFilename (const char* filename) +{ + std::wstring ret; + int fnlen = static_cast (strlen (filename)); + int len = MultiByteToWideChar (CP_UTF8, 0, filename, fnlen, NULL, 0); + if (len > 0) + { + ret.resize (len); + MultiByteToWideChar (CP_UTF8, 0, filename, fnlen, &ret[0], len); + } + return ret; +} + +// This is a big work around mechanism for compiling using mingw / gcc under windows +// until mingw 9 where they add the wide filename version of open +# if (defined(__GLIBCXX__) && !(defined(_GLIBCXX_HAVE_WFOPEN) && defined(_GLIBCXX_USE_WCHAR_T))) +# define USE_WIDEN_FILEBUF 1 +template +class WidenFilebuf : public std::basic_filebuf +{ + inline int mode_to_flags (std::ios_base::openmode mode) + { + int flags = 0; + if (mode & std::ios_base::in) flags |= _O_RDONLY; + if (mode & std::ios_base::out) + { + flags |= _O_WRONLY; + flags |= _O_CREAT; + if (mode & std::ios_base::app) flags |= _O_APPEND; + if (mode & std::ios_base::trunc) flags |= _O_TRUNC; + } + if (mode & std::ios_base::binary) + flags |= _O_BINARY; + else + flags |= _O_TEXT; + return flags; + } + +public: + using base_filebuf = std::basic_filebuf; + inline base_filebuf* wide_open (std::wstring& fn, std::ios_base::openmode m) + { + if (this->is_open () || fn.empty ()) + return nullptr; + + int fd; + errno_t e = _wsopen_s ( + &fd, + fn.c_str (), + mode_to_flags (m), + _SH_DENYNO, + _S_IREAD | _S_IWRITE); + if (e != 0) + return nullptr; + + // sys_open will do an fdopen internally which will then clean up the fd upon close + this->_M_file.sys_open (fd, m); + if (this->is_open ()) + { + // reset the internal state, these members are consistent between gcc versions 4.3 - 9 + // but at 9, the wfopen stuff should become available, such that this will no longer be + // active + this->_M_allocate_internal_buffer (); + this->_M_mode = m; + this->_M_reading = false; + this->_M_writing = false; + this->_M_set_buffer (-1); + this->_M_state_last = this->_M_state_cur = this->_M_state_beg; + + if ((m & std::ios_base::ate) && + this->seekoff (0, std::ios_base::end, m) == + static_cast (-1)) + { + this->close (); + return nullptr; + } + } + return this; + } +}; +# endif // __GLIBCXX__ +#endif // _WIN32 + +template +inline void +OpenStreamWithUTF8Name ( + StreamType& is, const char* filename, std::ios_base::openmode mode) +{ +#ifdef _WIN32 + std::wstring wfn = WidenFilename (filename); +# ifdef USE_WIDEN_FILEBUF + using CharT = typename StreamType::char_type; + using TraitsT = typename StreamType::traits_type; + using wbuf = WidenFilebuf; + if (!static_cast (is.rdbuf ())->wide_open (wfn, mode)) + is.setstate (std::ios_base::failbit); + else + is.clear (); +# else + is.rdbuf ()->open (wfn.c_str (), mode); +# endif +#else + is.rdbuf ()->open (filename, mode); +#endif +} + +} // namespace testutil + +#endif // INCLUDE_TestUtilFStream_h_ diff --git a/OpenEXR/IlmImfTest/testBackwardCompatibility.cpp b/OpenEXR/IlmImfTest/testBackwardCompatibility.cpp index 9d4b921c9b..69100f7be7 100644 --- a/OpenEXR/IlmImfTest/testBackwardCompatibility.cpp +++ b/OpenEXR/IlmImfTest/testBackwardCompatibility.cpp @@ -37,6 +37,7 @@ #endif #include "testBackwardCompatibility.h" +#include "TestUtilFStream.h" #include #include @@ -104,8 +105,9 @@ const int H = 197; void diffImageFiles (const char * fn1, const char * fn2) { - ifstream i1 (fn1, ios::binary); - ifstream i2 (fn2, ios::binary); + ifstream i1, i2; + testutil::OpenStreamWithUTF8Name (i1, fn1, ios::in | ios::binary); + testutil::OpenStreamWithUTF8Name (i2, fn2, ios::in | ios::binary); if(!i1.good()){THROW (IEX_NAMESPACE::BaseExc, string("cannot open ") + string(fn1));} if(!i2.good()){THROW (IEX_NAMESPACE::BaseExc, string("cannot open ") + string(fn2));} diff --git a/OpenEXR/IlmImfTest/testExistingStreams.cpp b/OpenEXR/IlmImfTest/testExistingStreams.cpp index 67fc7422ac..a27ae90998 100644 --- a/OpenEXR/IlmImfTest/testExistingStreams.cpp +++ b/OpenEXR/IlmImfTest/testExistingStreams.cpp @@ -54,6 +54,7 @@ #include #include +#include "TestUtilFStream.h" using namespace OPENEXR_IMF_NAMESPACE; using namespace std; @@ -139,7 +140,9 @@ MMIFStream::MMIFStream (const char fileName[]): _length (0), _pos (0) { - std::ifstream ifs (fileName, ios_base::binary); + std::ifstream ifs; + testutil::OpenStreamWithUTF8Name ( + ifs, fileName, ios::in | ios_base::binary); // // Get length of file @@ -229,19 +232,23 @@ writeReadScanLines (const char fileName[], { cout << "writing"; - remove (fileName); - std::ofstream os (fileName, ios_base::binary); - StdOFStream ofs (os, fileName); - RgbaOutputFile out (ofs, header, WRITE_RGBA); - out.setFrameBuffer (&p1[0][0], 1, width); - out.writePixels (height); + remove (fileName); + std::ofstream os; + testutil::OpenStreamWithUTF8Name ( + os, fileName, ios::out | ios_base::binary); + StdOFStream ofs (os, fileName); + RgbaOutputFile out (ofs, header, WRITE_RGBA); + out.setFrameBuffer (&p1[0][0], 1, width); + out.writePixels (height); } { cout << ", reading"; - std::ifstream is (fileName, ios_base::binary); - StdIFStream ifs (is, fileName); - RgbaInputFile in (ifs); + std::ifstream is; + testutil::OpenStreamWithUTF8Name ( + is, fileName, ios::in | ios_base::binary); + StdIFStream ifs (is, fileName); + RgbaInputFile in (ifs); const Box2i &dw = in.dataWindow(); int w = dw.max.x - dw.min.x + 1; @@ -333,7 +340,9 @@ writeReadMultiPart (const char fileName[], { cout << "writing"; remove (fileName); - std::ofstream os (fileName, ios_base::binary); + std::ofstream os; + testutil::OpenStreamWithUTF8Name ( + os, fileName, ios::out | ios_base::binary); StdOFStream ofs (os, fileName); MultiPartOutputFile out (ofs, &headers[0],2); FrameBuffer f; @@ -352,7 +361,9 @@ writeReadMultiPart (const char fileName[], { cout << ", reading"; - std::ifstream is (fileName, ios_base::binary); + std::ifstream is; + testutil::OpenStreamWithUTF8Name ( + is, fileName, ios::in | ios_base::binary); StdIFStream ifs (is, fileName); MultiPartInputFile in (ifs); @@ -464,19 +475,23 @@ writeReadTiles (const char fileName[], { cout << "writing"; - remove (fileName); - std::ofstream os (fileName, ios_base::binary); - StdOFStream ofs (os, fileName); - TiledRgbaOutputFile out (ofs, header, WRITE_RGBA, 20, 20, ONE_LEVEL); - out.setFrameBuffer (&p1[0][0], 1, width); + remove (fileName); + std::ofstream os; + testutil::OpenStreamWithUTF8Name ( + os, fileName, ios_base::out | ios_base::binary); + StdOFStream ofs (os, fileName); + TiledRgbaOutputFile out (ofs, header, WRITE_RGBA, 20, 20, ONE_LEVEL); + out.setFrameBuffer (&p1[0][0], 1, width); out.writeTiles (0, out.numXTiles() - 1, 0, out.numYTiles() - 1); } { cout << ", reading"; - std::ifstream is (fileName, ios_base::binary); - StdIFStream ifs (is, fileName); - TiledRgbaInputFile in (ifs); + std::ifstream is; + testutil::OpenStreamWithUTF8Name ( + is, fileName, ios::in | ios_base::binary); + StdIFStream ifs (is, fileName); + TiledRgbaInputFile in (ifs); const Box2i &dw = in.dataWindow(); int w = dw.max.x - dw.min.x + 1; diff --git a/OpenEXR/IlmImfTest/testMagic.cpp b/OpenEXR/IlmImfTest/testMagic.cpp index 1d95c091bd..fb4b26fe1d 100644 --- a/OpenEXR/IlmImfTest/testMagic.cpp +++ b/OpenEXR/IlmImfTest/testMagic.cpp @@ -44,6 +44,8 @@ #include #include +#include "TestUtilFStream.h" + #ifndef ILM_IMF_TEST_IMAGEDIR #define ILM_IMF_TEST_IMAGEDIR #endif @@ -60,7 +62,8 @@ testFile1 (const char fileName[], bool isImfFile) { cout << fileName << " " << flush; - ifstream f (fileName, ios_base::binary); + ifstream f; + testutil::OpenStreamWithUTF8Name (f, fileName, ios::in | ios_base::binary); assert (!!f); char bytes[4]; diff --git a/OpenEXR/IlmImfTest/testPreviewImage.cpp b/OpenEXR/IlmImfTest/testPreviewImage.cpp index ad4fae9dc0..5b32c50102 100644 --- a/OpenEXR/IlmImfTest/testPreviewImage.cpp +++ b/OpenEXR/IlmImfTest/testPreviewImage.cpp @@ -42,6 +42,7 @@ #include #include #include +#include "TestUtilFStream.h" #ifndef ILM_IMF_TEST_IMAGEDIR #define ILM_IMF_TEST_IMAGEDIR @@ -179,20 +180,23 @@ readWriteFiles (const char fileName1[], cout << "comparing files " << fileName2 << " and " << fileName3 << endl; { - ifstream file2 (fileName2, std::ios_base::binary); - ifstream file3 (fileName3, std::ios_base::binary); - - while (true) - { - int c2 = file2.get(); - int c3 = file3.get(); - - if (file2.eof()) - break; - - assert (c2 == c3); - assert (!!file2 && !!file3); - } + ifstream file2, file3; + testutil::OpenStreamWithUTF8Name ( + file2, fileName2, std::ios_base::in | std::ios_base::binary); + testutil::OpenStreamWithUTF8Name ( + file3, fileName3, std::ios_base::in | std::ios_base::binary); + + while (true) + { + int c2 = file2.get(); + int c3 = file3.get(); + + if (file2.eof()) + break; + + assert (c2 == c3); + assert (!!file2 && !!file3); + } } remove (fileName2);