# HG changeset patch
# User Markus Mützel <markus.muetzel@gmx.de>
# Date 1479920815 -3600
#      Wed Nov 23 18:06:55 2016 +0100
# Node ID e87e9e7d18f8906a55d6441d3e9d992dcf93c806
# Parent  86b6f79d4de19998c555dfe4ce839fef51e26efc
Use wide character Windows API (bug #49118).

* file-ops.[cc,h]: Add helper functions for convertion between multibyte
UTF-8 strings and wide character (UTF-16) strings. Add helper function
to get short filename (8.3) from wide character (UTF-16) filename. Use
wide character API for mkdir and rmdir. Add abstraction layer for fopen.

* oct-parse.in.yy (parse_fcn_file): Use abstraction layer for fopen.

* lo-sysdep.cc, sysdep.cc: Use wide character API for getcwd, chdir, and
same_file_internal.

* ls-hdf5.c, load-save.cc: Use short filenames (8.3) for HDF5.

diff -r 86b6f79d4de1 -r e87e9e7d18f8 libinterp/corefcn/load-save.cc
--- a/libinterp/corefcn/load-save.cc	Wed Nov 23 10:17:06 2016 -0500
+++ b/libinterp/corefcn/load-save.cc	Wed Nov 23 18:06:55 2016 +0100
@@ -302,7 +302,11 @@
 
 #if defined (HAVE_HDF5)
   // check this before we open the file
+#if defined (OCTAVE_USE_WINDOWS_API)
+  if (H5Fis_hdf5 (octave::sys::get_short_filename (fname.c_str ())) > 0)
+#else
   if (H5Fis_hdf5 (fname.c_str ()) > 0)
+#endif
     return LS_HDF5;
 #endif
 
@@ -1661,8 +1665,14 @@
           if (append)
             error ("save: appending to HDF5 files is not implemented");
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+          bool write_header_info
+            = ! (append && H5Fis_hdf5 (
+                 octave::sys::get_short_filename (fname.c_str ())) > 0);
+#else
           bool write_header_info
             = ! (append && H5Fis_hdf5 (fname.c_str ()) > 0);
+#endif
 
           hdf5_ofstream hdf5_file (fname.c_str (), mode);
 
diff -r 86b6f79d4de1 -r e87e9e7d18f8 libinterp/corefcn/ls-hdf5.cc
--- a/libinterp/corefcn/ls-hdf5.cc	Wed Nov 23 10:17:06 2016 -0500
+++ b/libinterp/corefcn/ls-hdf5.cc	Wed Nov 23 18:06:55 2016 +0100
@@ -91,15 +91,30 @@
 {
 #if defined (HAVE_HDF5)
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+  const char *s_name = octave::sys::get_short_filename (name);
+#else
+  const char *s_name = name;
+#endif
+
   if (mode & std::ios::in)
-    file_id = H5Fopen (name, H5F_ACC_RDONLY, octave_H5P_DEFAULT);
+    file_id = H5Fopen (s_name, H5F_ACC_RDONLY, octave_H5P_DEFAULT);
   else if (mode & std::ios::out)
     {
-      if (mode & std::ios::app && H5Fis_hdf5 (name) > 0)
-        file_id = H5Fopen (name, H5F_ACC_RDWR, octave_H5P_DEFAULT);
+      if (mode & std::ios::app && H5Fis_hdf5 (s_name) > 0)
+        file_id = H5Fopen (s_name, H5F_ACC_RDWR, octave_H5P_DEFAULT);
       else
+#if defined (OCTAVE_USE_WINDOWS_API)
+        {
+          // FIXME: Create a file with an ASCII name in an accessible folder.
+          //        Close the file, move and rename using wchar_t and re-open.
+          file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
+                              octave_H5P_DEFAULT);
+        }
+#else
         file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
                              octave_H5P_DEFAULT);
+#endif
     }
   if (file_id < 0)
     std::ios::setstate (std::ios::badbit);
@@ -138,15 +153,30 @@
 
   clear ();
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+  const char *s_name = octave::sys::get_short_filename (name);
+#else
+  const char *s_name = name;
+#endif
+
   if (mode & std::ios::in)
