# HG changeset patch
# User Markus Mützel <markus.muetzel@gmx.de>
# Date 1481053236 -3600
#      Tue Dec 06 20:40:36 2016 +0100
# Node ID a611183c4000fc1f20f3d27f9bba5dc20f037497
# Parent  78f1d359dd8fbba5a954c5cb38ff0276f8d420f0
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 78f1d359dd8f -r a611183c4000 libinterp/corefcn/load-save.cc
--- a/libinterp/corefcn/load-save.cc	Tue Dec 06 10:44:20 2016 -0500
+++ b/libinterp/corefcn/load-save.cc	Tue Dec 06 20:40:36 2016 +0100
@@ -302,7 +302,7 @@
 
 #if defined (HAVE_HDF5)
   // check this before we open the file
-  if (H5Fis_hdf5 (fname.c_str ()) > 0)
+  if (H5Fis_hdf5 (octave::sys::shorten_file_name (fname).c_str ()) > 0)
     return LS_HDF5;
 #endif
 
@@ -1656,7 +1656,8 @@
             error ("save: appending to HDF5 files is not implemented");
 
           bool write_header_info
-            = ! (append && H5Fis_hdf5 (fname.c_str ()) > 0);
+            = ! (append && H5Fis_hdf5 (
+                 octave::sys::shorten_file_name (fname).c_str ()) > 0);
 
           hdf5_ofstream hdf5_file (fname.c_str (), mode);
 
diff -r 78f1d359dd8f -r a611183c4000 libinterp/corefcn/ls-hdf5.cc
--- a/libinterp/corefcn/ls-hdf5.cc	Tue Dec 06 10:44:20 2016 -0500
+++ b/libinterp/corefcn/ls-hdf5.cc	Tue Dec 06 20:40:36 2016 +0100
@@ -91,13 +91,18 @@
 {
 #if defined (HAVE_HDF5)
 
+  const char *s_name = octave::sys::shorten_file_name (std::string (name)).c_str ();
+
   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
+        // FIXME: For Windows, create a file with an ASCII name in an
+        //        accessible folder, close the file move and rename using
+        //        wide character API and re-open.
         file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
                              octave_H5P_DEFAULT);
     }
@@ -138,13 +143,18 @@
 
   clear ();
 
+  const char *s_name = octave::sys::shorten_file_name (std::string (name)).c_str ();
+
   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
+        // FIXME: For Windows, create a file with an ASCII name in an
+        //        accessible folder, close the file move and rename using
+        //        wide character API and re-open.
         file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
                              octave_H5P_DEFAULT);
     }
