# HG changeset patch
# User Markus Mützel <markus.muetzel@gmx.de>
# Date 1549134465 -3600
#      Sat Feb 02 20:07:45 2019 +0100
# Node ID b6db0122e1b987219a8ca23e7741bf33a6ad1a62
# Parent  a7dfb00d5bf51e29857a50cbe719b9d15f9e269b
Support codepages for file streams (bug #55452).

* oct-string.[h,cc] (u8_to_encoding, u8_from_encoding): New functions.
* file-io.cc (Ffopen): Add support for input and output argument "codepage".
  (do_stream_open): Check if codepage is valid.
* oct-stream.h (base_stream): Store codepage.
* oct-stream.cc (do_scanf_conv): Convert strings from codepage.
  (do_printf_string): Convert strings to codepage.
  (base_stream::printf): Convert format string to codepage.
  (do_textscan): Pass codepage.
  (textscan): Convert strings from codepage.
* oct-stdstrm.h (octave_tstdiostream, octave_zstdiostream, octave::stream): Pass
  codepage through.
* oct-prcstrm.[cc,h] (octave_iprocstream, octave_oprocstream, create): Pass
  codepage through.
* oct-strstrm.[cc,h] (octave_istrstream, octave_ostrstream, create): Pass
  codepage through.
* io.tst (fopen): Adapt for new syntax.

diff -r a7dfb00d5bf5 -r b6db0122e1b9 libinterp/corefcn/file-io.cc
--- a/libinterp/corefcn/file-io.cc	Fri Feb 01 22:16:15 2019 -0800
+++ b/libinterp/corefcn/file-io.cc	Sat Feb 02 20:07:45 2019 +0100
@@ -52,6 +52,7 @@
 
 #include "file-ops.h"
 #include "file-stat.h"
+#include "iconv-wrappers.h"
 #include "lo-ieee.h"
 #include "lo-sysdep.h"
 #include "mkostemp-wrapper.h"
@@ -405,12 +406,21 @@
 
 static octave::stream
 do_stream_open (const std::string& name, const std::string& mode_arg,
-                const std::string& arch, int& fid)
+                const std::string& arch, const std::string& codepage, int& fid)
 {
   octave::stream retval;
 
   fid = -1;
 
+  if (codepage.compare ("utf-8"))
+  {
+    // check if codepage is valid
+    octave_iconv_open_wrapper (codepage.c_str (), "utf-8");
+    if (errno == EINVAL)
+      error ("fopen: conversion from codepage '%s' not supported",
+             codepage.c_str ());
+  }
+
   std::string mode = mode_arg;
   bool use_zlib = false;
   normalize_fopen_mode (mode, use_zlib);
@@ -440,8 +450,8 @@
 
               gzFile gzf = ::gzdopen (fd, mode.c_str ());
 
-              retval = octave_zstdiostream::create (fname, gzf, fd,
-                                                    md, flt_fmt);
+              retval = octave_zstdiostream::create (fname, gzf, fd, md,
+                                                    flt_fmt, codepage);
             }
           else
             retval.error (std::strerror (errno));
@@ -451,8 +461,8 @@
         {
           FILE *fptr = octave::sys::fopen (fname.c_str (), mode.c_str ());
 
-          retval = octave_stdiostream::create (fname, fptr, md,
-                                               flt_fmt);
+          retval = octave_stdiostream::create (fname, fptr, md, flt_fmt,
+                                               codepage);
 
           if (! fptr)
             retval.error (std::strerror (errno));
@@ -465,7 +475,8 @@
 
 static octave::stream
 do_stream_open (const octave_value& tc_name, const octave_value& tc_mode,
-                const octave_value& tc_arch, const char *fcn, int& fid)
+                const octave_value& tc_arch, const octave_value& tc_codepage,
+                const char *fcn, int& fid)
 {
   octave::stream retval;
 
@@ -474,8 +485,9 @@
   std::string name = tc_name.xstring_value ("%s: filename must be a string", fcn);
   std::string mode = tc_mode.xstring_value ("%s: file mode must be a string", fcn);
   std::string arch = tc_arch.xstring_value ("%s: architecture type must be a string", fcn);
-
-  retval = do_stream_open (name, mode, arch, fid);
+  std::string codepage = tc_codepage.xstring_value ("%s: CODEPAGE must be a string", fcn);
+
+  retval = do_stream_open (name, mode, arch, codepage, fid);
 
   return retval;
 }