-    file_id = H5Fopen (name, H5F_ACC_RDONLY, octave_H5P_DEFAULT);
+    file_id = H5Fopen (s_name, H5F_ACC_RDONLY, octave_H5P_DEFAULT);
   else if (mode & std::ios::out)
     {
-      if (mode & std::ios::app && H5Fis_hdf5 (name) > 0)
-        file_id = H5Fopen (name, H5F_ACC_RDWR, octave_H5P_DEFAULT);
+      if (mode & std::ios::app && H5Fis_hdf5 (s_name) > 0)
+        file_id = H5Fopen (s_name, H5F_ACC_RDWR, octave_H5P_DEFAULT);
       else
+#if defined (OCTAVE_USE_WINDOWS_API)
+        {
+          // FIXME: Create a file with an ASCII name in an accessible folder.
+          //        Close the file move and rename using wchar_t and re-open
+          file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
+                              octave_H5P_DEFAULT);
+        }
+#else
         file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
                              octave_H5P_DEFAULT);
+#endif
     }
   if (file_id < 0)
     std::ios::setstate (std::ios::badbit);
diff -r 86b6f79d4de1 -r e87e9e7d18f8 libinterp/corefcn/sysdep.cc
--- a/libinterp/corefcn/sysdep.cc	Wed Nov 23 10:17:06 2016 -0500
+++ b/libinterp/corefcn/sysdep.cc	Wed Nov 23 18:06:55 2016 +0100
@@ -267,11 +267,11 @@
 
   bool retval = false;
 
-  const char *f1 = file1.c_str ();
-  const char *f2 = file2.c_str ();
+  const wchar_t *f1 = octave::sys::mb2wc (file1.c_str ());
+  const wchar_t *f2 = octave::sys::mb2wc (file2.c_str ());
 
-  bool f1_is_dir = GetFileAttributes (f1) & FILE_ATTRIBUTE_DIRECTORY;
-  bool f2_is_dir = GetFileAttributes (f2) & FILE_ATTRIBUTE_DIRECTORY;
+  bool f1_is_dir = GetFileAttributesW (f1) & FILE_ATTRIBUTE_DIRECTORY;
+  bool f2_is_dir = GetFileAttributesW (f2) & FILE_ATTRIBUTE_DIRECTORY;
 
   // Windows native code
   // Reference: http://msdn2.microsoft.com/en-us/library/aa363788.aspx
@@ -279,13 +279,13 @@
   DWORD share = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
 
   HANDLE hfile1
