# HG changeset patch
# User Markus Mützel <markus.muetzel@gmx.de>
# Date 1485814172 -3600
#      Mon Jan 30 23:09:32 2017 +0100
# Node ID 6893a7850a132fa65caae0ad9db23132fa2fbe80
# Parent  868daa374c4980355cd1daf24ababe0a5b419e18
Use wide character Windows API for file system (bug #49118).

* oct-winapi.(cc,h]: New files with helper functions for convertion
between multibyte UTF-8 strings and wide character (UTF-16) wstrings.

* dir-ops.cc (dir_entry::read): Use wide character API for dir.

* file-ops.[cc,h]: 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.

* file-stat.cc: Use wide character API for stat.

* 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 868daa374c49 -r 6893a7850a13 libinterp/corefcn/load-save.cc
--- a/libinterp/corefcn/load-save.cc	Tue Nov 15 23:10:15 2016 +0100
+++ b/libinterp/corefcn/load-save.cc	Mon Jan 30 23:09:32 2017 +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 868daa374c49 -r 6893a7850a13 libinterp/corefcn/ls-hdf5.cc
--- a/libinterp/corefcn/ls-hdf5.cc	Tue Nov 15 23:10:15 2016 +0100
+++ b/libinterp/corefcn/ls-hdf5.cc	Mon Jan 30 23:09:32 2017 +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 868daa374c49 -r 6893a7850a13 libinterp/corefcn/sysdep.cc
--- a/libinterp/corefcn/sysdep.cc	Tue Nov 15 23:10:15 2016 +0100
+++ b/libinterp/corefcn/sysdep.cc	Mon Jan 30 23:09:32 2017 +0100
@@ -70,6 +70,10 @@
 #include "quit.h"
 #include "unistd-wrappers.h"
 #include "unsetenv-wrapper.h"
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include "oct-winapi.h"
+#endif
+
 
 #include "builtin-defun-decls.h"
 #include "Cell.h"
@@ -267,11 +271,11 @@
 
   bool retval = false;
 
-  const char *f1 = file1.c_str ();
-  const char *f2 = file2.c_str ();
+  const wchar_t *f1 = octave::sys::winapi::u8_to_wstring (file1).c_str ();
+  const wchar_t *f2 = octave::sys::winapi::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,14 +283,14 @@
   DWORD share = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
 
   HANDLE hfile1
-    = CreateFile (f1, 0, share, 0, OPEN_EXISTING,
-                  f1_is_dir ? FILE_FLAG_BACKUP_SEMANTICS : 0, 0);
+    = 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,
-                      f2_is_dir ? FILE_FLAG_BACKUP_SEMANTICS : 0, 0);
+        = CreateFileW (f2, 0, share, 0, OPEN_EXISTING,
+                       f2_is_dir ? FILE_FLAG_BACKUP_SEMANTICS : 0, 0);
 
       if (hfile2 != INVALID_HANDLE_VALUE)
         {
diff -r 868daa374c49 -r 6893a7850a13 libinterp/parse-tree/oct-parse.in.yy
--- a/libinterp/parse-tree/oct-parse.in.yy	Tue Nov 15 23:10:15 2016 +0100
+++ b/libinterp/parse-tree/oct-parse.in.yy	Mon Jan 30 23:09:32 2017 +0100
@@ -4260,7 +4260,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 868daa374c49 -r 6893a7850a13 liboctave/system/dir-ops.cc
--- a/liboctave/system/dir-ops.cc	Tue Nov 15 23:10:15 2016 +0100
+++ b/liboctave/system/dir-ops.cc	Mon Jan 30 23:09:32 2017 +0100
@@ -31,6 +31,11 @@
 #include <list>
 #include <string>
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <windows.h>
+#  include "oct-winapi.h"
+#endif
+
 #include "dirent-wrappers.h"
 
 #include "dir-ops.h"
@@ -75,10 +80,32 @@
         {
           std::list<std::string> dirlist;
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+          _WIN32_FIND_DATAW ffd;
+
+          std::string path_name (name);
+          path_name.append ("\\*");
+
+          // Find first file in directory.
+          HANDLE hFind = FindFirstFileW (winapi::u8_to_wstring (path_name).c_str (),
+                                  &ffd);
+          if (INVALID_HANDLE_VALUE == hFind) 
+            return retval;
+
+          do
+          {
+            dirlist.push_back (winapi::wstring_to_u8 (ffd.cFileName));
+          }
+          while (FindNextFileW (hFind, &ffd) != 0);
+
+          FindClose(hFind);
+
+#else
           char *fname;
 
           while ((fname = octave_readdir_wrapper (dir)))
             dirlist.push_back (fname);
+#endif
 
           retval = string_vector (dirlist);
         }
diff -r 868daa374c49 -r 6893a7850a13 liboctave/system/file-ops.cc
--- a/liboctave/system/file-ops.cc	Tue Nov 15 23:10:15 2016 +0100
+++ b/liboctave/system/file-ops.cc	Mon Jan 30 23:09:32 2017 +0100
@@ -36,6 +36,12 @@
 #include <iostream>
 #include <vector>
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <windows.h>
+#  include <wchar.h>
+#  include "oct-winapi.h"
+#endif
+
 #include "areadlink-wrapper.h"
 #include "canonicalize-file-name-wrapper.h"
 #include "dir-ops.h"
@@ -415,7 +421,12 @@
     {
       msg = "";
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+      int status = _wmkdir (winapi::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 +564,11 @@
 
       int status = -1;
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+      status = _wrmdir (winapi::u8_to_wstring (name).c_str ());
+#else
       status = octave_rmdir_wrapper (name.c_str ());
+#endif
 
       if (status < 0)
         msg = std::strerror (errno);
@@ -638,6 +653,17 @@
       return status;
     }
 
+    std::FILE*
+    fopen (const std::string& filename, const std::string& mode)
+    {
+#if defined (OCTAVE_USE_WINDOWS_API)
+      return _wfopen (winapi::u8_to_wstring (filename).c_str (),
+                      winapi::u8_to_wstring (mode).c_str ());
+#else
+      return std::fopen (filename.c_str (), mode.c_str ());
+#endif
+    }
+
     int
     umask (mode_t mode)
     {
@@ -747,5 +773,27 @@
 
       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 = winapi::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 = winapi::wstring_to_u8 (std::wstring (w_short_file_name));
+
+      return short_file_name;
+#else
+      return long_file_name;
+#endif
+    }
+
   }
 }
