changeset 29540:b6f80b1d448f

Windows: Support non-ASCII characters in command line arguments. * configure.ac: Define macros necessary for Unicode support on Windows. Add linker flags for Unicode executables with mingw. * src/main-cli.cc, src/main-gui.cc, src/main.in.cc, src/mkoctfile.in.cc, src/octave-config.in.cc, src/octave-svgconvert.cc: Optionally use "wmain" instead of "main" as primary entry function. * src/mkoctfile.in.cc (main, wmain): Use Unicode API to open pipe on Windows if possible. * src/module.mk: Use flags for linking with Unicode "wmain" function.
author Markus Mützel <markus.muetzel@gmx.de>
date Sat, 17 Apr 2021 12:21:48 +0200
parents b51ba0fdd513
children ebf68dde5579
files configure.ac src/main-cli.cc src/main-gui.cc src/main.in.cc src/mkoctfile.in.cc src/module.mk src/octave-config.in.cc src/octave-svgconvert.cc
diffstat 8 files changed, 235 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Sat Apr 17 12:21:35 2021 +0200
+++ b/configure.ac	Sat Apr 17 12:21:48 2021 +0200
@@ -1251,6 +1251,23 @@
   AC_MSG_ERROR([MATH DEFINES in math.h such as M_PI are required to build Octave])
 fi
 
+## Use Unicode aware functions on Windows
+case $host_os in
+  msdosmsvc | mingw*)
+    AC_DEFINE(_UNICODE, 1, [Use Unicode CRT functions on Windows by default.])
+    AC_DEFINE(UNICODE, 1, [Use Windows Unicode API by default.])
+  ;;
+esac
+
+OCTAVE_UNICODE_EXE_LDFLAGS=""
+case $host_os in
+  mingw*)
+    OCTAVE_UNICODE_EXE_LDFLAGS="-municode"
+  ;;
+esac
+
+AC_SUBST(OCTAVE_UNICODE_EXE_LDFLAGS)
+
 ## Windows-specific use of functions
 case $host_os in
   msdosmsvc | mingw*)
--- a/src/main-cli.cc	Sat Apr 17 12:21:35 2021 +0200
+++ b/src/main-cli.cc	Sat Apr 17 12:21:48 2021 +0200
@@ -32,6 +32,12 @@
 #include <iostream>
 #include <string>
 
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+#  include <vector>
+#  include <locale>
+#  include <codecvt>
+#endif
+
 #include "liboctave-build-info.h"
 
 #include "liboctinterp-build-info.h"