@@ -485,19 +497,22 @@
 @deftypefn  {} {@var{fid} =} fopen (@var{name})
 @deftypefnx {} {@var{fid} =} fopen (@var{name}, @var{mode})
 @deftypefnx {} {@var{fid} =} fopen (@var{name}, @var{mode}, @var{arch})
+@deftypefnx {} {@var{fid} =} fopen (@var{name}, @var{mode}, @var{arch}, @var{codepage})
 @deftypefnx {} {[@var{fid}, @var{msg}] =} fopen (@dots{})
 @deftypefnx {} {@var{fid_list} =} fopen ("all")
-@deftypefnx {} {[@var{file}, @var{mode}, @var{arch}] =} fopen (@var{fid})
+@deftypefnx {} {[@var{file}, @var{mode}, @var{arch}, @var{codepage}] =} fopen (@var{fid})
 Open a file for low-level I/O or query open files and file descriptors.
 
 The first form of the @code{fopen} function opens the named file with
-the specified mode (read-write, read-only, etc.@:) and architecture
-interpretation (IEEE big endian, IEEE little endian, etc.), and returns
-an integer value that may be used to refer to the file later.  If an
-error occurs, @var{fid} is set to @minus{}1 and @var{msg} contains the
+the specified mode (read-write, read-only, etc.@:), architecture
+interpretation (IEEE big endian, IEEE little endian, etc.) and file encoding,
+and returns an integer value that may be used to refer to the file later.  If
+an error occurs, @var{fid} is set to @minus{}1 and @var{msg} contains the
 corresponding system error message.  The @var{mode} is a one or two
 character string that specifies whether the file is to be opened for
-reading, writing, or both.
+reading, writing, or both.  The @var{codepage} is a character sting with a
+valid code page identifier.  This code page is used when strings are read from
+or written to the file.
 
 The second form of the @code{fopen} function returns a vector of file ids
 corresponding to all the currently open files, excluding the
@@ -571,10 +586,6 @@
 IEEE little endian format.
 @end table
 
-@noindent
-However, conversions are currently only supported for @samp{native},
-@samp{ieee-be}, and @samp{ieee-le} formats.
-
 When opening a new file that does not yet exist, permissions will be set to
 @code{0666 - @var{umask}}.
 
@@ -590,7 +601,7 @@
 {
   int nargin = args.length ();
 
-  if (nargin < 1 || nargin > 3)
+  if (nargin < 1 || nargin > 4)
     print_usage ();
 
   octave_value_list retval = ovl (-1.0);
@@ -612,21 +623,22 @@
         {
           string_vector tmp = streams.get_info (args(0));
 
-          retval = ovl (tmp(0), tmp(1), tmp(2));
+          retval = ovl (tmp(0), tmp(1), tmp(2), tmp(3));
 
           return retval;
         }
     }
 
-  octave_value mode = (nargin == 2 || nargin == 3)
-                      ? args(1) : octave_value ("r");
-
-  octave_value arch = (nargin == 3)
-                      ? args(2) : octave_value ("native");
+  octave_value mode = (nargin > 1) ? args(1) : octave_value ("r");
+
+  octave_value arch = (nargin > 2) ? args(2) : octave_value ("native");
+
+  octave_value codepage = (nargin > 3) ? args(3) : octave_value ("utf-8");
 
   int fid = -1;
 
-  octave::stream os = do_stream_open (args(0), mode, arch, "fopen", fid);
+  octave::stream os = do_stream_open (args(0), mode, arch, codepage, "fopen",
+                                      fid);
 
   if (os)
     retval = ovl (streams.insert (os), "");