diff -r 78f1d359dd8f -r a611183c4000 libinterp/corefcn/sysdep.cc
--- a/libinterp/corefcn/sysdep.cc	Tue Dec 06 10:44:20 2016 -0500
+++ b/libinterp/corefcn/sysdep.cc	Tue Dec 06 20:40:36 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::u8_to_wstring (file1).c_str ();
+  const wchar_t* f2 = octave::sys::u8_to_wstring (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 78f1d359dd8f -r a611183c4000 libinterp/parse-tree/oct-parse.in.yy
--- a/libinterp/parse-tree/oct-parse.in.yy	Tue Dec 06 10:44:20 2016 -0500
+++ b/libinterp/parse-tree/oct-parse.in.yy	Tue Dec 06 20:40:36 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 78f1d359dd8f -r a611183c4000 liboctave/system/file-ops.cc
--- a/liboctave/system/file-ops.cc	Tue Dec 06 10:44:20 2016 -0500
+++ b/liboctave/system/file-ops.cc	Tue Dec 06 20:40:36 2016 +0100
@@ -33,8 +33,15 @@
 #  include <algorithm>
 #endif
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <windows.h>
+#  include <wchar.h>
+#endif
+
 #include <iostream>
+#include <locale>
 #include <vector>
+#include <codecvt>
 
 #include "areadlink-wrapper.h"
 #include "canonicalize-file-name-wrapper.h"
@@ -415,7 +422,12 @@
     {
       msg = "";
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+      int status = _wmkdir (u8_to_wstring (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 +565,11 @@
 
       int status = -1;
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+      status = _wrmdir (u8_to_wstring (name).c_str ());
+#else
       status = octave_rmdir_wrapper (name.c_str ());
+#endif
 
       if (status < 0)
         msg = std::strerror (errno);
@@ -638,6 +654,16 @@
       return status;
     }
 
+    std::FILE*
+    fopen (const std::string& filename, const std::string& mode)
+    {
+#if defined (OCTAVE_USE_WINDOWS_API)
+      return _wfopen (u8_to_wstring (filename).c_str (), u8_to_wstring (mode).c_str ());
+#else
+      return std::fopen (filename.c_str (), mode.c_str ());
+#endif
+    }
+
     int
     umask (mode_t mode)
     {
@@ -747,6 +773,42 @@
 
       return retval;
     }
+
+    std::string
+    shorten_file_name (const std::string& long_file_name)
+    {
+#if defined (OCTAVE_USE_WINDOWS_API)
+      const wchar_t* w_long_file_name = u8_to_wstring (long_file_name).c_str ();
+      // get short filename (8.3) from UTF-16 filename
+      long length = GetShortPathNameW (w_long_file_name, NULL, 0);
+
+      // Dynamically allocate the correct size 
+      // (terminating null char was included in length)
+      wchar_t* w_short_file_name = new wchar_t[length];
+      length = GetShortPathNameW (w_long_file_name, w_short_file_name, length);
+ 
+      std::string short_file_name = wstring_to_u8 (std::wstring (w_short_file_name));
+
+      return short_file_name;
+#else
+      return long_file_name;
+#endif
+    }
+
+    std::wstring
+    u8_to_wstring (const std::string& utf8_string)
+    {
+      std::wstring_convert <std::codecvt_utf8_utf16 <wchar_t, 0x10ffff, std::little_endian>, wchar_t> convert; // converts between UTF-8 <-> UTF-16
+      return convert.from_bytes(utf8_string);
+    }
+
+    std::string
+    wstring_to_u8 (const std::wstring& w_string)
+    {
+      std::wstring_convert <std::codecvt_utf8_utf16 <wchar_t, 0x10ffff, std::little_endian>, wchar_t> convert; // converts between UTF-8 <-> UTF-16
+      return convert.to_bytes(w_string);
+    }
+
   }
 }
 
diff -r 78f1d359dd8f -r a611183c4000 liboctave/system/file-ops.h
--- a/liboctave/system/file-ops.h	Tue Dec 06 10:44:20 2016 -0500
+++ b/liboctave/system/file-ops.h	Tue Dec 06 20:40:36 2016 +0100
@@ -198,6 +198,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 +221,16 @@
 
     extern OCTAVE_API std::string
     canonicalize_file_name (const std::string&, std::string&);
+
+    extern OCTAVE_API std::string
+    shorten_file_name (const std::string&);
+
+    extern OCTAVE_API std::wstring
+    u8_to_wstring (const std::string&);
+
+    extern OCTAVE_API std::string
+    wstring_to_u8 (const std::wstring&);
+
   }
 }
 
diff -r 78f1d359dd8f -r a611183c4000 liboctave/system/lo-sysdep.cc
--- a/liboctave/system/lo-sysdep.cc	Tue Dec 06 10:44:20 2016 -0500
+++ b/liboctave/system/lo-sysdep.cc	Tue Dec 06 20:40:36 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 = wstring_to_u8 (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 (u8_to_wstring (path).c_str ());
+#else
+      return octave_chdir_wrapper (path.c_str ());
 #endif
-
-      return octave_chdir_wrapper (path.c_str ());
     }
   }
 }