@@ -81,9 +87,28 @@
     exit (1);
 }
 
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+extern "C"
+int
+wmain (int argc, wchar_t **wargv)
+{
+  static char **argv = new char * [argc + 1];
+  std::vector<std::string> argv_str;
+
+  // convert wide character strings to multibyte UTF-8 strings
+  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_conv;
+  for (int i_arg = 0; i_arg < argc; i_arg++)
+    {
+      argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg]));
+      argv[i_arg] = &argv_str[i_arg][0];
+    }
+  argv[argc] = nullptr;
+
+#else
 int
 main (int argc, char **argv)
 {
+#endif
   check_hg_versions ();
 
   octave_block_async_signals ();
--- a/src/main-gui.cc	Sat Apr 17 12:21:35 2021 +0200
+++ b/src/main-gui.cc	Sat Apr 17 12:21:48 2021 +0200
@@ -32,6 +32,12 @@
 #include <iostream>
 #include <string>
 
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+#  include <vector>
+#  include <locale>
+#  include <codecvt>
+#endif
+
 #include "liboctave-build-info.h"
 
 #include "liboctinterp-build-info.h"
@@ -94,9 +100,28 @@
     exit (1);
 }
 
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+extern "C"
+int
+wmain (int argc, wchar_t **wargv)
+{
+  static char **argv = new char * [argc + 1];
+  std::vector<std::string> argv_str;
+
+  // convert wide character strings to multibyte UTF-8 strings
+  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_conv;
+  for (int i_arg = 0; i_arg < argc; i_arg++)
+    {
+      argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg]));
+      argv[i_arg] = &argv_str[i_arg][0];
+    }
+  argv[argc] = nullptr;
+
+#else
 int
 main (int argc, char **argv)
 {
+#endif
   check_hg_versions ();
 
   octave::sys::env::set_program_name (argv[0]);
--- a/src/main.in.cc	Sat Apr 17 12:21:35 2021 +0200
+++ b/src/main.in.cc	Sat Apr 17 12:21:48 2021 +0200
@@ -43,6 +43,12 @@
 #include <iostream>
 #include <string>
 
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+#  include <vector>
+#  include <locale>
+#  include <codecvt>
+#endif
+
 // We are linking against static libs so do not decorate with dllimport.
 // FIXME: This should be done by the build system.
 #undef OCTAVE_API
@@ -210,9 +216,28 @@
   return tmp;
 }
 
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+extern "C"
+int
+wmain (int argc, wchar_t **wargv)
+{
+  static char **argv = new char * [argc + 1];
+  std::vector<std::string> argv_str;
+
+  // convert wide character strings to multibyte UTF-8 strings
+  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_conv;
+  for (int i_arg = 0; i_arg < argc; i_arg++)
+    {
+      argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg]));
+      argv[i_arg] = &argv_str[i_arg][0];
+    }
+  argv[argc] = nullptr;
+
+#else
 int
 main (int argc, char **argv)
 {
+#endif
   int retval = 0;
 
   int idx_gui = -1;
--- a/src/mkoctfile.in.cc	Sat Apr 17 12:21:35 2021 +0200
+++ b/src/mkoctfile.in.cc	Sat Apr 17 12:21:48 2021 +0200
@@ -40,6 +40,11 @@
 #include <vector>
 #include <cstdlib>
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <locale>
+#  include <codecvt>
+#endif
+
 // Programming note:  The CROSS macro here refers to building a
 // cross-compiler aware version of mkoctfile that can be used to cross
 // compile .oct file for Windows builds of Octave, not that mkoctfile
@@ -92,14 +97,6 @@
   return mkostemps (tmpl, suffixlen, 0);
 }
 