diff -r a7dfb00d5bf5 -r b6db0122e1b9 libinterp/corefcn/oct-prcstrm.cc
--- a/libinterp/corefcn/oct-prcstrm.cc	Fri Feb 01 22:16:15 2019 -0800
+++ b/libinterp/corefcn/oct-prcstrm.cc	Sat Feb 02 20:07:45 2019 +0100
@@ -31,16 +31,18 @@
 
 octave::stream
 octave_iprocstream::create (const std::string& n, std::ios::openmode arg_md,
-                            octave::mach_info::float_format ff)
+                            octave::mach_info::float_format ff,
+                            const std::string& codepage)
 {
-  return octave::stream (new octave_iprocstream (n, arg_md, ff));
+  return octave::stream (new octave_iprocstream (n, arg_md, ff, codepage));
 }
 
 octave_iprocstream::octave_iprocstream (const std::string& n,
                                         std::ios::openmode arg_md,
-                                        octave::mach_info::float_format ff)
+                                        octave::mach_info::float_format ff,
+                                        const std::string& codepage)
   : octave_stdiostream (n, octave::popen (n.c_str (), "r"),
-                        arg_md, ff, octave::pclose)
+                        arg_md, ff, codepage, octave::pclose)
 { }
 
 octave_iprocstream::~octave_iprocstream (void)
@@ -50,16 +52,18 @@
 
 octave::stream
 octave_oprocstream::create (const std::string& n, std::ios::openmode arg_md,
-                            octave::mach_info::float_format ff)
+                            octave::mach_info::float_format ff,
+                            const std::string& codepage)
 {
-  return octave::stream (new octave_oprocstream (n, arg_md, ff));
+  return octave::stream (new octave_oprocstream (n, arg_md, ff, codepage));
 }
 
 octave_oprocstream::octave_oprocstream (const std::string& n,
                                         std::ios::openmode arg_md,
-                                        octave::mach_info::float_format ff)
+                                        octave::mach_info::float_format ff,
+                                        const std::string& codepage)
   : octave_stdiostream (n, octave::popen (n.c_str (), "w"),
-                        arg_md, ff, octave::pclose)
+                        arg_md, ff, codepage, octave::pclose)
 { }
 
 octave_oprocstream::~octave_oprocstream (void)
diff -r a7dfb00d5bf5 -r b6db0122e1b9 libinterp/corefcn/oct-prcstrm.h
--- a/libinterp/corefcn/oct-prcstrm.h	Fri Feb 01 22:16:15 2019 -0800
+++ b/libinterp/corefcn/oct-prcstrm.h	Sat Feb 02 20:07:45 2019 +0100
@@ -38,7 +38,8 @@
   octave_iprocstream (const std::string& n,
                       std::ios::openmode arg_md = std::ios::in,
                       octave::mach_info::float_format flt_fmt
-                        = octave::mach_info::native_float_format ());
+                        = octave::mach_info::native_float_format (),
+                      const std::string& codepage = "utf-8");
 
   // No copying!
 
@@ -49,7 +50,8 @@
   static octave::stream
   create (const std::string& n, std::ios::openmode arg_md = std::ios::in,
           octave::mach_info::float_format flt_fmt
-            = octave::mach_info::native_float_format ());
+            = octave::mach_info::native_float_format (),
+          const std::string& codepage = "utf-8");
 
 protected:
 
@@ -64,7 +66,8 @@
   octave_oprocstream (const std::string& n,
                       std::ios::openmode arg_md = std::ios::out,
                       octave::mach_info::float_format flt_fmt
-                        = octave::mach_info::native_float_format ());
+                        = octave::mach_info::native_float_format (),
+                      const std::string& codepage = "utf-8");
 
   // No copying!
 
@@ -75,7 +78,8 @@
   static octave::stream
   create (const std::string& n, std::ios::openmode arg_md = std::ios::out,
           octave::mach_info::float_format flt_fmt
-            = octave::mach_info::native_float_format ());
+            = octave::mach_info::native_float_format (),
+          const std::string& codepage = "utf-8");
 
 protected:
 
diff -r a7dfb00d5bf5 -r b6db0122e1b9 libinterp/corefcn/oct-stdstrm.h
--- a/libinterp/corefcn/oct-stdstrm.h	Fri Feb 01 22:16:15 2019 -0800
+++ b/libinterp/corefcn/oct-stdstrm.h	Sat Feb 02 20:07:45 2019 +0100
@@ -40,8 +40,9 @@
                        std::ios::openmode m = std::ios::in | std::ios::out,
                        octave::mach_info::float_format ff
                          = octave::mach_info::native_float_format (),