diff -r 868daa374c49 -r 6893a7850a13 liboctave/system/file-ops.h
--- a/liboctave/system/file-ops.h	Tue Nov 15 23:10:15 2016 +0100
+++ b/liboctave/system/file-ops.h	Mon Jan 30 23:09:32 2017 +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,10 @@
 
     extern OCTAVE_API std::string
     canonicalize_file_name (const std::string&, std::string&);
+
+    extern OCTAVE_API std::string
+    shorten_file_name (const std::string&);
+
   }
 }
 
diff -r 868daa374c49 -r 6893a7850a13 liboctave/system/file-stat.cc
--- a/liboctave/system/file-stat.cc	Tue Nov 15 23:10:15 2016 +0100
+++ b/liboctave/system/file-stat.cc	Mon Jan 30 23:09:32 2017 +0100
@@ -33,6 +33,11 @@
 #include "stat-wrappers.h"
 #include "strmode-wrapper.h"
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <sys/stat.h>
+#  include "oct-winapi.h"
+#endif
+
 namespace octave
 {
   namespace sys
@@ -166,6 +171,43 @@
     }
 
     // Private stuff:
+#if defined (OCTAVE_USE_WINDOWS_API)
+static inline void
+assign_stat_fields_winapi (struct _stat *buf, mode_t *mode, ino_t *ino,
+                    dev_t *dev, nlink_t *nlink, uid_t *uid,
+                    gid_t *gid, off_t *size, time_t *atime,
+                    time_t *mtime, time_t *ctime, dev_t *rdev,
+                    long *blksize, long *blocks)
+{
+  *mode = buf->st_mode;
+  *ino = buf->st_ino;
+  *dev = buf->st_dev;
+  *nlink = buf->st_nlink;
+  *uid = buf->st_uid;
+  *gid = buf->st_gid;
+  *size = buf->st_size;
+  *atime = buf->st_atime;
+  *mtime = buf->st_mtime;
+  *ctime = buf->st_ctime;
+
+#if defined (HAVE_STRUCT_STAT_ST_RDEV)
+  *rdev = buf->st_rdev;
+#else
+  *rdev = 0;
+#endif
+
+#if defined (HAVE_STRUCT_STAT_ST_BLKSIZE)
+  *blksize = buf->st_blksize;
+  *blksize = 0;
+#endif
+
+#if defined (HAVE_STRUCT_STAT_ST_BLOCKS)
+  *blocks = buf->st_blocks;
+  *blksize = 0;
+#endif
+}
+#endif
+
 
     void
     file_stat::update_internal (bool force)
