0029081: With Mingw-w64 Unicode Paths Do Not Work
authorkgv <kgv@opencascade.com>
Wed, 6 Sep 2017 08:14:53 +0000 (11:14 +0300)
committerabv <abv@opencascade.com>
Fri, 6 Oct 2017 17:49:14 +0000 (20:49 +0300)
OSD_OpenStream() now uses __gnu_cxx::stdio_filebuf extension for opening UNICODE files on MinGW when using C++ file streams.
Variant accepting filebuf returns bool (true if succeeded and false otherwise).

Checks of ofstream to be opened made via calls to low-level ofstream::rdbuf() are replaced by calls to ofstream::is_open(); state of the stream is also checked (to be good).
Unicode name used for test file in test bugs fclasses bug22125 is described (for possibility to check it).

src/BRepTools/BRepTools.cxx
src/Draw/Draw_VariableCommands.cxx
src/OSD/OSD_OpenFile.cxx
src/OSD/OSD_OpenFile.hxx
src/StepSelect/StepSelect_WorkLibrary.cxx
tests/bugs/fclasses/bug22125

index 7efc932..449a9c6 100644 (file)
@@ -789,7 +789,8 @@ Standard_Boolean  BRepTools::Write(const TopoDS_Shape& Sh,
 {
   ofstream os;
   OSD_OpenStream(os, File, ios::out);
-  if (!os.rdbuf()->is_open()) return Standard_False;
+  if (!os.is_open() || !os.good())
+    return Standard_False;
 
   Standard_Boolean isGood = (os.good() && !os.eof());
   if(!isGood)
index cfa2ced..d60318c 100644 (file)
@@ -141,12 +141,12 @@ static Standard_Integer save(Draw_Interpretor& di, Standard_Integer n, const cha
 {
   if (n <= 2) return 1;
 
-
   const char* name = a[2];
   ofstream os;
   os.precision(15);
   OSD_OpenStream(os, name, ios::out);
-  if (!os.rdbuf()->is_open()) {
+  if (!os.is_open() || !os.good())
+  {
     di << "Cannot open file for writing "<<name;
     return 1;
   }
index 12d6e7a..ba8db4b 100644 (file)
 
 #ifdef _WIN32
   #include <windows.h>
+  #include <share.h>
 #endif
 
 #include <OSD_OpenFile.hxx>
 
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
+
+//! Auxiliary function converting C++ ios open mode flags to C fopen() flags.
+static int OSD_OpenFile_iosMode2FileFlags (::std::ios_base::openmode theMode)
+{
+  int aFlags = 0;
+  if (theMode & ::std::ios_base::in)
+  {
+    aFlags |= O_RDONLY;
+  }
+  if (theMode & ::std::ios_base::out)
+  {
+    aFlags |= O_WRONLY;
+    aFlags |= O_CREAT;
+    if (theMode & ::std::ios_base::app)
+    {
+      aFlags |= O_APPEND;
+    }
+    if (theMode & ::std::ios_base::trunc)
+    {
+      aFlags |= O_TRUNC;
+    }
+  }
+#ifdef _WIN32
+  if (theMode & ::std::ios_base::binary)
+  {
+    aFlags |= O_BINARY;
+  }
+  else
+  {
+    aFlags |= O_TEXT;
+  }
+#endif
+  return aFlags;
+}
+
+// ==============================================
+// function : OSD_OpenFile
+// purpose : Opens file
+// ==============================================
+int OSD_OpenFileDescriptor (const TCollection_ExtendedString& theName,
+                            ::std::ios_base::openmode theMode)
+{
+  int aFileDesc = -1;
+  const int aFlags = OSD_OpenFile_iosMode2FileFlags (theMode);
+#if defined(_WIN32)
+  const errno_t anErrCode = _wsopen_s (&aFileDesc, theName.ToWideString(), aFlags, _SH_DENYNO, _S_IREAD | _S_IWRITE);
+  if (anErrCode != 0)
+  {
+    return -1;
+  }
+#else
+  NCollection_Utf8String aString (theName.ToExtString());
+  aFileDesc = open (aString.ToCString(), aFlags);
+#endif
+  return aFileDesc;
+}
 
 // ==============================================
 // function : OSD_OpenFile
index 378951e..24d3737 100644 (file)
 #include <TCollection_ExtendedString.hxx>
 #include <NCollection_UtfString.hxx>
 
+#if defined(_WIN32) && defined(__GLIBCXX__)
+  #include <ext/stdio_filebuf.h> // __gnu_cxx::stdio_filebuf
+#endif
+
 //! Function opens the file.
 //! @param theName name of file encoded in UTF-16
 //! @param theMode opening mode
@@ -37,21 +41,46 @@ __Standard_API FILE* OSD_OpenFile (const TCollection_ExtendedString& theName,
 //! @return stat.st_ctime value
 __Standard_API Standard_Time OSD_FileStatCTime (const char* theName);
 
-//! Function opens the file stream.
-//! @param theStream stream to open
-//! @param theName name of file encoded in UTF-8
+//! Open file descriptor for specified UTF-16 file path.
+//! @param theName name of file encoded in UTF-16
 //! @param theMode opening mode
-template <typename T>
-inline void OSD_OpenStream (T& theStream,
-                            const char* theName,
+//! @return file descriptor on success or -1 on error
+__Standard_API int OSD_OpenFileDescriptor (const TCollection_ExtendedString& theName,
+                                           ::std::ios_base::openmode theMode);
+
+//! Function opens the file buffer.
+//! @param theFileBuf file buffer to open
+//! @param theName name of file encoded in UTF-16
+//! @param theMode opening mode
+//! @return true if success, false otherwise
+inline bool OSD_OpenStream (::std::filebuf& theFileBuf,
+                            const TCollection_ExtendedString& theName,
                             const std::ios_base::openmode theMode)
 {
-#if defined(_WIN32) && defined(_MSC_VER)
-  // file name is treated as UTF-8 string and converted to UTF-16 one
-  const TCollection_ExtendedString aFileNameW (theName, Standard_True);
-  theStream.open (aFileNameW.ToWideString(), theMode);
+#if defined(_WIN32)
+  #if defined(__GLIBCXX__)
+  // if file buffer is already open, open() should fail according to C++ standard
+  if (theFileBuf.is_open())
+    return false;
+  // __gnu_cxx::stdio_filebuf is a std::filebuf providing extra constructor taking FILE* or file descriptor;
+  // It does not modify virtual methods or add any fields - so we can safely use swap (or move operator) here.
+  // MinGW does not provide open() methods taking wchar_t* or file descriptor - thus, creating __gnu_cxx::stdio_filebuf
+  // is the only way for opening such files since _wfopen()/_wsopen_s() from C world are available.
+  const int aFileDesc = OSD_OpenFileDescriptor (theName.ToWideString(), theMode);
+  __gnu_cxx::stdio_filebuf<char> aGccBuf (aFileDesc, theMode);
+  if (aGccBuf.is_open())
+  {
+    theFileBuf.swap (aGccBuf);
+    return true;
+  }
+  return false;
+  #else
+  return theFileBuf.open (theName.ToWideString(), theMode) != 0;
+  #endif
 #else
-  theStream.open (theName, theMode);
+  // conversion to UTF-8 for linux
+  NCollection_Utf8String aString (theName.ToExtString());
+  return theFileBuf.open (aString.ToCString(), theMode) != 0;
 #endif
 }
 
@@ -64,8 +93,22 @@ inline void OSD_OpenStream (T& theStream,
                             const TCollection_ExtendedString& theName,
                             const std::ios_base::openmode theMode)
 {
-#if defined(_WIN32) && defined(_MSC_VER)
+#if defined(_WIN32)
+  #if defined(__GLIBCXX__)
+  // Use hackish code for opening wchar_t* file paths on MinGW,
+  // which considers implementation details of std::filebuf within std::fstream/std::ifstream/std::ofstream.
+  // Should be removed when MinGW will be improved to support wchar_t file paths natively within C++ streams.
+  if (! OSD_OpenStream (*theStream.rdbuf(), theName, theMode))
+  {
+    theStream.setstate (std::ios_base::failbit);
+  }
+  else
+  {
+    theStream.clear();
+  }
+  #else
   theStream.open (theName.ToWideString(), theMode);
+  #endif
 #else
   // conversion in UTF-8 for linux
   NCollection_Utf8String aString (theName.ToExtString());
@@ -73,6 +116,24 @@ inline void OSD_OpenStream (T& theStream,
 #endif
 }
 
+//! Function opens the file stream.
+//! @param theStream stream to open
+//! @param theName name of file encoded in UTF-8
+//! @param theMode opening mode
+template <typename T>
+inline void OSD_OpenStream (T& theStream,
+                            const char* theName,
+                            const std::ios_base::openmode theMode)
+{
+#if defined(_WIN32)
+  // redirect to method taking UTF-16 string
+  const TCollection_ExtendedString aFileNameW (theName, Standard_True);
+  OSD_OpenStream (theStream, aFileNameW, theMode);
+#else
+  theStream.open (theName, theMode);
+#endif
+}
+
 extern "C" {
 #endif // __cplusplus
 
index 272f079..623d88c 100644 (file)
@@ -91,7 +91,7 @@ Standard_Boolean  StepSelect_WorkLibrary::WriteFile
   ofstream fout;
   OSD_OpenStream(fout,ctx.FileName(),ios::out|ios::trunc);
 
-  if (!fout || !fout.rdbuf()->is_open()) {
+  if (!fout || !fout.is_open()) {
     ctx.CCheck(0)->AddFail("Step File could not be created");
     sout<<" Step File could not be created : " << ctx.FileName() << endl; return 0;
   }
index 70eafe4..ee5781a 100644 (file)
@@ -8,7 +8,9 @@ puts ""
 
 pload XDE
 
-set s [encoding convertfrom unicode "\xDE\x30\xF9\x30\xF1\x30"]
+# words "it works" translated to Traditional Chinese by Google Translate
+set s [encoding convertfrom utf-8 "\xE6\x9C\x89\xE7\x94\xA8"]
+
 igesbrep [locate_data_file bug22125_Part1_badname.igs] a *
 brepiges a ${imagedir}/Part1_badname_$s.igs
 igesbrep ${imagedir}/Part1_badname_$s.igs result *