+                       const std::string& codepage = "utf-8",
                        typename BUF_T::close_fcn cf = BUF_T::file_close)
-    : octave::base_stream (m, ff), nm (n), md (m),
+    : octave::base_stream (m, ff, codepage), nm (n), md (m),
       s (f ? new STREAM_T (f, cf) : nullptr), fnum (fid)
   { }
 
@@ -108,18 +109,20 @@
                       std::ios::openmode m = std::ios::in | std::ios::out,
                       octave::mach_info::float_format ff
                         = octave::mach_info::native_float_format (),
+                      const std::string& codepage = "utf-8",
                       c_file_ptr_buf::close_fcn cf = c_file_ptr_buf::file_close)
     : octave_tstdiostream<c_file_ptr_buf, io_c_file_ptr_stream, FILE *>
-       (n, f, f ? fileno (f) : -1, m, ff, cf) { }
+       (n, f, f ? fileno (f) : -1, m, ff, codepage, cf) { }
 
   static octave::stream
   create (const std::string& n, FILE *f = nullptr,
           std::ios::openmode m = std::ios::in | std::ios::out,
           octave::mach_info::float_format ff
             = octave::mach_info::native_float_format (),
+          const std::string& codepage = "utf-8",
           c_file_ptr_buf::close_fcn cf = c_file_ptr_buf::file_close)
   {
-    return octave::stream (new octave_stdiostream (n, f, m, ff, cf));
+    return octave::stream (new octave_stdiostream (n, f, m, ff, codepage, cf));
   }
 
   // No copying!
@@ -145,19 +148,22 @@
                        std::ios::openmode m = std::ios::in | std::ios::out,
                        octave::mach_info::float_format ff
                          = octave::mach_info::native_float_format (),
+                       const std::string& codepage = "utf-8",
                        c_zfile_ptr_buf::close_fcn cf
                          = c_zfile_ptr_buf::file_close)
     : octave_tstdiostream<c_zfile_ptr_buf, io_c_zfile_ptr_stream, gzFile>
-       (n, f, fid, m, ff, cf) { }
+       (n, f, fid, m, ff, codepage, cf) { }
 
   static octave::stream
   create (const std::string& n, gzFile f = nullptr, int fid = 0,
           std::ios::openmode m = std::ios::in | std::ios::out,
           octave::mach_info::float_format ff
             = octave::mach_info::native_float_format (),
+          const std::string& codepage = "utf-8",
           c_zfile_ptr_buf::close_fcn cf = c_zfile_ptr_buf::file_close)
   {
-    return octave::stream (new octave_zstdiostream (n, f, fid, m, ff, cf));
+    return octave::stream (new octave_zstdiostream (n, f, fid, m, ff, codepage,
+                                                    cf));
   }
 
   // No copying!
diff -r a7dfb00d5bf5 -r b6db0122e1b9 libinterp/corefcn/oct-stream.cc
--- a/libinterp/corefcn/oct-stream.cc	Fri Feb 01 22:16:15 2019 -0800
+++ b/libinterp/corefcn/oct-stream.cc	Sat Feb 02 20:07:45 2019 +0100
@@ -54,6 +54,7 @@
 #include "octave.h"
 #include "oct-iostrm.h"
 #include "oct-stdstrm.h"
+#include "oct-string.h"
 #include "oct-stream.h"
 #include "ov.h"
 #include "ovl.h"
@@ -1774,7 +1775,8 @@
   {
   public:
 
-    textscan (const std::string& who_arg = "textscan");
+    textscan (const std::string& who_arg = "textscan",
+              const std::string& codepage = "utf-8");
 
     // No copying!
 
@@ -1796,6 +1798,8 @@
     // What function name should be shown when reporting errors.
     std::string who;
 
+    std::string mcodepage;
+
     std::string buf;
 
     // Three cases for delim_table and delim_list
@@ -2505,13 +2509,13 @@
     return retval;             // May have returned 4 above.
   }
 