-    = CreateFile (f1, 0, share, 0, OPEN_EXISTING,
+    = CreateFileW (f1, 0, share, 0, OPEN_EXISTING,
                   f1_is_dir ? FILE_FLAG_BACKUP_SEMANTICS : 0, 0);
 
   if (hfile1 != INVALID_HANDLE_VALUE)
     {
       HANDLE hfile2
-        = CreateFile (f2, 0, share, 0, OPEN_EXISTING,
+        = CreateFileW (f2, 0, share, 0, OPEN_EXISTING,
                       f2_is_dir ? FILE_FLAG_BACKUP_SEMANTICS : 0, 0);
 
       if (hfile2 != INVALID_HANDLE_VALUE)
diff -r 86b6f79d4de1 -r e87e9e7d18f8 libinterp/parse-tree/oct-parse.in.yy
--- a/libinterp/parse-tree/oct-parse.in.yy	Wed Nov 23 10:17:06 2016 -0500
+++ b/libinterp/parse-tree/oct-parse.in.yy	Wed Nov 23 18:06:55 2016 +0100
@@ -4165,7 +4165,7 @@
   FILE *ffile = 0;
 
   if (! full_file.empty ())
-    ffile = std::fopen (full_file.c_str (), "rb");
+    ffile = octave::sys::fopen (full_file, "rb");
 
   if (ffile)
     {
diff -r 86b6f79d4de1 -r e87e9e7d18f8 liboctave/system/file-ops.cc
--- a/liboctave/system/file-ops.cc	Wed Nov 23 10:17:06 2016 -0500
+++ b/liboctave/system/file-ops.cc	Wed Nov 23 18:06:55 2016 +0100
@@ -33,6 +33,10 @@
 #  include <algorithm>
 #endif
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <windows.h>
+#endif
+
 #include <iostream>
 #include <vector>
 
@@ -415,7 +419,12 @@
     {
       msg = "";
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+      int status = _wmkdir (mb2wc (name.c_str ()));
+      octave_unused_parameter (mode);
+#else
       int status = octave_mkdir_wrapper (name.c_str (), mode);
+#endif
 
       if (status < 0)
         msg = std::strerror (errno);
@@ -553,7 +562,11 @@
 
       int status = -1;
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+      status = _wrmdir (mb2wc (name.c_str ()));
+#else
       status = octave_rmdir_wrapper (name.c_str ());
+#endif
 
       if (status < 0)
         msg = std::strerror (errno);
@@ -638,6 +651,16 @@
       return status;
     }
 
+    std::FILE*
+    fopen (const std::string& filename, const std::string& mode)
+    {
+#if defined (OCTAVE_USE_WINDOWS_API)
+      return _wfopen (mb2wc (filename.c_str ()), mb2wc (mode.c_str ()));
+#else
+      return std::fopen (filename.c_str (), mode.c_str ());
+#endif
+    }
+
     int
     umask (mode_t mode)
     {
@@ -747,6 +770,48 @@
 
       return retval;
     }
+#if defined (OCTAVE_USE_WINDOWS_API)
+    wchar_t *
+    mb2wc (const char *mb)
+    {
+      // convert multibyte UTF-8 string to wide character string
+      int mblen = static_cast<int> (strlen (mb));
+      int wlen = MultiByteToWideChar (65001, 0, mb, mblen, NULL, 0);
+      wchar_t* wc = new wchar_t[wlen+1];
+      MultiByteToWideChar (65001, 0, mb, mblen, wc, wlen);
+      wc[wlen] = 0; // null-terminate string
+      return wc;
+    }
+
+    char *
+    wc2mb (const wchar_t *wc)
+    {
+      // convert wide character string to multibyte UTF-8 string
+      int w_len = static_cast<int> (wcslen (wc));
+      int mb_len = WideCharToMultiByte (65001, 0, wc, w_len, NULL, 0, NULL, NULL);
+      char* mb = new char[mb_len+1];
+      WideCharToMultiByte (65001, 0, wc, w_len, mb, mb_len, NULL, NULL);
+      mb[mb_len] = 0; // null-terminate string
+      return mb;
+    }
+
+    char *
+    get_short_filename (const char *long_filename)
+    {
+      wchar_t *w_long_filename = mb2wc (long_filename);
+      // get short filename (8.3) from wide character filename
+      long length = GetShortPathNameW (w_long_filename, NULL, 0);
+
+      // Dynamically allocate the correct size 
+      // (terminating null char was included in length)
+      wchar_t *w_short_filename = new wchar_t[length];
+      length = GetShortPathNameW (w_long_filename, w_short_filename, length);
+      char *retval = wc2mb (w_short_filename);
+
+      return retval;
+    }
+#endif
+
   }
 }
 
diff -r 86b6f79d4de1 -r e87e9e7d18f8 liboctave/system/file-ops.h
--- a/liboctave/system/file-ops.h	Wed Nov 23 10:17:06 2016 -0500
+++ b/liboctave/system/file-ops.h	Wed Nov 23 18:06:55 2016 +0100
@@ -31,6 +31,10 @@
 
 #include "str-vec.h"
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <wchar.h>
+#endif
+
 namespace octave
 {
   namespace sys
@@ -198,6 +202,9 @@
     extern OCTAVE_API int
     recursive_rmdir (const std::string&, std::string&);
 
+    extern OCTAVE_API std::FILE*
+    fopen (const std::string&, const std::string&);
+
     extern OCTAVE_API int
     umask (mode_t);
 
@@ -218,6 +225,17 @@
 
     extern OCTAVE_API std::string
     canonicalize_file_name (const std::string&, std::string&);
+
+#if defined (OCTAVE_USE_WINDOWS_API)
+    extern wchar_t *
+    mb2wc (const char *mb);
+
+    extern char *
+    wc2mb (const wchar_t *wc);
+
+    extern char *
+    get_short_filename (const char *long_filename);
+#endif
   }
 }
 
diff -r 86b6f79d4de1 -r e87e9e7d18f8 liboctave/system/lo-sysdep.cc
--- a/liboctave/system/lo-sysdep.cc	Wed Nov 23 10:17:06 2016 -0500
+++ b/liboctave/system/lo-sysdep.cc	Wed Nov 23 18:06:55 2016 +0100
@@ -40,6 +40,14 @@
     {
       std::string retval;
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+      wchar_t *tmp = _wgetcwd (NULL, 0);
+
+      if (! tmp)
+        (*current_liboctave_error_handler) ("unable to find current directory");
+
+      retval = wc2mb (tmp);
+#else
       // Using octave_getcwd_wrapper ensures that we have a getcwd that
       // will allocate a buffer as large as necessary if buf and size are
       // both 0.
@@ -50,6 +58,7 @@
         (*current_liboctave_error_handler) ("unable to find current directory");
 
       retval = tmp;
+#endif
       free (tmp);
 
       return retval;
@@ -63,9 +72,10 @@
 #if defined (OCTAVE_USE_WINDOWS_API)
       if (path.length () == 2 && path[1] == ':')
         path += "\\";
+      return _wchdir (mb2wc (path.c_str ()));
+#else
+      return octave_chdir_wrapper (path.c_str ());
 #endif
-
-      return octave_chdir_wrapper (path.c_str ());
     }
   }
 }