-static char *
-octave_u8_conv_to_encoding (const char *tocode, const uint8_t *src,
-                            size_t srclen, size_t *lengthp)
-{
-  // FIXME: Do we need to provide the conversion here?
-  return nullptr;
-}
-
 static int
 octave_unlink_wrapper (const char *nm)
 {
@@ -702,9 +699,28 @@
     octave_unlink_wrapper (file.c_str ());
 }
 
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+extern "C"
+int
+wmain (int argc, wchar_t **wargv)
+{
+  static char **argv = new char * [argc + 1];
+  std::vector<std::string> argv_str;
+
+  // convert wide character strings to multibyte UTF-8 strings
+  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_conv;
+  for (int i_arg = 0; i_arg < argc; i_arg++)
+    {
+      argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg]));
+      argv[i_arg] = &argv_str[i_arg][0];
+    }
+  argv[argc] = nullptr;
+
+#else
 int
 main (int argc, char **argv)
 {
+#endif
   if (argc == 1)
     {
       std::cout << usage_msg << std::endl;
@@ -1032,6 +1048,10 @@
 
   if (depend)
     {
+#if defined (OCTAVE_USE_WINDOWS_API) && ! defined (_UNICODE)
+      std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_conv;
+#endif
+
       for (const auto& f : cfiles)
         {
           std::string dfile = basename (f, true) + ".d", line;
@@ -1043,33 +1063,34 @@
                + vars["CPPFLAGS"] + ' ' + vars["ALL_CFLAGS"] + ' '
                + incflags  + ' ' + defs + ' ' + quote_path (f));
 
-          // FIXME: Use wide character API for popen on Windows.
+#if defined (OCTAVE_USE_WINDOWS_API)
+          FILE *fd;
+          try
+            {
+              std::wstring wcmd = wchar_conv.from_bytes (cmd);
+              fd = ::_wpopen (wcmd.c_str (), L"r");
+            }
+          catch (const std::range_error& e)
+            {
+              fd = ::popen (cmd.c_str (), "r");
+            }
+
+          std::ofstream fo;
+          try
+            {
+              std::wstring wfile = wchar_conv.from_bytes (dfile);
+              fo.open (wfile.c_str ());
+            }
+          catch (const std::range_error& e)
+            {
+              fo.open (dfile.c_str ());
+            }
+#else
           FILE *fd = popen (cmd.c_str (), "r");
 
-#if defined (OCTAVE_USE_WINDOWS_API)
-          // FIXME: liboctinterp isn't linked in to mkoctfile.
-          // So we cannot use octave::sys::ofstream. Instead we fall back
-          // on using the functions available from libwrappers.
-          size_t srclen = dfile.length ();
-          const uint8_t *src = reinterpret_cast<const uint8_t *>
-                               (dfile.c_str ());
-
-          size_t length = 0;
-          wchar_t *wchar = reinterpret_cast<wchar_t *>
-                           (octave_u8_conv_to_encoding ("wchar_t", src, srclen,
-                                                        &length));
-
-          std::ofstream fo;
-          if (wchar != nullptr)
-            {
-              fo.open (wchar);
-              free (static_cast<void *> (wchar));
-            }
-          else
-            fo.open (dfile.c_str ());
-#else
           std::ofstream fo (dfile.c_str ());
 #endif
+
           size_t pos;
           while (! feof (fd))
             {
@@ -1102,33 +1123,34 @@
                + vars["CPPFLAGS"] + ' ' + vars["ALL_CXXFLAGS"] + ' '
                + incflags  + ' ' + defs + ' ' + quote_path (f));
 
-          // FIXME: Use wide character API for popen on Windows.
+#if defined (OCTAVE_USE_WINDOWS_API)
+          FILE *fd;
+          try
+            {
+              std::wstring wcmd = wchar_conv.from_bytes (cmd);
+              fd = ::_wpopen (wcmd.c_str (), L"r");
+            }
+          catch (const std::range_error& e)
+            {
+              fd = ::popen (cmd.c_str (), "r");
+            }
+
+          std::ofstream fo;
+          try
+            {
+              std::wstring wfile = wchar_conv.from_bytes (dfile);
+              fo.open (wfile.c_str ());
+            }
+          catch (const std::range_error& e)
+            {
+              fo.open (dfile.c_str ());
+            }
+#else
           FILE *fd = popen (cmd.c_str (), "r");
 
-#if defined (OCTAVE_USE_WINDOWS_API)
-          // FIXME: liboctinterp isn't linked in to mkoctfile.
-          // So we cannot use octave::sys::ofstream. Instead we fall back
-          // on using the functions available from libwrappers.
-          size_t srclen = dfile.length ();
-          const uint8_t *src = reinterpret_cast<const uint8_t *>
-                               (dfile.c_str ());
-
-          size_t length = 0;
-          wchar_t *wchar = reinterpret_cast<wchar_t *>
-                           (octave_u8_conv_to_encoding ("wchar_t", src, srclen,
-                                                        &length));
-
-          std::ofstream fo;
-          if (wchar != nullptr)
-            {
-              fo.open (wchar);
-              free (static_cast<void *> (wchar));
-            }
-          else
-            fo.open (dfile.c_str ());
-#else
           std::ofstream fo (dfile.c_str ());
 #endif
+
           size_t pos;
           while (! feof (fd))
             {
--- a/src/module.mk	Sat Apr 17 12:21:35 2021 +0200
+++ b/src/module.mk	Sat Apr 17 12:21:48 2021 +0200
@@ -74,7 +74,8 @@
 %canon_reldir%_octave_LDFLAGS = \
   $(NO_UNDEFINED_LDFLAG) \
   $(OCTAVE_LINK_OPTS) \
-  $(WARN_LDFLAGS)
+  $(WARN_LDFLAGS) \
+  $(OCTAVE_UNICODE_EXE_LDFLAGS)
 
 if AMCOND_BUILD_QT_GUI
   OCTAVE_CPPFLAGS = -DHAVE_OCTAVE_QT_GUI
@@ -94,7 +95,8 @@
 %canon_reldir%_octave_cli_LDFLAGS = \
   $(NO_UNDEFINED_LDFLAG) \
   $(OCTAVE_LINK_OPTS) \
-  $(WARN_LDFLAGS)
+  $(WARN_LDFLAGS) \
+  $(OCTAVE_UNICODE_EXE_LDFLAGS)
 
 %canon_reldir%_octave_cli_CPPFLAGS = \
   $(SRC_DIR_CPPFLAGS) \
@@ -119,7 +121,8 @@
 %canon_reldir%_octave_gui_LDFLAGS = \
   $(NO_UNDEFINED_LDFLAG) \
   $(OCTAVE_GUI_LINK_OPTS) \
-  $(WARN_LDFLAGS)
+  $(WARN_LDFLAGS) \
+  $(OCTAVE_UNICODE_EXE_LDFLAGS)
 
 %canon_reldir%_octave_svgconvert_SOURCES = %reldir%/octave-svgconvert.cc
 
@@ -127,7 +130,9 @@
 
 %canon_reldir%_octave_svgconvert_LDADD = $(QT_LIBS)
 
-%canon_reldir%_octave_svgconvert_LDFLAGS = $(QT_LDFLAGS)
+%canon_reldir%_octave_svgconvert_LDFLAGS = \
+  $(QT_LDFLAGS) \
+  $(OCTAVE_UNICODE_EXE_LDFLAGS)
 
 %canon_reldir%_mkoctfile_SOURCES =
 
@@ -137,6 +142,9 @@
   liboctave/wrappers/libwrappers.la \
   libgnu/libgnu.la $(LIBS)
 
+%canon_reldir%_mkoctfile_LDFLAGS = \
+  $(OCTAVE_UNICODE_EXE_LDFLAGS)
+
 %canon_reldir%_mkoctfile_CPPFLAGS = \
   $(SRC_DIR_CPPFLAGS) \
   $(OCTAVE_CPPFLAGS)
@@ -150,6 +158,9 @@
   libgnu/libgnu.la \
   $(LIBS)
 
+%canon_reldir%_octave_config_LDFLAGS = \
+  $(OCTAVE_UNICODE_EXE_LDFLAGS)
+
 %canon_reldir%_octave_config_CPPFLAGS = \
   $(SRC_DIR_CPPFLAGS) \
   $(OCTAVE_CPPFLAGS)
--- a/src/octave-config.in.cc	Sat Apr 17 12:21:35 2021 +0200
+++ b/src/octave-config.in.cc	Sat Apr 17 12:21:48 2021 +0200
@@ -35,6 +35,12 @@
 #include <algorithm>
 #include <cstdlib>
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <vector>
+#  include <locale>
+#  include <codecvt>
+#endif
+
 #if ! defined (OCTAVE_PREFIX)
 #  define OCTAVE_PREFIX %OCTAVE_PREFIX%
 #endif
@@ -139,9 +145,28 @@
   vars["STARTUPFILEDIR"] = prepend_octave_home (%OCTAVE_STARTUPFILEDIR%);
 }
 
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+extern "C"
+int
+wmain (int argc, wchar_t **wargv)
+{
+  static char **argv = new char * [argc + 1];
+  std::vector<std::string> argv_str;
+
+  // convert wide character strings to multibyte UTF-8 strings
+  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_conv;
+  for (int i_arg = 0; i_arg < argc; i_arg++)
+    {
+      argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg]));
+      argv[i_arg] = &argv_str[i_arg][0];
+    }
+  argv[argc] = nullptr;
+
+#else
 int
 main (int argc, char **argv)
 {
+#endif
   initialize ();
 
   if (argc == 1)
--- a/src/octave-svgconvert.cc	Sat Apr 17 12:21:35 2021 +0200
+++ b/src/octave-svgconvert.cc	Sat Apr 17 12:21:48 2021 +0200
@@ -29,6 +29,12 @@
 
 #include <iostream>
 
+#if defined (OCTAVE_USE_WINDOWS_API)
+#  include <vector>
+#  include <locale>
+#  include <codecvt>
+#endif
+
 #include <QtCore>
 #include <QtXml>
 
@@ -815,8 +821,28 @@
     replace_polygons (parent_elt, collection[ii].first, collection[ii].second);
 }
 
-int main(int argc, char *argv[])
+#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
+extern "C"
+int
+wmain (int argc, wchar_t **wargv)
 {
+  static char **argv = new char * [argc + 1];
+  std::vector<std::string> argv_str;
+
+  // convert wide character strings to multibyte UTF-8 strings
+  std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_conv;
+  for (int i_arg = 0; i_arg < argc; i_arg++)
+    {
+      argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg]));
+      argv[i_arg] = &argv_str[i_arg][0];
+    }
+  argv[argc] = nullptr;
+
+#else
+int
+main (int argc, char **argv)
+{
+#endif
   const char *doc = "See \"octave-svgconvert -h\"";
   const char *help = "Usage:\n\
 octave-svgconvert infile fmt dpi font reconstruct outfile\n\n\