-  textscan::textscan (const std::string& who_arg)
-    : who (who_arg), buf (), whitespace_table (), delim_table (),
-      delims (), comment_style (), comment_len (0), comment_char (-2),
-      buffer_size (0), date_locale (), inf_nan (init_inf_nan ()),
-      empty_value (numeric_limits<double>::NaN ()), exp_chars ("edED"),
-      header_lines (0), treat_as_empty (), treat_as_empty_len (0),
-      whitespace (" \b\t"), eol1 ('\r'), eol2 ('\n'),
+  textscan::textscan (const std::string& who_arg, const std::string& codepage)
+    : who (who_arg), mcodepage (codepage), buf (), whitespace_table (),
+      delim_table (), delims (), comment_style (), comment_len (0),
+      comment_char (-2), buffer_size (0), date_locale (),
+      inf_nan (init_inf_nan ()), empty_value (numeric_limits<double>::NaN ()),
+      exp_chars ("edED"), header_lines (0), treat_as_empty (),
+      treat_as_empty_len (0), whitespace (" \b\t"), eol1 ('\r'), eol2 ('\n'),
       return_on_error (1), collect_output (false),
       multiple_delims_as_one (false), default_exp (true), lines (0)
   { }
@@ -3147,6 +3151,10 @@
         ends[i++] = eol2;
         val = textscan::read_until (is, delim_list, ends);
       }
+
+    // convert from codepage
+    if (mcodepage.compare ("utf-8"))
+      val = string::u8_from_encoding ("textscan", val, mcodepage);
   }
 
   // Return in VAL the run of characters from IS contained in PATTERN.
@@ -3194,6 +3202,10 @@
             is.get_undelim ();
           }
       }
+
+    // convert from codepage
+    if (mcodepage.compare ("utf-8"))
+      val = string::u8_from_encoding ("textscan", val, mcodepage);
   }
 
   // Read from IS into VAL a string of the next fmt.width characters,
@@ -3216,6 +3228,10 @@
             break;
           }
       }
+
+    // convert from codepage
+    if (mcodepage.compare ("utf-8"))
+      val = string::u8_from_encoding ("textscan", val, mcodepage);
   }
 
   //  Read a single '%...' conversion and place it in position ROW of OV.
@@ -4529,6 +4545,8 @@
 #define FINISH_CHARACTER_CONVERSION()                                   \
   do                                                                    \
     {                                                                   \
+      if (codepage ().compare ("utf-8"))                                \
+        tmp = octave::string::u8_from_encoding (who, tmp, codepage ()); \
       width = tmp.length ();                                            \
                                                                         \
       if (is)                                                           \
@@ -5306,7 +5324,7 @@
       invalid_operation (who, "reading");
     else
       {
-        textscan scanner (who);
+        textscan scanner (who, codepage ());
 
         retval = scanner.scan (*isp, fmt, ntimes, options, read_count);
       }
@@ -5557,7 +5575,7 @@
   static size_t
   do_printf_string (std::ostream& os, const printf_format_elt *elt,
                     int nsa, int sa_1, int sa_2, const std::string& arg,
-                    const std::string& who)
+                    const std::string& codepage, const std::string& who)
   {
     if (nsa > 2)
       ::error ("%s: internal error handling format", who.c_str ());
@@ -5568,12 +5586,19 @@
 
     size_t len = arg.length ();
 
+    size_t prec = (nsa > 1 ? sa_2 : (elt->prec == -1 ? len : elt->prec));
+
+    std::string print_str = prec < arg.length () ? arg.substr (0, prec) : arg;
+    if (codepage.compare ("utf-8"))
+      {
+        size_t src_len = print_str.length ();
+        print_str = string::u8_to_encoding (who, print_str, codepage);
+        len -= src_len - print_str.length ();
+      }
+
     size_t fw = (nsa > 0 ? sa_1 : (elt->fw == -1 ? len : elt->fw));
-    size_t prec = (nsa > 1 ? sa_2 : (elt->prec == -1 ? len : elt->prec));
-
-    os << std::setw (fw)
-       << (left ? std::left : std::right)
-       << (prec < len ? arg.substr (0, prec) : arg);
+
+    os << std::setw (fw) << (left ? std::left : std::right) << print_str;
 
     return len > fw ? len : fw;
   }