@@ -185,10 +227,17 @@
             full_file_name.resize (full_file_name.length () - 1);
 #endif
 
+          time_t sys_atime, sys_mtime, sys_ctime;
+
+#if defined (OCTAVE_USE_WINDOWS_API)
+          struct _stat buf;
+          int status = _wstat (octave::sys::winapi::u8_to_wstring (full_file_name).c_str (), &buf);
+          assign_stat_fields_winapi (&buf, &m_mode, &m_ino, &m_dev, &m_nlink, &m_uid,
+                              &m_gid, &m_size, &sys_atime, &sys_mtime,
+                              &sys_ctime, &m_rdev, &m_blksize, &m_blocks);
+#else
           const char *cname = full_file_name.c_str ();
 
-          time_t sys_atime, sys_mtime, sys_ctime;
-
           int status
             = (follow_links
                ? octave_stat_wrapper (cname, &m_mode, &m_ino, &m_dev,
@@ -199,6 +248,7 @@
                                        &m_nlink, &m_uid, &m_gid, &m_size,
                                        &sys_atime, &sys_mtime, &sys_ctime,
                                        &m_rdev, &m_blksize, &m_blocks));
+#endif
 
           if (status < 0)
             {
diff -r 868daa374c49 -r 6893a7850a13 liboctave/system/lo-sysdep.cc
--- a/liboctave/system/lo-sysdep.cc	Tue Nov 15 23:10:15 2016 +0100
+++ b/liboctave/system/lo-sysdep.cc	Mon Jan 30 23:09:32 2017 +0100
@@ -31,6 +31,10 @@
 #include "lo-sysdep.h"
 #include "unistd-wrappers.h"
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include "oct-winapi.h"
+#endif
+
 namespace octave
 {
   namespace sys
@@ -40,6 +44,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 = winapi::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 +62,8 @@
         (*current_liboctave_error_handler) ("unable to find current directory");
 
       retval = tmp;
+#endif
+
       free (tmp);
 
       return retval;
@@ -65,7 +79,11 @@
         path += "\\";
 #endif
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+      return _wchdir (winapi::u8_to_wstring (path).c_str ());
+#else
       return octave_chdir_wrapper (path.c_str ());
+#endif
     }
   }
 }
diff -r 868daa374c49 -r 6893a7850a13 liboctave/system/module.mk
--- a/liboctave/system/module.mk	Tue Nov 15 23:10:15 2016 +0100
+++ b/liboctave/system/module.mk	Mon Jan 30 23:09:32 2017 +0100
@@ -10,7 +10,8 @@
   liboctave/system/oct-passwd.h \
   liboctave/system/oct-syscalls.h \
   liboctave/system/oct-time.h \