@@ -5846,7 +5871,8 @@
                         std::string sval = val.string_value ();
 
                         retval += do_printf_string (os, elt, nsa, sa_1,
-                                                    sa_2, sval, who);
+                                                    sa_2, sval, codepage (),
+                                                    who);
                       }
                     else
                       retval += do_numeric_printf_conv (os, elt, nsa, sa_1,
@@ -5886,10 +5912,13 @@
   }
 
   int
-  base_stream::printf (const std::string& fmt,
+  base_stream::printf (std::string fmt,
                        const octave_value_list& args,
                        const std::string& who)
   {
+    if (codepage ().compare ("utf-8"))
+      fmt = string::u8_to_encoding (who, fmt, codepage ());
+
     printf_format_list fmt_list (fmt);
 
     if (fmt_list.num_conversions () == -1)
@@ -7470,7 +7499,7 @@
 
   string_vector stream_list::get_info (int fid) const
   {
-    string_vector retval (3);
+    string_vector retval (4);
 
     if (fid < 0)
       return retval;
@@ -7495,6 +7524,7 @@
     retval(0) = os.name ();
     retval(1) = stream::mode_as_string (os.mode ());
     retval(2) = mach_info::float_format_as_string (os.float_format ());
+    retval(3) = os.codepage ();
 
     return retval;
   }
diff -r a7dfb00d5bf5 -r b6db0122e1b9 libinterp/corefcn/oct-stream.h
--- a/libinterp/corefcn/oct-stream.h	Fri Feb 01 22:16:15 2019 -0800
+++ b/libinterp/corefcn/oct-stream.h	Sat Feb 02 20:07:45 2019 +0100
@@ -67,9 +67,10 @@
   public:
 
     base_stream (std::ios::openmode arg_md = std::ios::in | std::ios::out,
-                 mach_info::float_format ff = mach_info::native_float_format ())
-      : count (0), md (arg_md), flt_fmt (ff), fail (false), open_state (true),
-        errmsg ()
+                 mach_info::float_format ff = mach_info::native_float_format (),
+                 const std::string& codepage = "utf-8")
+      : count (0), md (arg_md), flt_fmt (ff), mcodepage (codepage),
+        fail (false), open_state (true), errmsg ()
     { }
 
     // No copying!
@@ -152,6 +153,8 @@
 
     mach_info::float_format float_format (void) const { return flt_fmt; }
 
+    std::string codepage (void) const { return mcodepage; }
+
     // Set current error state and set fail to TRUE.
 
     void error (const std::string& msg);
@@ -177,6 +180,9 @@
     // Data format.
     mach_info::float_format flt_fmt;
 
+    // Code page
+    std::string mcodepage;
+
     // TRUE if an error has occurred.
     bool fail;
 
@@ -229,7 +235,7 @@
     int do_printf (printf_format_list& fmt_list, const octave_value_list& args,
                    const std::string& who /* = "printf" */);
 
-    int printf (const std::string& fmt, const octave_value_list& args,
+    int printf (std::string fmt, const octave_value_list& args,
                 const std::string& who /* = "printf" */);
 
     int puts (const std::string& s, const std::string& who /* = "puts" */);
@@ -362,6 +368,11 @@
     mach_info::float_format float_format (void) const;
 
     static std::string mode_as_string (int mode);
+    
+    std::string codepage (void)
+    {
+      return rep ? rep->codepage () : std::string ();
+    }
 
     std::istream * input_stream (void)
     {
diff -r a7dfb00d5bf5 -r b6db0122e1b9 libinterp/corefcn/oct-strstrm.cc
--- a/libinterp/corefcn/oct-strstrm.cc	Fri Feb 01 22:16:15 2019 -0800
+++ b/libinterp/corefcn/oct-strstrm.cc	Sat Feb 02 20:07:45 2019 +0100
@@ -50,21 +50,27 @@
 
 octave::stream
 octave_istrstream::create (const char *data, std::ios::openmode arg_md,
-                           octave::mach_info::float_format flt_fmt)
+                           octave::mach_info::float_format flt_fmt,
+                           const std::string& codepage)
 {
-  return octave::stream (new octave_istrstream (data, arg_md, flt_fmt));
+  return octave::stream (new octave_istrstream (data, arg_md, flt_fmt,
+                                                codepage));
 }
 
 octave::stream
 octave_istrstream::create (const std::string& data, std::ios::openmode arg_md,
-                           octave::mach_info::float_format flt_fmt)
+                           octave::mach_info::float_format flt_fmt,
+                           const std::string& codepage)
 {
-  return octave::stream (new octave_istrstream (data, arg_md, flt_fmt));
+  return octave::stream (new octave_istrstream (data, arg_md, flt_fmt,
+                                                codepage));
 }
 
 octave::stream
 octave_ostrstream::create (std::ios::openmode arg_md,
-                           octave::mach_info::float_format flt_fmt)
+                           octave::mach_info::float_format flt_fmt,
+                           const std::string& codepage)
 {
-  return octave::stream (new octave_ostrstream (arg_md, flt_fmt));
+  return octave::stream (new octave_ostrstream (arg_md, flt_fmt,
+                                                codepage));
 }
diff -r a7dfb00d5bf5 -r b6db0122e1b9 libinterp/corefcn/oct-strstrm.h
--- a/libinterp/corefcn/oct-strstrm.h	Fri Feb 01 22:16:15 2019 -0800
+++ b/libinterp/corefcn/oct-strstrm.h	Sat Feb 02 20:07:45 2019 +0100
@@ -37,8 +37,9 @@
 
   octave_base_strstream (std::ios::openmode m = std::ios::out,
                          octave::mach_info::float_format ff
-                           = octave::mach_info::native_float_format ())
-    : octave::base_stream (m, ff) { }
+                           = octave::mach_info::native_float_format (),
+                         const std::string& codepage = "utf-8")
+    : octave::base_stream (m, ff, codepage) { }
 
   // No copying!
 
@@ -79,14 +80,16 @@
   octave_istrstream (const char *data,
                      std::ios::openmode arg_md = std::ios::out,
                      octave::mach_info::float_format ff
-                       = octave::mach_info::native_float_format ())
-    : octave_base_strstream (arg_md, ff), is (data) { }
+                       = octave::mach_info::native_float_format (),
+                     const std::string& codepage = "utf-8")
+    : octave_base_strstream (arg_md, ff, codepage), is (data) { }
 
   octave_istrstream (const std::string& data,
                      std::ios::openmode arg_md = std::ios::out,
                      octave::mach_info::float_format ff
-                       = octave::mach_info::native_float_format ())
-    : octave_base_strstream (arg_md, ff), is (data) { }
+                       = octave::mach_info::native_float_format (),
+                     const std::string& codepage = "utf-8")
+    : octave_base_strstream (arg_md, ff, codepage), is (data) { }
 
   // No copying!
 
@@ -104,12 +107,14 @@
   static octave::stream
   create (const char *data, std::ios::openmode arg_md = std::ios::out,
           octave::mach_info::float_format ff
-            = octave::mach_info::native_float_format ());
+            = octave::mach_info::native_float_format (),
+          const std::string& codepage = "utf-8");
 
   static octave::stream
   create (const std::string& data, std::ios::openmode arg_md = std::ios::out,
           octave::mach_info::float_format ff
-            = octave::mach_info::native_float_format ());
+            = octave::mach_info::native_float_format (),
+          const std::string& codepage = "utf-8");
 
   // Return nonzero if EOF has been reached on this stream.
 
@@ -139,8 +144,9 @@
 
   octave_ostrstream (std::ios::openmode arg_md = std::ios::out,
                      octave::mach_info::float_format ff
-                       = octave::mach_info::native_float_format ())
-    : octave_base_strstream (arg_md, ff), os () { }
+                       = octave::mach_info::native_float_format (),
+                     const std::string& codepage = "utf-8")
+    : octave_base_strstream (arg_md, ff, codepage), os () { }
 
   // No copying!
 
@@ -157,7 +163,8 @@
   static octave::stream
   create (std::ios::openmode arg_md = std::ios::out,
           octave::mach_info::float_format ff
-            = octave::mach_info::native_float_format ());
+            = octave::mach_info::native_float_format (),
+          const std::string& codepage = "utf-8");
 
   // Return nonzero if EOF has been reached on this stream.
 