-  liboctave/system/oct-uname.h
+  liboctave/system/oct-uname.h \
+  liboctave/system/oct-winapi.h
 
 SYSTEM_SRC = \
   liboctave/system/child-list.cc \
@@ -24,7 +25,8 @@
   liboctave/system/oct-passwd.cc \
   liboctave/system/oct-syscalls.cc \
   liboctave/system/oct-time.cc \
-  liboctave/system/oct-uname.cc
+  liboctave/system/oct-uname.cc \
+  liboctave/system/oct-winapi.cc
 
 noinst_LTLIBRARIES += liboctave/system/libsystem.la
 
diff -r 868daa374c49 -r 6893a7850a13 liboctave/system/oct-winapi.cc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/system/oct-winapi.cc	Mon Jan 30 23:09:32 2017 +0100
@@ -0,0 +1,84 @@
+/*
+
+Copyright (C) 2017 M. Mützel
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <iostream>
+
+#if defined (OCTAVE_USE_WINDOWS_API)
+
+#include <windows.h>
+#include <wchar.h>
+
+#include "oct-winapi.h"
+
+namespace octave
+{
+  namespace sys
+  {
+    namespace winapi
+    {
+      std::wstring
+      u8_to_wstring (const std::string& utf8_string)
+      {
+        // convert multibyte UTF-8 string to wide character string
+        const char* mb = utf8_string.c_str ();
+        int mb_len = static_cast<int> (strlen (mb));
+        int wc_len = MultiByteToWideChar (CP_UTF8, 0, mb, mb_len, NULL, 0);
+        if (wc_len == 0)
+          return L"";
+        wchar_t* wc = new wchar_t[wc_len+1];
+        wc_len = MultiByteToWideChar (CP_UTF8, 0, mb, mb_len, wc, wc_len);
+        if (wc_len == 0)
+          return L"";
+        wc[wc_len] = 0; // null-terminate string
+        std::wstring retval = std::wstring (wc, wc_len);
+        delete [] wc;
+        return retval;
+      }
+
+      std::string
+      wstring_to_u8 (const std::wstring& utf16_string)
+      {
+        // convert wide character string to multibyte UTF-8 string
+        const wchar_t* wc = utf16_string.c_str ();
+        int wc_len = static_cast<int> (wcslen (wc));
+        int mb_len = WideCharToMultiByte (CP_UTF8, 0, wc, wc_len, NULL, 0, NULL, NULL);
+        if (mb_len == 0)
+          return "";
+        char* mb = new char[mb_len+1];
+        mb_len = WideCharToMultiByte (CP_UTF8, 0, wc, wc_len, mb, mb_len, NULL, NULL);
+        if (mb_len == 0)
+          return "";
+        mb[mb_len] = 0; // null-terminate string
+        std::string retval = std::string (mb, mb_len);
+        delete [] mb;
+        return retval;
+      }
+
+    }
+  }
+}
+
+#endif
\ No newline at end of file
diff -r 868daa374c49 -r 6893a7850a13 liboctave/system/oct-winapi.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/system/oct-winapi.h	Mon Jan 30 23:09:32 2017 +0100
@@ -0,0 +1,44 @@
+/*
+
+Copyright (C) 2017 M. Mützel
+
+This file is part of Octave.
+
+Octave is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+// only include this header if OCTAVE_USE_WINDOWS_API is defined
+
+#if ! defined (octave_oct_winapi_h)
+#define octave_winapi_h 1
+
+#include <string>
+
+namespace octave
+{
+  namespace sys
+  {
+    namespace winapi
+    {
+      extern OCTAVE_API std::wstring
+      u8_to_wstring (const std::string&);
+
+      extern OCTAVE_API std::string
+      wstring_to_u8 (const std::wstring&);
+    }
+  }
+}
+#endif
\ No newline at end of file