diff -r a7dfb00d5bf5 -r b6db0122e1b9 liboctave/util/oct-string.cc
--- a/liboctave/util/oct-string.cc	Fri Feb 01 22:16:15 2019 -0800
+++ b/liboctave/util/oct-string.cc	Sat Feb 02 20:07:45 2019 +0100
@@ -34,6 +34,8 @@
 #include "Array.h"
 #include "lo-ieee.h"
 #include "lo-mappers.h"
+#include "uniconv-wrappers.h"
+#include "unwind-prot.h"
 
 template <typename T>
 static bool
@@ -484,6 +486,70 @@
   return val;
 }
 
+std::string
+octave::string::u8_to_encoding (const std::string& who,
+                                const std::string& u8_string,
+                                const std::string& encoding)
+{
+  const uint8_t *src = reinterpret_cast<const uint8_t *>
+                       (u8_string.c_str ());
+  size_t srclen = u8_string.length ();
+
+  size_t length;
+  char *native_str = octave_u8_conv_to_encoding (encoding.c_str (), src,
+                                                 srclen, &length);
+
+  if (! native_str)
+    {
+      if (errno == ENOSYS)
+        (*current_liboctave_error_handler)
+          ("%s: iconv() is not supported. Installing GNU libiconv and then "
+           "re-compiling Octave could fix this.", who.c_str ());
+      else
+        (*current_liboctave_error_handler)
+          ("%s: converting from UTF-8 to codepage '%s' failed: %s",
+           who.c_str (), encoding.c_str (), std::strerror (errno));
+    }
+
+  octave::unwind_protect frame;
+  frame.add_fcn (::free, static_cast<void *> (native_str));
+
+  std::string retval = std::string (native_str, length);
+
+  return retval;
+}
+
+std::string
+octave::string::u8_from_encoding (const std::string& who,
+                                  const std::string& native_string,
+                                  const std::string& encoding)
+{
+  const char *src = native_string.c_str ();
+  size_t srclen = native_string.length ();
+
+  size_t length;
+  uint8_t *utf8_str = octave_u8_conv_from_encoding (encoding.c_str (), src,
+                                                    srclen, &length);
+  if (! utf8_str)
+    {
+      if (errno == ENOSYS)
+        (*current_liboctave_error_handler)
+          ("%s: iconv() is not supported. Installing GNU libiconv and then "
+           "re-compiling Octave could fix this.", who.c_str ());
+      else
+        (*current_liboctave_error_handler)
+          ("%s: converting from codepage '%s' to UTF-8 failed: %s",
+           who.c_str (), encoding.c_str (), std::strerror (errno));
+    }
+
+  octave::unwind_protect frame;
+  frame.add_fcn (::free, static_cast<void *> (utf8_str));
+
+  std::string retval = std::string (reinterpret_cast<char *> (utf8_str), length);
+
+  return retval;
+}
+
 template <typename T>
 std::string
 rational_approx (T val, int len)
diff -r a7dfb00d5bf5 -r b6db0122e1b9 liboctave/util/oct-string.h
--- a/liboctave/util/oct-string.h	Fri Feb 01 22:16:15 2019 -0800
+++ b/liboctave/util/oct-string.h	Sat Feb 02 20:07:45 2019 +0100
@@ -125,6 +125,14 @@
 
     extern OCTAVE_API Complex
     str2double (const std::string& str_arg);
+
+    extern OCTAVE_API std::string
+    u8_to_encoding (const std::string& who, const std::string& u8_string,
+                    const std::string& encoding);
+
+    extern OCTAVE_API std::string
+    u8_from_encoding (const std::string& who, const std::string& native_string,
+                      const std::string& encoding);
   }
 }
 
diff -r a7dfb00d5bf5 -r b6db0122e1b9 test/io.tst
--- a/test/io.tst	Fri Feb 01 22:16:15 2019 -0800
+++ b/test/io.tst	Sat Feb 02 20:07:45 2019 +0100
@@ -494,7 +494,7 @@
 %! assert (__prog_output_assert__ ("error:"));
 
 %!error <Invalid call to fopen> fopen ()
-%!error <Invalid call to fopen> fopen ("foo", "wb", "native", 1)
+%!error <Invalid call to fopen> fopen ("foo", "wb", "native", "utf-8", 1)
 
 %!error fclose (0)
 %!error <Invalid call to fclose> fclose (1, 2)
