changeset 25993:f75bb9d659e0

eliminate global and file-scope static variables from load-save.cc (bug #54571) * interpreter-private.h, interpreter-private.cc (__get_load_save_system__): New function. * interpreter.h, interpreter.cc (interpreter::m_load_save_system, interpreter::get_load_save_system): New member variable and access function. * load-save.h, load-save.cc: Rewrite to use class for load/save configuration variables and functions. Change all uses.
author John W. Eaton <jwe@octave.org>
date Wed, 31 Oct 2018 18:22:23 -0400
parents 2d739bbe744b
children f881d3e271d2
files libinterp/corefcn/interpreter-private.cc libinterp/corefcn/interpreter-private.h libinterp/corefcn/interpreter.cc libinterp/corefcn/interpreter.h libinterp/corefcn/load-save.cc libinterp/corefcn/load-save.h
diffstat 6 files changed, 1717 insertions(+), 1426 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/interpreter-private.cc	Wed Oct 31 14:29:03 2018 -0700
+++ b/libinterp/corefcn/interpreter-private.cc	Wed Oct 31 18:22:23 2018 -0400
@@ -36,6 +36,7 @@
 #include "interpreter-private.h"
 #include "interpreter.h"
 #include "load-path.h"
+#include "load-save.h"
 #include "ov-classdef.h"
 #include "pager.h"
 #include "symtab.h"
@@ -90,6 +91,13 @@
     return interp.get_load_path ();
   }
 
+  load_save_system& __get_load_save_system__ (const std::string& who)
+  {
+    interpreter& interp = __get_interpreter__ (who);
+
+    return interp.get_load_save_system ();
+  }
+
   type_info& __get_type_info__ (const std::string& who)
   {
     interpreter& interp = __get_interpreter__ (who);
--- a/libinterp/corefcn/interpreter-private.h	Wed Oct 31 14:29:03 2018 -0700
+++ b/libinterp/corefcn/interpreter-private.h	Wed Oct 31 18:22:23 2018 -0400
@@ -42,6 +42,7 @@
   class input_system;
   class interpreter;
   class load_path;
+  class load_save_system;
   class output_system;
   class tree_evaluator;
   class type_info;
@@ -56,6 +57,8 @@
 
   extern load_path& __get_load_path__ (const std::string& who);
 
+  extern load_save_system& __get_load_save_system__ (const std::string& who);
+
   extern output_system& __get_output_system__ (const std::string& who);
 
   extern type_info& __get_type_info__ (const std::string& who);
--- a/libinterp/corefcn/interpreter.cc	Wed Oct 31 14:29:03 2018 -0700
+++ b/libinterp/corefcn/interpreter.cc	Wed Oct 31 18:22:23 2018 -0400
@@ -364,6 +364,7 @@
       m_output_system (*this),
       m_dynamic_loader (*this),
       m_load_path (),
+      m_load_save_system (*this),
       m_type_info (),
       m_symbol_table (),
       m_evaluator (*this),
@@ -410,8 +411,6 @@
 
     octave_ieee_init ();
 
-    octave_prepare_hdf5 ();
-
     initialize_xerbla_error_handler ();
 
     initialize_error_handlers ();
@@ -1031,8 +1030,6 @@
 
     OCTAVE_SAFE_CALL (sysdep_cleanup, ());
 
-    OCTAVE_SAFE_CALL (octave_finalize_hdf5, ());
-
     OCTAVE_SAFE_CALL (flush_stdout, ());
 
     // Don't call singleton_cleanup_list::cleanup until we have the
@@ -1178,16 +1175,18 @@
 
     m_evaluator.PS4 ("");
 
+    m_load_save_system.crash_dumps_octave_core (false);
+    m_load_save_system.save_default_options ("-mat-binary");
+
     Fbeep_on_error (octave_value (true));
     Fconfirm_recursive_rmdir (octave_value (false));
-    Fcrash_dumps_octave_core (octave_value (false));
+
     Fdisable_diagonal_matrix (octave_value (true));
     Fdisable_permutation_matrix (octave_value (true));
     Fdisable_range (octave_value (true));
     Ffixed_point_format (octave_value (true));
     Fhistory_timestamp_format_string (octave_value ("%%-- %D %I:%M %p --%%"));
     Fprint_empty_dimensions (octave_value (false));
-    Fsave_default_options (octave_value ("-mat-binary"));
     Fstruct_levels_to_print (octave_value (0));
 
     disable_warning ("Octave:abbreviated-property-match");
--- a/libinterp/corefcn/interpreter.h	Wed Oct 31 14:29:03 2018 -0700
+++ b/libinterp/corefcn/interpreter.h	Wed Oct 31 18:22:23 2018 -0400
@@ -37,6 +37,7 @@
 #include "help.h"
 #include "input.h"
 #include "load-path.h"
+#include "load-save.h"
 #include "oct-stream.h"
 #include "ov-classdef.h"
 #include "ov-typeinfo.h"
@@ -182,6 +183,11 @@
       return m_load_path;
     }
 
+    load_save_system& get_load_save_system (void)
+    {
+      return m_load_save_system;
+    }
+
     symbol_table& get_symbol_table (void)
     {
       return m_symbol_table;
@@ -276,6 +282,8 @@
 
     load_path m_load_path;
 
+    load_save_system m_load_save_system;
+
     type_info m_type_info;
 
     symbol_table m_symbol_table;
--- a/libinterp/corefcn/load-save.cc	Wed Oct 31 14:29:03 2018 -0700
+++ b/libinterp/corefcn/load-save.cc	Wed Oct 31 18:22:23 2018 -0400
@@ -56,6 +56,7 @@
 #include "defun.h"
 #include "error.h"
 #include "errwarn.h"
+#include "interpreter.h"
 #include "interpreter-private.h"
 #include "load-path.h"
 #include "load-save.h"
@@ -90,465 +91,1497 @@
 #  include "zfstream.h"
 #endif
 
-// Write octave-workspace file if Octave crashes or is killed by a signal.
-static bool Vcrash_dumps_octave_core = true;
-
-// The maximum amount of memory (in kilobytes) that we will attempt to
-// write to the Octave core file.
-static double Voctave_core_file_limit = -1.0;
-
-// The name of the Octave core file.
-static std::string Voctave_core_file_name = "octave-workspace";
-
-// The default output format.  May be one of "binary", "text",
-// "mat-binary", or "hdf5".
-static std::string Vsave_default_options = "-text";
-
-// The output format for Octave core files.
-static std::string Voctave_core_file_options = "-binary";
-
-static std::string
-default_save_header_format (void)
+namespace octave
 {
-  return
-    std::string ("# Created by Octave " OCTAVE_VERSION
-                 ", %a %b %d %H:%M:%S %Y %Z <")
-    + octave::sys::env::get_user_name ()
-    + '@'
-    + octave::sys::env::get_host_name ()
-    + '>';
-}
+  OCTAVE_NORETURN static
+  void
+  err_file_open (const std::string& fcn, const std::string& file)
+  {
+    if (fcn == "load")
+      error ("%s: unable to open input file '%s'", fcn.c_str (), file.c_str ());
+    else if (fcn == "save")
+      error ("%s: unable to open output file '%s'", fcn.c_str (), file.c_str ());
+    else
+      error ("%s: unable to open file '%s'", fcn.c_str (), file.c_str ());
+  }
 
-// The format string for the comment line at the top of text-format
-// save files.  Passed to strftime.  Should begin with '#' and contain
-// no newline characters.
-static std::string Vsave_header_format_string = default_save_header_format ();
-
-OCTAVE_NORETURN static
-void
-err_file_open (const std::string& fcn, const std::string& file)
-{
-  if (fcn == "load")
-    error ("%s: unable to open input file '%s'", fcn.c_str (), file.c_str ());
-  else if (fcn == "save")
-    error ("%s: unable to open output file '%s'", fcn.c_str (), file.c_str ());
-  else
-    error ("%s: unable to open file '%s'", fcn.c_str (), file.c_str ());
-}
+  // Return TRUE if NAME matches one of the given globbing PATTERNS.
 
-// Install a variable with name NAME and the value VAL in the
-// symbol table.  If GLOBAL is TRUE, make the variable global.
+  static bool
+  matches_patterns (const string_vector& patterns, int pat_idx,
+                    int num_pat, const std::string& name)
+  {
+    for (int i = pat_idx; i < num_pat; i++)
+      {
+        glob_match pattern (patterns[i]);
 
-static void
-install_loaded_variable (const std::string& name,
-                         const octave_value& val,
-                         bool global, const std::string& /*doc*/)
-{
-  octave::symbol_table& symtab
-    = octave::__get_symbol_table__ ("install_loaded_variable");
+        if (pattern.match (name))
+          return true;
+      }
 
-  octave::symbol_scope scope
-    = symtab.require_current_scope ("install_loaded_variable");
-
-  if (global)
-    {
-      octave::symbol_record sym = scope.find_symbol (name);
+    return false;
+  }
 
-      if (! sym.is_global ())
-        {
-          octave::symbol_scope global_scope = symtab.global_scope ();
-          octave::symbol_record global_sym = global_scope.find_symbol (name);
-
-          sym.bind_fwd_rep (global_scope.get_rep (), global_sym);
-        }
-    }
-
-  scope.assign (name, val);
-}
-
-// Return TRUE if NAME matches one of the given globbing PATTERNS.
+  static int
+  read_binary_file_header (std::istream& is, bool& swap,
+                           mach_info::float_format& flt_fmt,
+                           bool quiet = false)
+  {
+    const int magic_len = 10;
+    char magic[magic_len+1];
+    is.read (magic, magic_len);
+    magic[magic_len] = '\0';
 
-static bool
-matches_patterns (const string_vector& patterns, int pat_idx,
-                  int num_pat, const std::string& name)
-{
-  for (int i = pat_idx; i < num_pat; i++)
-    {
-      glob_match pattern (patterns[i]);
-
-      if (pattern.match (name))
-        return true;
-    }
-
-  return false;
-}
+    if (strncmp (magic, "Octave-1-L", magic_len) == 0)
+      swap = mach_info::words_big_endian ();
+    else if (strncmp (magic, "Octave-1-B", magic_len) == 0)
+      swap = ! mach_info::words_big_endian ();
+    else
+      {
+        if (! quiet)
+          error ("load: unable to read binary file");
 
-int
-read_binary_file_header (std::istream& is, bool& swap,
-                         octave::mach_info::float_format& flt_fmt, bool quiet)
-{
-  const int magic_len = 10;
-  char magic[magic_len+1];
-  is.read (magic, magic_len);
-  magic[magic_len] = '\0';
+        return -1;
+      }
 
-  if (strncmp (magic, "Octave-1-L", magic_len) == 0)
-    swap = octave::mach_info::words_big_endian ();
-  else if (strncmp (magic, "Octave-1-B", magic_len) == 0)
-    swap = ! octave::mach_info::words_big_endian ();
-  else
-    {
-      if (! quiet)
-        error ("load: unable to read binary file");
+    char tmp = 0;
+    is.read (&tmp, 1);
+
+    flt_fmt = mopt_digit_to_float_format (tmp);
 
-      return -1;
-    }
-
-  char tmp = 0;
-  is.read (&tmp, 1);
-
-  flt_fmt = mopt_digit_to_float_format (tmp);
+    if (flt_fmt == mach_info::flt_fmt_unknown)
+      {
+        if (! quiet)
+          error ("load: unrecognized binary format!");
 
-  if (flt_fmt == octave::mach_info::flt_fmt_unknown)
-    {
-      if (! quiet)
-        error ("load: unrecognized binary format!");
+        return -1;
+      }
 
-      return -1;
-    }
-
-  return 0;
-}
+    return 0;
+  }
 
 #if defined (HAVE_ZLIB)
-static bool
-check_gzip_magic (const std::string& fname)
-{
-  bool retval = false;
+  static bool
+  check_gzip_magic (const std::string& fname)
+  {
+    bool retval = false;
 
-  std::string ascii_fname = octave::sys::get_ASCII_filename (fname);
+    std::string ascii_fname = sys::get_ASCII_filename (fname);
 
-  std::ifstream file (ascii_fname.c_str (),
-                      std::ios::in | std::ios::binary);
+    std::ifstream file (ascii_fname.c_str (),
+                        std::ios::in | std::ios::binary);
 
-  unsigned char magic[2];
-  if (file.read (reinterpret_cast<char *> (&magic[0]), 2)
-      && magic[0] == 0x1f && magic[1] == 0x8b)
-    retval = true;
+    unsigned char magic[2];
+    if (file.read (reinterpret_cast<char *> (&magic[0]), 2)
+        && magic[0] == 0x1f && magic[1] == 0x8b)
+      retval = true;
 
-  file.close ();
+    file.close ();
 
-  return retval;
-}
+    return retval;
+  }
 #endif
 
-static load_save_format
-get_file_format (std::istream& file, const std::string& filename)
-{
-  load_save_format retval = LS_UNKNOWN;
+  static std::string
+  find_file_to_load (const std::string& name, const std::string& orig_name)
+  {
+    std::string fname = find_data_file_in_load_path ("load", name, true);
+
+    size_t dot_pos = fname.rfind ('.');
+    size_t sep_pos = fname.find_last_of (sys::file_ops::dir_sep_chars ());
+
+    if (dot_pos == std::string::npos
+        || (sep_pos != std::string::npos && dot_pos < sep_pos))
+      {
+        // Either no '.' in name or no '.' appears after last directory
+        // separator.
+
+        sys::file_stat fs (fname);
 
-  octave::mach_info::float_format flt_fmt = octave::mach_info::flt_fmt_unknown;
+        if (! (fs.exists () && fs.is_reg ()))
+          fname = find_file_to_load (fname + ".mat", orig_name);
+      }
+    else
+      {
+        sys::file_stat fs (fname);
 
-  bool swap = false;
+        if (! (fs.exists () && fs.is_reg ()))
+          {
+            fname = "";
+
+            error ("load: unable to find file %s", orig_name.c_str ());
+          }
+      }
+
+    return fname;
+  }
+
+  // Return TRUE if PATTERN has any special globbing chars in it.
 
-  if (read_binary_file_header (file, swap, flt_fmt, true) == 0)
-    retval = LS_BINARY;
-  else
-    {
-      file.clear ();
-      file.seekg (0, std::ios::beg);
+  static bool
+  glob_pattern_p (const std::string& pattern)
+  {
+    int open = 0;
+
+    int len = pattern.length ();
 
-      int32_t mopt, nr, nc, imag, len;
+    for (int i = 0; i < len; i++)
+      {
+        char c = pattern[i];
+
+        switch (c)
+          {
+          case '?':
+          case '*':
+            return true;
 
-      int err = read_mat_file_header (file, swap, mopt, nr, nc, imag, len,
-                                      true);
+          case '[':       // Only accept an open brace if there is a close
+            open++;       // brace to match it.  Bracket expressions must be
+            continue;     // complete, according to Posix.2
+
+          case ']':
+            if (open)
+              return true;
+            continue;
 
-      if (! err)
-        retval = LS_MAT_BINARY;
-      else
-        {
-          file.clear ();
-          file.seekg (0, std::ios::beg);
+          case '\\':
+            if (i == len - 1)
+              return false;
+            continue;
 
-          err = read_mat5_binary_file_header (file, swap, true, filename);
+          default:
+            continue;
+          }
+      }
+
+    return false;
+  }
 
-          if (! err)
-            {
-              file.clear ();
-              file.seekg (0, std::ios::beg);
-              retval = LS_MAT5_BINARY;
-            }
-          else
-            {
-              file.clear ();
-              file.seekg (0, std::ios::beg);
+  load_save_system::load_save_system (interpreter& interp)
+    : m_interpreter (interp),
+      m_crash_dumps_octave_core (true),
+      m_octave_core_file_limit (-1.0),
+      m_octave_core_file_name ("octave-workspace"),
+      m_save_default_options ("-text"),
+      m_octave_core_file_options ("-binary"),
+      m_save_header_format_string (init_save_header_format ())
+  {
+#if defined (HAVE_HDF5)
+    H5dont_atexit ();
+#endif
+  }
 
-              std::string name_val = extract_keyword (file, "name");
-              std::string type_val = extract_keyword (file, "type");
+  load_save_system::~load_save_system (void)
+  {
+#if defined (HAVE_HDF5)
+    H5close ();
+#endif
+  }
 
-              if (name_val.empty () != true && type_val.empty () != true)
-                retval = LS_TEXT;
-              else
-                {
-                  file.clear ();
-                  file.seekg (0, std::ios::beg);
+  octave_value
+  load_save_system::crash_dumps_octave_core (const octave_value_list& args,
+                                             int nargout)
+  {
+    return set_internal_variable (m_crash_dumps_octave_core, args, nargout,
+                                  "crash_dumps_octave_core");
+  }
+
+  octave_value
+  load_save_system::octave_core_file_limit (const octave_value_list& args,
+                                            int nargout)
+  {
+    return set_internal_variable (m_octave_core_file_limit, args, nargout,
+                                  "octave_core_file_limit");
+  }
 
-                  // FIXME: looks_like_mat_ascii_file does not check to see
-                  // whether the file contains numbers.  It just skips comments
-                  // and checks for the same number of words on each line.  We
-                  // may need a better check here.  The best way to do that
-                  // might be just to try to read the file and see if it works.
+  octave_value
+  load_save_system::octave_core_file_name (const octave_value_list& args,
+                                           int nargout)
+  {
+    return set_internal_variable (m_octave_core_file_name, args, nargout,
+                                  "octave_core_file_name", false);
+  }
+
+  octave_value
+  load_save_system::save_default_options (const octave_value_list& args,
+                                          int nargout)
+  {
+    return set_internal_variable (m_save_default_options, args, nargout,
+                                  "save_default_options", false);
+  }
 
-                  if (looks_like_mat_ascii_file (file, filename))
-                    retval = LS_MAT_ASCII;
-                }
-            }
-        }
-    }
+  octave_value
+  load_save_system::octave_core_file_options (const octave_value_list& args,
+                                              int nargout)
+  {
+    return set_internal_variable (m_octave_core_file_options, args, nargout,
+                                  "octave_core_file_options", false);
+  }
 
-  return retval;
-}
+  octave_value
+  load_save_system::save_header_format_string (const octave_value_list& args,
+                                               int nargout)
+  {
+    return set_internal_variable (m_save_header_format_string, args, nargout,
+                                  "save_header_format_string");
+  }
 
-static load_save_format
-get_file_format (const std::string& fname, const std::string& orig_fname,
-                 bool& use_zlib, bool quiet = false)
-{
-  load_save_format retval = LS_UNKNOWN;
+  load_save_format
+  load_save_system::get_file_format (const std::string& fname,
+                                     const std::string& orig_fname,
+                                     bool& use_zlib, bool quiet)
+  {
+    load_save_format retval = UNKNOWN;
 
-  std::string ascii_fname = octave::sys::get_ASCII_filename (fname);
+    std::string ascii_fname = sys::get_ASCII_filename (fname);
 
 #if defined (HAVE_HDF5)
-  // check this before we open the file
-  if (H5Fis_hdf5 (ascii_fname.c_str ()) > 0)
-    return LS_HDF5;
+    // check this before we open the file
+    if (H5Fis_hdf5 (ascii_fname.c_str ()) > 0)
+      return HDF5;
 #endif
 
 #if defined (HAVE_ZLIB)
-  use_zlib = check_gzip_magic (fname);
+    use_zlib = check_gzip_magic (fname);
 #else
-  use_zlib = false;
+    use_zlib = false;
+#endif
+
+    if (! use_zlib)
+      {
+        std::ifstream file (ascii_fname.c_str (),
+                            std::ios::in | std::ios::binary);
+        if (file)
+          {
+            retval = get_file_format (file, orig_fname);
+            file.close ();
+          }
+        else if (! quiet)
+          err_file_open ("load", orig_fname);
+      }
+#if defined (HAVE_ZLIB)
+    else
+      {
+        gzifstream gzfile (fname.c_str (), std::ios::in | std::ios::binary);
+        if (gzfile)
+          {
+            retval = get_file_format (gzfile, orig_fname);
+            gzfile.close ();
+          }
+        else if (! quiet)
+          err_file_open ("load", orig_fname);
+      }
+#endif
+
+    return retval;
+  }
+
+  octave_value
+  load_save_system::load_vars (std::istream& stream,
+                               const std::string& orig_fname,
+                               const load_save_format& fmt,
+                               mach_info::float_format flt_fmt,
+                               bool list_only, bool swap, bool verbose,
+                               const string_vector& argv, int argv_idx,
+                               int argc, int nargout)
+  {
+    octave_value retval;
+
+    octave_scalar_map retstruct;
+
+    std::ostringstream output_buf;
+    std::list<std::string> symbol_names;
+
+    octave_idx_type count = 0;
+
+    for (;;)
+      {
+        bool global = false;
+        octave_value tc;
+
+        std::string name;
+        std::string doc;
+
+        switch (fmt.type ())
+          {
+          case TEXT:
+            name = read_text_data (stream, orig_fname, global, tc, count);
+            break;
+
+          case BINARY:
+            name = read_binary_data (stream, swap, flt_fmt, orig_fname,
+                                     global, tc, doc);
+            break;
+
+          case MAT_ASCII:
+            name = read_mat_ascii_data (stream, orig_fname, tc);
+            break;
+
+          case MAT_BINARY:
+            name = read_mat_binary_data (stream, orig_fname, tc);
+            break;
+
+#if defined (HAVE_HDF5)
+          case HDF5:
+            name = read_hdf5_data (stream, orig_fname, global, tc, doc,
+                                   argv, argv_idx, argc);
+            break;
 #endif
 
-  if (! use_zlib)
-    {
-      std::ifstream file (ascii_fname.c_str (),
-                          std::ios::in | std::ios::binary);
-      if (file)
-        {
-          retval = get_file_format (file, orig_fname);
-          file.close ();
-        }
-      else if (! quiet)
-        err_file_open ("load", orig_fname);
-    }
-#if defined (HAVE_ZLIB)
-  else
-    {
-      gzifstream gzfile (fname.c_str (), std::ios::in | std::ios::binary);
-      if (gzfile)
-        {
-          retval = get_file_format (gzfile, orig_fname);
-          gzfile.close ();
-        }
-      else if (! quiet)
-        err_file_open ("load", orig_fname);
-    }
+          case MAT5_BINARY:
+          case MAT7_BINARY:
+            name = read_mat5_binary_element (stream, orig_fname, swap,
+                                             global, tc);
+            break;
+
+          default:
+            err_unrecognized_data_fmt ("load");
+            break;
+          }
+
+        if (stream.eof () || name.empty ())
+          break;
+        else
+          {
+            if (! tc.is_defined ())
+              error ("load: unable to load variable '%s'", name.c_str ());
+
+            if (fmt.type () == MAT_ASCII && argv_idx < argc)
+              warning ("load: loaded ASCII file '%s' -- ignoring extra args",
+                       orig_fname.c_str ());
+
+            if (fmt.type () == MAT_ASCII
+                || argv_idx == argc
+                || matches_patterns (argv, argv_idx, argc, name))
+              {
+                count++;
+                if (list_only)
+                  {
+                    if (verbose)
+                      {
+                        if (count == 1)
+                          output_buf
+                            << "type               rows   cols   name\n"
+                            << "====               ====   ====   ====\n";
+
+                        output_buf
+                          << std::setiosflags (std::ios::left)
+                          << std::setw (16) << tc.type_name ().c_str ()
+                          << std::setiosflags (std::ios::right)
+                          << std::setw (7) << tc.rows ()
+                          << std::setw (7) << tc.columns ()
+                          << "   " << name << "\n";
+                      }
+                    else
+                      symbol_names.push_back (name);
+                  }
+                else
+                  {
+                    if (nargout == 1)
+                      {
+                        if (fmt.type () == MAT_ASCII)
+                          retval = tc;
+                        else
+                          retstruct.assign (name, tc);
+                      }
+                    else
+                      install_loaded_variable (name, tc, global, doc);
+                  }
+              }
+
+            // Only attempt to read one item from a headless text file.
+
+            if (fmt.type () == MAT_ASCII)
+              break;
+          }
+      }
+
+    if (list_only && count)
+      {
+        if (verbose)
+          {
+            std::string msg = output_buf.str ();
+
+            if (nargout > 0)
+              retval = msg;
+            else
+              octave_stdout << msg;
+          }
+        else
+          {
+            if (nargout  > 0)
+              retval = Cell (string_vector (symbol_names));
+            else
+              {
+                string_vector names (symbol_names);
+
+                names.list_in_columns (octave_stdout);
+
+                octave_stdout << "\n";
+              }
+          }
+      }
+    else if (retstruct.nfields () != 0)
+      retval = retstruct;
+
+    return retval;
+  }
+
+  string_vector
+  load_save_system::parse_save_options (const string_vector& argv,
+                                        load_save_format& fmt, bool& append,
+                                        bool& save_as_floats, bool& use_zlib)
+  {
+#if ! defined (HAVE_ZLIB)
+    octave_unused_parameter (use_zlib);
 #endif
 
-  return retval;
-}
+    string_vector retval;
+    int argc = argv.numel ();
+
+    bool do_double = false;
+    bool do_tabs = false;
 
-octave_value
-do_load (std::istream& stream, const std::string& orig_fname,
-         load_save_format format, octave::mach_info::float_format flt_fmt,
-         bool list_only, bool swap, bool verbose,
-         const string_vector& argv, int argv_idx, int argc, int nargout)
-{
-  octave_value retval;
+    for (int i = 0; i < argc; i++)
+      {
+        if (argv[i] == "-append")
+          {
+            append = true;
+          }
+        else if (argv[i] == "-ascii" || argv[i] == "-a")
+          {
+            fmt.set_type (MAT_ASCII);
+          }
+        else if (argv[i] == "-double")
+          {
+            do_double = true;
+          }
+        else if (argv[i] == "-tabs")
+          {
+            do_tabs = true;
+          }
+        else if (argv[i] == "-text" || argv[i] == "-t")
+          {
+            fmt.set_type (TEXT);
+          }
+        else if (argv[i] == "-binary" || argv[i] == "-b")
+          {
+            fmt.set_type (BINARY);
+          }
+        else if (argv[i] == "-hdf5" || argv[i] == "-h")
+          {
+#if defined (HAVE_HDF5)
+            fmt.set_type (HDF5);
+#else
+            err_disabled_feature ("save", "HDF5");
+#endif
+          }
+        else if (argv[i] == "-mat-binary" || argv[i] == "-mat"
+                 || argv[i] == "-m" || argv[i] == "-6" || argv[i] == "-v6"
+                 || argv[i] == "-V6")
+          {
+            fmt.set_type (MAT5_BINARY);
+          }
+#if defined (HAVE_ZLIB)
+        else if (argv[i] == "-mat7-binary" || argv[i] == "-7"
+                 || argv[i] == "-v7" || argv[i] == "-V7")
+          {
+            fmt.set_type (MAT7_BINARY);
+          }
+#endif
+        else if (argv[i] == "-mat4-binary" || argv[i] == "-V4"
+                 || argv[i] == "-v4" || argv[i] == "-4")
+          {
+            fmt.set_type (MAT_BINARY);
+          }
+        else if (argv[i] == "-float-binary" || argv[i] == "-f")
+          {
+            fmt.set_type (BINARY);
+            save_as_floats = true;
+          }
+        else if (argv[i] == "-float-hdf5")
+          {
+#if defined (HAVE_HDF5)
+            fmt.set_type (HDF5);
+            save_as_floats = true;
+#else
+            err_disabled_feature ("save", "HDF5");
+#endif
+          }
+#if defined (HAVE_ZLIB)
+        else if (argv[i] == "-zip" || argv[i] == "-z")
+          {
+            use_zlib = true;
+          }
+#endif
+        else if (argv[i] == "-struct")
+          {
+            retval.append (argv[i]);
+          }
+        else if (argv[i][0] == '-' && argv[i] != "-")
+          {
+            error ("save: Unrecognized option '%s'", argv[i].c_str ());
+          }
+        else
+          retval.append (argv[i]);
+      }
 
-  octave_scalar_map retstruct;
+    if (do_double)
+      {
+        if (fmt.type () == MAT_ASCII)
+          fmt.set_option (MAT_ASCII_LONG);
+        else
+          warning (R"(save: "-double" option only has an effect with "-ascii")");
+      }
 
-  std::ostringstream output_buf;
-  std::list<std::string> symbol_names;
+    if (do_tabs)
+      {
+        if (fmt.type () == MAT_ASCII)
+          fmt.set_option (MAT_ASCII_TABS);
+        else
+          warning (R"(save: "-tabs" option only has an effect with "-ascii")");
+      }
 
-  octave_idx_type count = 0;
+    return retval;
+  }
 
-  for (;;)
-    {
-      bool global = false;
-      octave_value tc;
+  string_vector
+  load_save_system::parse_save_options (const std::string& arg,
+                                        load_save_format& fmt,
+                                        bool& append, bool& save_as_floats,
+                                        bool& use_zlib)
+  {
+    std::istringstream is (arg);
+    std::string str;
+    string_vector argv;
+
+    while (! is.eof ())
+      {
+        is >> str;
+        argv.append (str);
+      }
+
+    return parse_save_options (argv, fmt, append, save_as_floats, use_zlib);
+  }
+
+  void load_save_system::save_vars (const string_vector& argv, int argv_idx,
+                                    int argc, std::ostream& os,
+                                    const load_save_format& fmt,
+                                    bool save_as_floats,
+                                    bool write_header_info)
+  {
+    if (write_header_info)
+      write_header (os, fmt);
 
-      std::string name;
-      std::string doc;
+    if (argv_idx == argc)
+      {
+        save_vars (os, "*", fmt, save_as_floats);
+      }
+    else if (argv[argv_idx] == "-struct")
+      {
+        if (++argv_idx >= argc)
+          error ("save: missing struct name");
+
+        std::string struct_name = argv[argv_idx];
+
+        symbol_scope scope = m_interpreter.get_current_scope ();
+
+        octave_value struct_var;
+
+        if (scope)
+          {
+            if (! scope.is_variable (struct_name))
+              error ("save: no such variable: '%s'", struct_name.c_str ());
+
+            struct_var = scope.varval (struct_name);
+          }
+
+        if (! struct_var.isstruct () || struct_var.numel () != 1)
+          error ("save: '%s' is not a scalar structure", struct_name.c_str ());
+
+        octave_scalar_map struct_var_map = struct_var.scalar_map_value ();
+
+        ++argv_idx;
 
-      switch (format.type)
-        {
-        case LS_TEXT:
-          name = read_text_data (stream, orig_fname, global, tc, count);
-          break;
+        if (argv_idx < argc)
+          {
+            for (int i = argv_idx; i < argc; i++)
+              {
+                if (! save_fields (os, struct_var_map, argv[i], fmt,
+                                   save_as_floats))
+                  {
+                    warning ("save: no such field '%s.%s'",
+                             struct_name.c_str (), argv[i].c_str ());
+                  }
+              }
+          }
+        else
+          save_fields (os, struct_var_map, "*", fmt, save_as_floats);
+      }
+    else
+      {
+        for (int i = argv_idx; i < argc; i++)
+          {
+            if (argv[i] == "")
+              continue;  // Skip empty vars for Matlab compatibility
+            if (! save_vars (os, argv[i], fmt, save_as_floats))
+              warning ("save: no such variable '%s'", argv[i].c_str ());
+          }
+      }
+  }
+
+  void load_save_system::dump_octave_core (void)
+  {
+    if (m_crash_dumps_octave_core)
+      {
+        // FIXME: should choose better filename?
 
-        case LS_BINARY:
-          name = read_binary_data (stream, swap, flt_fmt, orig_fname,
-                                   global, tc, doc);
-          break;
+        const char *fname = m_octave_core_file_name.c_str ();
+
+        message (nullptr, "attempting to save variables to '%s'...", fname);
+
+        load_save_format fmt (BINARY);
+
+        bool save_as_floats = false;
+
+        bool append = false;
+
+        bool use_zlib = false;
+
+        load_save_system::parse_save_options (m_octave_core_file_options,
+                                              fmt, append, save_as_floats,
+                                              use_zlib);
 
-        case LS_MAT_ASCII:
-          name = read_mat_ascii_data (stream, orig_fname, tc);
-          break;
+        std::ios::openmode mode = std::ios::out;
+
+        // Matlab v7 files are always compressed
+        if (fmt.type () == MAT7_BINARY)
+          use_zlib = false;
 
-        case LS_MAT_BINARY:
-          name = read_mat_binary_data (stream, orig_fname, tc);
-          break;
+        if (fmt.type () == BINARY
+#if defined (HAVE_HDF5)
+            || fmt.type () == HDF5
+#endif
+            || fmt.type () == MAT_BINARY
+            || fmt.type () == MAT5_BINARY
+            || fmt.type () == MAT7_BINARY)
+          mode |= std::ios::binary;
+
+        mode |= append ? std::ios::ate : std::ios::trunc;
 
 #if defined (HAVE_HDF5)
-        case LS_HDF5:
-          name = read_hdf5_data (stream, orig_fname, global, tc, doc,
-                                 argv, argv_idx, argc);
-          break;
+        if (fmt.type () == HDF5)
+          {
+            hdf5_ofstream file (fname, mode);
+
+            if (file.file_id >= 0)
+              {
+                dump_octave_core (file, fname, fmt, save_as_floats);
+
+                file.close ();
+              }
+            else
+              warning ("dump_octave_core: unable to open '%s' for writing...",
+                       fname);
+          }
+        else
 #endif
+          // don't insert any commands here!  The open brace below must
+          // go with the else above!
+          {
+#if defined (HAVE_ZLIB)
+            if (use_zlib)
+              {
+                gzofstream file (fname, mode);
+
+                if (file)
+                  {
+                    dump_octave_core (file, fname, fmt, save_as_floats);
+
+                    file.close ();
+                  }
+                else
+                  warning ("dump_octave_core: unable to open '%s' for writing...",
+                           fname);
+              }
+            else
+#endif
+              {
+                std::ofstream file (fname, mode);
+
+                if (file)
+                  {
+                    dump_octave_core (file, fname, fmt, save_as_floats);
 
-        case LS_MAT5_BINARY:
-        case LS_MAT7_BINARY:
-          name = read_mat5_binary_element (stream, orig_fname, swap,
-                                           global, tc);
-          break;
+                    file.close ();
+                  }
+                else
+                  warning ("dump_octave_core: unable to open '%s' for writing...",
+                           fname);
+              }
+          }
+      }
+  }
+
+  void load_save_system::write_header (std::ostream& os,
+                                       const load_save_format& fmt)
+  {
+    switch (fmt.type ())
+      {
+      case BINARY:
+        {
+          os << (mach_info::words_big_endian ()
+                 ? "Octave-1-B" : "Octave-1-L");
+
+          mach_info::float_format flt_fmt =
+            mach_info::native_float_format ();
+
+          char tmp = static_cast<char> (float_format_to_mopt_digit (flt_fmt));
+
+          os.write (&tmp, 1);
+        }
+        break;
 
-        default:
-          err_unrecognized_data_fmt ("load");
-          break;
+      case MAT5_BINARY:
+      case MAT7_BINARY:
+        {
+          char const *versionmagic;
+          char headertext[128];
+          sys::gmtime now;
+
+          // ISO 8601 format date
+          const char *matlab_format = "MATLAB 5.0 MAT-file, written by Octave "
+            OCTAVE_VERSION ", %Y-%m-%d %T UTC";
+          std::string comment_string = now.strftime (matlab_format);
+
+          size_t len = std::min (comment_string.length (), static_cast<size_t> (124));
+          memset (headertext, ' ', 124);
+          memcpy (headertext, comment_string.data (), len);
+
+          // The first pair of bytes give the version of the MAT file
+          // format.  The second pair of bytes form a magic number which
+          // signals a MAT file.  MAT file data are always written in
+          // native byte order.  The order of the bytes in the second
+          // pair indicates whether the file was written by a big- or
+          // little-endian machine.  However, the version number is
+          // written in the *opposite* byte order from everything else!
+          if (mach_info::words_big_endian ())
+            versionmagic = "\x01\x00\x4d\x49"; // this machine is big endian
+          else
+            versionmagic = "\x00\x01\x49\x4d"; // this machine is little endian
+
+          memcpy (headertext+124, versionmagic, 4);
+          os.write (headertext, 128);
         }
 
-      if (stream.eof () || name.empty ())
         break;
-      else
-        {
-          if (! tc.is_defined ())
-            error ("load: unable to load variable '%s'", name.c_str ());
 
-          if (format == LS_MAT_ASCII && argv_idx < argc)
-            warning ("load: loaded ASCII file '%s' -- ignoring extra args",
-                     orig_fname.c_str ());
+#if defined (HAVE_HDF5)
+      case HDF5:
+#endif
+      case TEXT:
+        {
+          sys::localtime now;
 
-          if (format == LS_MAT_ASCII
-              || argv_idx == argc
-              || matches_patterns (argv, argv_idx, argc, name))
+          std::string comment_string = now.strftime (m_save_header_format_string);
+
+          if (! comment_string.empty ())
             {
-              count++;
-              if (list_only)
+#if defined (HAVE_HDF5)
+              if (fmt.type () == HDF5)
                 {
-                  if (verbose)
-                    {
-                      if (count == 1)
-                        output_buf
-                          << "type               rows   cols   name\n"
-                          << "====               ====   ====   ====\n";
-
-                      output_buf
-                        << std::setiosflags (std::ios::left)
-                        << std::setw (16) << tc.type_name ().c_str ()
-                        << std::setiosflags (std::ios::right)
-                        << std::setw (7) << tc.rows ()
-                        << std::setw (7) << tc.columns ()
-                        << "   " << name << "\n";
-                    }
-                  else
-                    symbol_names.push_back (name);
+                  hdf5_ofstream& hs = dynamic_cast<hdf5_ofstream&> (os);
+                  H5Gset_comment (hs.file_id, "/", comment_string.c_str ());
                 }
               else
+#endif
+                os << comment_string << "\n";
+            }
+        }
+        break;
+
+      default:
+        break;
+      }
+  }
+
+  // Save variables with names matching PATTERN on stream OS in the
+  // format specified by FMT.
+
+  size_t load_save_system::save_vars (std::ostream& os,
+                                      const std::string& pattern,
+                                      const load_save_format& fmt,
+                                      bool save_as_floats)
+  {
+    symbol_scope scope
+      = m_interpreter.require_current_scope ("load_save_system::save_vars");
+
+    symbol_record::context_id context = scope.current_context ();
+
+    std::list<symbol_record> vars = scope.glob (pattern);
+
+    size_t saved = 0;
+
+    for (const auto& var : vars)
+      {
+        do_save (os, var, context, fmt, save_as_floats);
+
+        saved++;
+      }
+
+    return saved;
+  }
+
+  void load_save_system::do_save (std::ostream& os, const octave_value& tc,
+                                  const std::string& name,
+                                  const std::string& help,
+                                  bool global, const load_save_format& fmt,
+                                  bool save_as_floats)
+  {
+    switch (fmt.type ())
+      {
+      case TEXT:
+        save_text_data (os, tc, name, global, 0);
+        break;
+
+      case BINARY:
+        save_binary_data (os, tc, name, help, global, save_as_floats);
+        break;
+
+      case MAT_ASCII:
+        if (! save_mat_ascii_data (os, tc,
+                                   fmt.options () & MAT_ASCII_LONG ? 16 : 8,
+                                   fmt.options () & MAT_ASCII_TABS))
+          warning ("save: unable to save %s in ASCII format", name.c_str ());
+        break;
+
+      case MAT_BINARY:
+        save_mat_binary_data (os, tc, name);
+        break;
+
+#if defined (HAVE_HDF5)
+      case HDF5:
+        save_hdf5_data (os, tc, name, help, global, save_as_floats);
+        break;
+#endif
+
+      case MAT5_BINARY:
+        save_mat5_binary_element (os, tc, name, global, false, save_as_floats);
+        break;
+
+      case MAT7_BINARY:
+        save_mat5_binary_element (os, tc, name, global, true, save_as_floats);
+        break;
+
+      default:
+        err_unrecognized_data_fmt ("save");
+        break;
+      }
+  }
+
+  // Save the info from SR on stream OS in the format specified by FMT.
+
+  void load_save_system::do_save (std::ostream& os,
+                                  const symbol_record& sr,
+                                  symbol_record::context_id context,
+                                  const load_save_format& fmt,
+                                  bool save_as_floats)
+  {
+    octave_value val = sr.varval (context);
+
+    if (val.is_defined ())
+      {
+        std::string name = sr.name ();
+        std::string help;
+        bool global = sr.is_global ();
+
+        do_save (os, val, name, help, global, fmt, save_as_floats);
+      }
+  }
+
+  // save fields of a scalar structure STR matching PATTERN on stream OS
+  // in the format specified by FMT.
+
+  size_t load_save_system::save_fields (std::ostream& os,
+                                        const octave_scalar_map& m,
+                                        const std::string& pattern,
+                                        const load_save_format& fmt,
+                                        bool save_as_floats)
+  {
+    glob_match pat (pattern);
+
+    size_t saved = 0;
+
+    for (auto it = m.begin (); it != m.end (); it++)
+      {
+        std::string empty_str;
+
+        if (pat.match (m.key (it)))
+          {
+            do_save (os, m.contents (it), m.key (it), empty_str,
+                     0, fmt, save_as_floats);
+
+            saved++;
+          }
+      }
+
+    return saved;
+  }
+
+  void load_save_system::dump_octave_core (std::ostream& os,
+                                           const char *fname,
+                                           const load_save_format& fmt,
+                                           bool save_as_floats)
+  {
+    write_header (os, fmt);
+
+    symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+    symbol_scope top_scope = symtab.top_scope ();
+
+    symbol_record::context_id context = top_scope.current_context ();
+
+    std::list<symbol_record> vars = top_scope.all_variables ();
+
+    double save_mem_size = 0;
+
+    for (const auto& var : vars)
+      {
+        octave_value val = var.varval (context);
+
+        if (val.is_defined ())
+          {
+            std::string name = var.name ();
+            std::string help;
+            bool global = var.is_global ();
+
+            double val_size = val.byte_size () / 1024;
+
+            // FIXME: maybe we should try to throw out the largest first...
+
+            if (m_octave_core_file_limit < 0
+                || save_mem_size + val_size < m_octave_core_file_limit)
+              {
+                save_mem_size += val_size;
+
+                do_save (os, val, name, help, global, fmt, save_as_floats);
+              }
+          }
+      }
+
+    message (nullptr, "save to '%s' complete", fname);
+  }
+
+  // Install a variable with name NAME and the value VAL in the
+  // symbol table.  If GLOBAL is TRUE, make the variable global.
+
+  void load_save_system::install_loaded_variable (const std::string& name,
+                                                  const octave_value& val,
+                                                  bool global,
+                                                  const std::string& /*doc*/)
+  {
+    symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+    symbol_scope scope = symtab.require_current_scope ("load_save_system::install_loaded_variable");
+
+    if (global)
+      {
+        symbol_record sym = scope.find_symbol (name);
+
+        if (! sym.is_global ())
+          {
+            symbol_scope global_scope = symtab.global_scope ();
+            symbol_record global_sym = global_scope.find_symbol (name);
+
+            sym.bind_fwd_rep (global_scope.get_rep (), global_sym);
+          }
+      }
+
+    scope.assign (name, val);
+  }
+
+  std::string load_save_system::init_save_header_format (void)
+  {
+    return
+      (std::string ("# Created by Octave " OCTAVE_VERSION
+                   ", %a %b %d %H:%M:%S %Y %Z <")
+       + sys::env::get_user_name ()
+       + '@'
+       + sys::env::get_host_name ()
+       + '>');
+  }
+
+  load_save_format
+  load_save_system::get_file_format (std::istream& file,
+                                     const std::string& filename)
+  {
+    load_save_format retval = load_save_system::UNKNOWN;
+
+    mach_info::float_format flt_fmt
+      = mach_info::flt_fmt_unknown;
+
+    bool swap = false;
+
+    if (read_binary_file_header (file, swap, flt_fmt, true) == 0)
+      retval = BINARY;
+    else
+      {
+        file.clear ();
+        file.seekg (0, std::ios::beg);
+
+        int32_t mopt, nr, nc, imag, len;
+
+        int err = read_mat_file_header (file, swap, mopt, nr, nc, imag, len,
+                                        true);
+
+        if (! err)
+          retval = MAT_BINARY;
+        else
+          {
+            file.clear ();
+            file.seekg (0, std::ios::beg);
+
+            err = read_mat5_binary_file_header (file, swap, true, filename);
+
+            if (! err)
+              {
+                file.clear ();
+                file.seekg (0, std::ios::beg);
+                retval = MAT5_BINARY;
+              }
+            else
+              {
+                file.clear ();
+                file.seekg (0, std::ios::beg);
+
+                std::string name_val = extract_keyword (file, "name");
+                std::string type_val = extract_keyword (file, "type");
+
+                if (name_val.empty () != true && type_val.empty () != true)
+                  retval = TEXT;
+                else
+                  {
+                    file.clear ();
+                    file.seekg (0, std::ios::beg);
+
+                    // FIXME: looks_like_mat_ascii_file does not check
+                    // to see whether the file contains numbers.  It
+                    // just skips comments and checks for the same
+                    // number of words on each line.  We may need a
+                    // better check here.  The best way to do that might
+                    // be just to try to read the file and see if it
+                    // works.
+
+                    if (looks_like_mat_ascii_file (file, filename))
+                      retval = MAT_ASCII;
+                  }
+              }
+          }
+      }
+
+    return retval;
+  }
+
+  octave_value_list
+  load_save_system::load (const octave_value_list& args, int nargout)
+  {
+    octave_value_list retval;
+
+    int argc = args.length () + 1;
+
+    string_vector argv = args.make_argv ("load");
+
+    int i = 1;
+    std::string orig_fname = "";
+
+    // Function called with Matlab-style ["filename", options] syntax
+    if (argc > 1 && ! argv[1].empty () && argv[1].at (0) != '-')
+      {
+        orig_fname = argv[1];
+        i++;
+      }
+
+    // It isn't necessary to have the default load format stored in a
+    // user preference variable since we can determine the type of file
+    // as we are reading.
+
+    load_save_format format = UNKNOWN;
+
+    bool list_only = false;
+    bool verbose = false;
+
+    for (; i < argc; i++)
+      {
+        if (argv[i] == "-force" || argv[i] == "-f")
+          {
+            // Silently ignore this
+            // warning ("load: -force ignored");
+          }
+        else if (argv[i] == "-list" || argv[i] == "-l")
+          {
+            list_only = true;
+          }
+        else if (argv[i] == "-verbose" || argv[i] == "-v")
+          {
+            verbose = true;
+          }
+        else if (argv[i] == "-ascii" || argv[i] == "-a")
+          {
+            format = MAT_ASCII;
+          }
+        else if (argv[i] == "-binary" || argv[i] == "-b")
+          {
+            format = BINARY;
+          }
+        else if (argv[i] == "-mat-binary" || argv[i] == "-mat"
+                 || argv[i] == "-m" || argv[i] == "-6" || argv[i] == "-v6")
+          {
+            format = MAT5_BINARY;
+          }
+        else if (argv[i] == "-7" || argv[i] == "-v7")
+          {
+            format = MAT7_BINARY;
+          }
+        else if (argv[i] == "-mat4-binary" || argv[i] == "-V4"
+                 || argv[i] == "-v4" || argv[i] == "-4")
+          {
+            format = MAT_BINARY;
+          }
+        else if (argv[i] == "-hdf5" || argv[i] == "-h")
+          {
+#if defined (HAVE_HDF5)
+            format = HDF5;
+#else
+            err_disabled_feature ("load", "HDF5");
+#endif
+          }
+        else if (argv[i] == "-import" || argv[i] == "-i")
+          {
+            warning ("load: -import ignored");
+          }
+        else if (argv[i] == "-text" || argv[i] == "-t")
+          {
+            format = TEXT;
+          }
+        else
+          break;
+      }
+
+    if (orig_fname == "")
+      {
+        if (i == argc)
+          print_usage ();
+
+        orig_fname = argv[i];
+      }
+    else
+      i--;
+
+    mach_info::float_format flt_fmt = mach_info::flt_fmt_unknown;
+
+    bool swap = false;
+
+    if (orig_fname == "-")
+      {
+        i++;
+
+#if defined (HAVE_HDF5)
+        if (format.type () == HDF5)
+          error ("load: cannot read HDF5 format from stdin");
+        else
+#endif
+          if (format.type () != UNKNOWN)
+            {
+              // FIXME: if we have already seen EOF on a previous call,
+              // how do we fix up the state of std::cin so that we can get
+              // additional input?  I'm afraid that we can't fix this
+              // using std::cin only.
+
+              retval = load_vars (std::cin, orig_fname, format, flt_fmt,
+                                  list_only, swap, verbose, argv, i,
+                                  argc, nargout);
+            }
+          else
+            error ("load: must specify file format if reading from stdin");
+      }
+    else
+      {
+        std::string fname = sys::file_ops::tilde_expand (orig_fname);
+
+        fname = find_file_to_load (fname, orig_fname);
+
+        bool use_zlib = false;
+
+        if (format.type () == UNKNOWN)
+          format = get_file_format (fname, orig_fname, use_zlib);
+
+#if defined (HAVE_HDF5)
+        if (format.type () == HDF5)
+          {
+            i++;
+
+            hdf5_ifstream hdf5_file (fname.c_str ());
+
+            if (hdf5_file.file_id < 0)
+              err_file_open ("load", orig_fname);
+
+            retval = load_vars (hdf5_file, orig_fname, format, flt_fmt,
+                                list_only, swap, verbose, argv, i,
+                                argc, nargout);
+
+            hdf5_file.close ();
+          }
+        else
+#endif
+          // don't insert any statements here; the "else" above has to
+          // go with the "if" below!!!!!
+          if (format.type () != UNKNOWN)
+            {
+              i++;
+
+              // Always open in binary mode and handle various
+              // line-endings explicitly.
+              std::ios::openmode mode = std::ios::in | std::ios::binary;
+
+#if defined (HAVE_ZLIB)
+              if (use_zlib)
                 {
-                  if (nargout == 1)
+                  gzifstream file (fname.c_str (), mode);
+
+                  if (! file)
+                    err_file_open ("load", orig_fname);
+
+                  if (format.type () == BINARY)
                     {
-                      if (format == LS_MAT_ASCII)
-                        retval = tc;
-                      else
-                        retstruct.assign (name, tc);
+                      if (read_binary_file_header (file, swap, flt_fmt) < 0)
+                        {
+                          if (file) file.close ();
+                          return retval;
+                        }
+                    }
+                  else if (format.type () == MAT5_BINARY
+                           || format.type () == MAT7_BINARY)
+                    {
+                      if (read_mat5_binary_file_header (file, swap, false,
+                                                        orig_fname) < 0)
+                        {
+                          if (file) file.close ();
+                          return retval;
+                        }
                     }
-                  else
-                    install_loaded_variable (name, tc, global, doc);
+
+                  retval = load_vars (file, orig_fname, format, flt_fmt,
+                                      list_only, swap, verbose, argv, i,
+                                      argc, nargout);
+
+                  file.close ();
+                }
+              else
+#endif
+                {
+                  std::string ascii_fname = sys::get_ASCII_filename (fname);
+
+                  std::ifstream file (ascii_fname.c_str (), mode);
+
+                  if (! file)
+                    error ("load: unable to open input file '%s'",
+                           orig_fname.c_str ());
+
+                  if (format.type () == BINARY)
+                    {
+                      if (read_binary_file_header (file, swap, flt_fmt) < 0)
+                        {
+                          if (file) file.close ();
+                          return retval;
+                        }
+                    }
+                  else if (format.type () == MAT5_BINARY
+                           || format.type () == MAT7_BINARY)
+                    {
+                      if (read_mat5_binary_file_header (file, swap, false,
+                                                        orig_fname) < 0)
+                        {
+                          if (file) file.close ();
+                          return retval;
+                        }
+                    }
+
+                  retval = load_vars (file, orig_fname, format, flt_fmt,
+                                      list_only, swap, verbose, argv, i,
+                                      argc, nargout);
+
+                  file.close ();
                 }
             }
+          else
+            error ("load: unable to determine file format of '%s'",
+                   orig_fname.c_str ());
 
-          // Only attempt to read one item from a headless text file.
+      }
+
+    return retval;
+  }
+
+  octave_value_list
+  load_save_system::save (const octave_value_list& args, int nargout)
+  {
+    // Here is where we would get the default save format if it were
+    // stored in a user preference variable.
+    load_save_format format = TEXT;
+    bool save_as_floats = false;
+    bool append = false;
+    bool use_zlib = false;
+
+
+    // get default options
+    parse_save_options (save_default_options (), format, append,
+                        save_as_floats, use_zlib);
+
+    // override from command line
+    string_vector argv = args.make_argv ();
+
+    argv = parse_save_options (argv, format, append, save_as_floats, use_zlib);
+
+    int argc = argv.numel ();
+    int i = 0;
+
+    if (i == argc)
+      print_usage ();
+
+    if (save_as_floats && format.type () == TEXT)
+      error ("save: cannot specify both -text and -float-binary");
 
-          if (format == LS_MAT_ASCII)
-            break;
-        }
-    }
+    octave_value_list retval;
+
+    if (argv[i] == "-")
+      {
+        i++;
+
+#if defined (HAVE_HDF5)
+        if (format.type () == HDF5)
+          error ("save: cannot write HDF5 format to stdout");
+        else
+#endif
+          // don't insert any commands here!  the brace below must go
+          // with the "else" above!
+          {
+            if (append)
+              warning ("save: ignoring -append option for output to stdout");
 
-  if (list_only && count)
-    {
-      if (verbose)
-        {
-          std::string msg = output_buf.str ();
+            if (nargout == 0)
+              save_vars (argv, i, argc, std::cout, format,
+                         save_as_floats, true);
+            else
+              {
+                std::ostringstream output_buf;
+                save_vars (argv, i, argc, output_buf, format,
+                           save_as_floats, true);
+                retval = octave_value (output_buf.str());
+              }
+          }
+      }
+
+    // Guard against things like 'save a*', which are probably mistakes...
+
+    else if (i == argc - 1 && glob_pattern_p (argv[i]))
+      print_usage ();
+    else
+      {
+        std::string fname = sys::file_ops::tilde_expand (argv[i]);
+
+        i++;
 
-          if (nargout > 0)
-            retval = msg;
-          else
-            octave_stdout << msg;
-        }
-      else
-        {
-          if (nargout  > 0)
-            retval = Cell (string_vector (symbol_names));
-          else
-            {
-              string_vector names (symbol_names);
+        // Matlab v7 files are always compressed
+        if (format.type () == MAT7_BINARY)
+          use_zlib = false;
+
+        std::ios::openmode mode
+          = (append ? (std::ios::app | std::ios::ate) : std::ios::out);
+
+        if (format.type () == BINARY
+#if defined (HAVE_HDF5)
+            || format.type () == HDF5
+#endif
+            || format.type () == MAT_BINARY
+            || format.type () == MAT5_BINARY
+            || format.type () == MAT7_BINARY)
+          mode |= std::ios::binary;
+
+#if defined (HAVE_HDF5)
+        if (format.type () == HDF5)
+          {
+            // FIXME: It should be possible to append to HDF5 files.
+            if (append)
+              error ("save: appending to HDF5 files is not implemented");
+
+            std::string ascii_fname = sys::get_ASCII_filename (fname);
+
+            bool write_header_info
+              = ! (append && H5Fis_hdf5 (ascii_fname.c_str ()) > 0);
+
+            hdf5_ofstream hdf5_file (fname.c_str (), mode);
+
+            if (hdf5_file.file_id == -1)
+              err_file_open ("save", fname);
+
+            save_vars (argv, i, argc, hdf5_file, format, save_as_floats,
+                       write_header_info);
 
-              names.list_in_columns (octave_stdout);
+            hdf5_file.close ();
+          }
+        else
+#endif
+          // don't insert any statements here!  The brace below must go
+          // with the "else" above!
+          {
+#if defined (HAVE_ZLIB)
+            if (use_zlib)
+              {
+                gzofstream file (fname.c_str (), mode);
+
+                if (! file)
+                  err_file_open ("save", fname);
+
+                bool write_header_info = ! file.tellp ();
+
+                save_vars (argv, i, argc, file, format, save_as_floats,
+                           write_header_info);
 
-              octave_stdout << "\n";
-            }
-        }
-    }
-  else if (retstruct.nfields () != 0)
-    retval = retstruct;
+                file.close ();
+              }
+            else
+#endif
+              {
+                std::ofstream file (fname.c_str (), mode);
+
+                if (! file)
+                  err_file_open ("save", fname);
 
-  return retval;
+                bool write_header_info = ! file.tellp ();
+
+                save_vars (argv, i, argc, file, format, save_as_floats,
+                           write_header_info);
+
+                file.close ();
+              }
+          }
+      }
+
+    return retval;
+  }
 }
 
-static std::string
-find_file_to_load (const std::string& name, const std::string& orig_name)
+void
+dump_octave_core (void)
 {
-  std::string fname = octave::find_data_file_in_load_path ("load", name, true);
-
-  size_t dot_pos = fname.rfind ('.');
-  size_t sep_pos = fname.find_last_of (octave::sys::file_ops::dir_sep_chars ());
-
-  if (dot_pos == std::string::npos
-      || (sep_pos != std::string::npos && dot_pos < sep_pos))
-    {
-      // Either no '.' in name or no '.' appears after last directory
-      // separator.
-
-      octave::sys::file_stat fs (fname);
+  octave::load_save_system& load_save_sys
+    = octave::__get_load_save_system__ ("dump_octave_core");
 
-      if (! (fs.exists () && fs.is_reg ()))
-        fname = find_file_to_load (fname + ".mat", orig_name);
-    }
-  else
-    {
-      octave::sys::file_stat fs (fname);
-
-      if (! (fs.exists () && fs.is_reg ()))
-        {
-          fname = "";
-
-          error ("load: unable to find file %s", orig_name.c_str ());
-        }
-    }
-
-  return fname;
+  load_save_sys.dump_octave_core ();
 }
 
-bool
-is_octave_data_file (const std::string& fname)
-{
-  bool use_zlib = false;
-  return get_file_format (fname, fname, use_zlib, true) != LS_UNKNOWN;
-}
-
-DEFUN (load, args, nargout,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (load, interp, args, nargout,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {} load file
 @deftypefnx {} {} load options file
 @deftypefnx {} {} load options file v1 v2 @dots{}
@@ -646,838 +1679,13 @@
 @seealso{save, dlmwrite, csvwrite, fwrite}
 @end deftypefn */)
 {
-  octave_value_list retval;
-
-  int argc = args.length () + 1;
-
-  string_vector argv = args.make_argv ("load");
-
-  int i = 1;
-  std::string orig_fname = "";
-
-  // Function called with Matlab-style ["filename", options] syntax
-  if (argc > 1 && ! argv[1].empty () && argv[1].at (0) != '-')
-    {
-      orig_fname = argv[1];
-      i++;
-    }
-
-  // It isn't necessary to have the default load format stored in a
-  // user preference variable since we can determine the type of file
-  // as we are reading.
-
-  load_save_format format = LS_UNKNOWN;
-
-  bool list_only = false;
-  bool verbose = false;
-
-  for (; i < argc; i++)
-    {
-      if (argv[i] == "-force" || argv[i] == "-f")
-        {
-          // Silently ignore this
-          // warning ("load: -force ignored");
-        }
-      else if (argv[i] == "-list" || argv[i] == "-l")
-        {
-          list_only = true;
-        }
-      else if (argv[i] == "-verbose" || argv[i] == "-v")
-        {
-          verbose = true;
-        }
-      else if (argv[i] == "-ascii" || argv[i] == "-a")
-        {
-          format = LS_MAT_ASCII;
-        }
-      else if (argv[i] == "-binary" || argv[i] == "-b")
-        {
-          format = LS_BINARY;
-        }
-      else if (argv[i] == "-mat-binary" || argv[i] == "-mat" || argv[i] == "-m"
-               || argv[i] == "-6" || argv[i] == "-v6")
-        {
-          format = LS_MAT5_BINARY;
-        }
-      else if (argv[i] == "-7" || argv[i] == "-v7")
-        {
-          format = LS_MAT7_BINARY;
-        }
-      else if (argv[i] == "-mat4-binary" || argv[i] == "-V4"
-               || argv[i] == "-v4" || argv[i] == "-4")
-        {
-          format = LS_MAT_BINARY;
-        }
-      else if (argv[i] == "-hdf5" || argv[i] == "-h")
-        {
-#if defined (HAVE_HDF5)
-          format = LS_HDF5;
-#else
-          err_disabled_feature ("load", "HDF5");
-#endif
-        }
-      else if (argv[i] == "-import" || argv[i] == "-i")
-        {
-          warning ("load: -import ignored");
-        }
-      else if (argv[i] == "-text" || argv[i] == "-t")
-        {
-          format = LS_TEXT;
-        }
-      else
-        break;
-    }
-
-  if (orig_fname == "")
-    {
-      if (i == argc)
-        print_usage ();
-
-      orig_fname = argv[i];
-    }
-  else
-    i--;
-
-  octave::mach_info::float_format flt_fmt = octave::mach_info::flt_fmt_unknown;
-
-  bool swap = false;
-
-  if (orig_fname == "-")
-    {
-      i++;
-
-#if defined (HAVE_HDF5)
-      if (format == LS_HDF5)
-        error ("load: cannot read HDF5 format from stdin");
-      else
-#endif
-      if (format != LS_UNKNOWN)
-        {
-          // FIXME: if we have already seen EOF on a previous call,
-          // how do we fix up the state of std::cin so that we can get
-          // additional input?  I'm afraid that we can't fix this
-          // using std::cin only.
-
-          retval = do_load (std::cin, orig_fname, format, flt_fmt,
-                            list_only, swap, verbose, argv, i, argc,
-                            nargout);
-        }
-      else
-        error ("load: must specify file format if reading from stdin");
-    }
-  else
-    {
-      std::string fname = octave::sys::file_ops::tilde_expand (orig_fname);
-
-      fname = find_file_to_load (fname, orig_fname);
-
-      bool use_zlib = false;
-
-      if (format == LS_UNKNOWN)
-        format = get_file_format (fname, orig_fname, use_zlib);
-
-#if defined (HAVE_HDF5)
-      if (format == LS_HDF5)
-        {
-          i++;
-
-          hdf5_ifstream hdf5_file (fname.c_str ());
-
-          if (hdf5_file.file_id < 0)
-            err_file_open ("load", orig_fname);
-
-          retval = do_load (hdf5_file, orig_fname, format,
-                            flt_fmt, list_only, swap, verbose,
-                            argv, i, argc, nargout);
-
-          hdf5_file.close ();
-        }
-      else
-#endif
-        // don't insert any statements here; the "else" above has to
-        // go with the "if" below!!!!!
-      if (format != LS_UNKNOWN)
-        {
-          i++;
-
-          // Always open in binary mode and handle various
-          // line-endings explicitly.
-          std::ios::openmode mode = std::ios::in | std::ios::binary;
-
-#if defined (HAVE_ZLIB)
-          if (use_zlib)
-            {
-              gzifstream file (fname.c_str (), mode);
-
-              if (! file)
-                err_file_open ("load", orig_fname);
-
-              if (format == LS_BINARY)
-                {
-                  if (read_binary_file_header (file, swap, flt_fmt) < 0)
-                    {
-                      if (file) file.close ();
-                      return retval;
-                    }
-                }
-              else if (format == LS_MAT5_BINARY
-                       || format == LS_MAT7_BINARY)
-                {
-                  if (read_mat5_binary_file_header (file, swap, false,
-                                                    orig_fname) < 0)
-                    {
-                      if (file) file.close ();
-                      return retval;
-                    }
-                }
-
-              retval = do_load (file, orig_fname, format,
-                                flt_fmt, list_only, swap, verbose,
-                                argv, i, argc, nargout);
-
-              file.close ();
-            }
-          else
-#endif
-            {
-              std::string ascii_fname = octave::sys::get_ASCII_filename (fname);
+  octave::load_save_system& load_save_sys = interp.get_load_save_system ();
 
-              std::ifstream file (ascii_fname.c_str (), mode);
-
-              if (! file)
-                error ("load: unable to open input file '%s'",
-                       orig_fname.c_str ());
-
-              if (format == LS_BINARY)
-                {
-                  if (read_binary_file_header (file, swap, flt_fmt) < 0)
-                    {
-                      if (file) file.close ();
-                      return retval;
-                    }
-                }
-              else if (format == LS_MAT5_BINARY
-                       || format == LS_MAT7_BINARY)
-                {
-                  if (read_mat5_binary_file_header (file, swap, false,
-                                                    orig_fname) < 0)
-                    {
-                      if (file) file.close ();
-                      return retval;
-                    }
-                }
-
-              retval = do_load (file, orig_fname, format,
-                                flt_fmt, list_only, swap, verbose,
-                                argv, i, argc, nargout);
-
-              file.close ();
-            }
-        }
-      else
-        error ("load: unable to determine file format of '%s'",
-               orig_fname.c_str ());
-
-    }
-
-  return retval;
-}
-
-// Return TRUE if PATTERN has any special globbing chars in it.
-
-static bool
-glob_pattern_p (const std::string& pattern)
-{
-  int open = 0;
-
-  int len = pattern.length ();
-
-  for (int i = 0; i < len; i++)
-    {
-      char c = pattern[i];
-
-      switch (c)
-        {
-        case '?':
-        case '*':
-          return true;
-
-        case '[':       // Only accept an open brace if there is a close
-          open++;       // brace to match it.  Bracket expressions must be
-          continue;     // complete, according to Posix.2
-
-        case ']':
-          if (open)
-            return true;
-          continue;
-
-        case '\\':
-          if (i == len - 1)
-            return false;
-          continue;
-
-        default:
-          continue;
-        }
-    }
-
-  return false;
-}
-
-static void
-do_save (std::ostream& os, const octave_value& tc,
-         const std::string& name, const std::string& help,
-         bool global, load_save_format fmt, bool save_as_floats)
-{
-  switch (fmt.type)
-    {
-    case LS_TEXT:
-      save_text_data (os, tc, name, global, 0);
-      break;
-
-    case LS_BINARY:
-      save_binary_data (os, tc, name, help, global, save_as_floats);
-      break;
-
-    case LS_MAT_ASCII:
-      if (! save_mat_ascii_data (os, tc, fmt.opts & LS_MAT_ASCII_LONG ? 16 : 8,
-                                 fmt.opts & LS_MAT_ASCII_TABS))
-        warning ("save: unable to save %s in ASCII format", name.c_str ());
-      break;
-
-    case LS_MAT_BINARY:
-      save_mat_binary_data (os, tc, name);
-      break;
-
-#if defined (HAVE_HDF5)
-    case LS_HDF5:
-      save_hdf5_data (os, tc, name, help, global, save_as_floats);
-      break;
-#endif
-
-    case LS_MAT5_BINARY:
-      save_mat5_binary_element (os, tc, name, global, false, save_as_floats);
-      break;
-
-    case LS_MAT7_BINARY:
-      save_mat5_binary_element (os, tc, name, global, true, save_as_floats);
-      break;
-
-    default:
-      err_unrecognized_data_fmt ("save");
-      break;
-    }
-}
-
-// Save the info from SR on stream OS in the format specified by FMT.
-
-void
-do_save (std::ostream& os, const octave::symbol_record& sr,
-         octave::symbol_record::context_id context,
-         load_save_format fmt, bool save_as_floats)
-{
-  octave_value val = sr.varval (context);
-
-  if (val.is_defined ())
-    {
-      std::string name = sr.name ();
-      std::string help;
-      bool global = sr.is_global ();
-
-      do_save (os, val, name, help, global, fmt, save_as_floats);
-    }
-}
-
-// save fields of a scalar structure STR matching PATTERN on stream OS
-// in the format specified by FMT.
-
-static size_t
-save_fields (std::ostream& os, const octave_scalar_map& m,
-             const std::string& pattern,
-             load_save_format fmt, bool save_as_floats)
-{
-  glob_match pat (pattern);
-
-  size_t saved = 0;
-
-  for (auto it = m.begin (); it != m.end (); it++)
-    {
-      std::string empty_str;
-
-      if (pat.match (m.key (it)))
-        {
-          do_save (os, m.contents (it), m.key (it), empty_str,
-                   0, fmt, save_as_floats);
-
-          saved++;
-        }
-    }
-
-  return saved;
-}
-
-// Save variables with names matching PATTERN on stream OS in the
-// format specified by FMT.
-
-static size_t
-save_vars (std::ostream& os, const std::string& pattern,
-           load_save_format fmt, bool save_as_floats)
-{
-  octave::symbol_scope scope = octave::__require_current_scope__ ("save_vars");
-
-  octave::symbol_record::context_id context = scope.current_context ();
-
-  std::list<octave::symbol_record> vars = scope.glob (pattern);
-
-  size_t saved = 0;
-
-  for (const auto& var : vars)
-    {
-      do_save (os, var, context, fmt, save_as_floats);
-
-      saved++;
-    }
-
-  return saved;
+  return load_save_sys.load (args, nargout);
 }
 
-static string_vector
-parse_save_options (const string_vector& argv,
-                    load_save_format& format, bool& append,
-                    bool& save_as_floats, bool& use_zlib)
-{
-#if ! defined (HAVE_ZLIB)
-  octave_unused_parameter (use_zlib);
-#endif
-
-  string_vector retval;
-  int argc = argv.numel ();
-
-  bool do_double = false;
-  bool do_tabs = false;
-
-  for (int i = 0; i < argc; i++)
-    {
-      if (argv[i] == "-append")
-        {
-          append = true;
-        }
-      else if (argv[i] == "-ascii" || argv[i] == "-a")
-        {
-          format = LS_MAT_ASCII;
-        }
-      else if (argv[i] == "-double")
-        {
-          do_double = true;
-        }
-      else if (argv[i] == "-tabs")
-        {
-          do_tabs = true;
-        }
-      else if (argv[i] == "-text" || argv[i] == "-t")
-        {
-          format = LS_TEXT;
-        }
-      else if (argv[i] == "-binary" || argv[i] == "-b")
-        {
-          format = LS_BINARY;
-        }
-      else if (argv[i] == "-hdf5" || argv[i] == "-h")
-        {
-#if defined (HAVE_HDF5)
-          format = LS_HDF5;
-#else
-          err_disabled_feature ("save", "HDF5");
-#endif
-        }
-      else if (argv[i] == "-mat-binary" || argv[i] == "-mat"
-               || argv[i] == "-m" || argv[i] == "-6" || argv[i] == "-v6"
-               || argv[i] == "-V6")
-        {
-          format = LS_MAT5_BINARY;
-        }
-#if defined (HAVE_ZLIB)
-      else if (argv[i] == "-mat7-binary" || argv[i] == "-7"
-               || argv[i] == "-v7" || argv[i] == "-V7")
-        {
-          format = LS_MAT7_BINARY;
-        }
-#endif
-      else if (argv[i] == "-mat4-binary" || argv[i] == "-V4"
-               || argv[i] == "-v4" || argv[i] == "-4")
-        {
-          format = LS_MAT_BINARY;
-        }
-      else if (argv[i] == "-float-binary" || argv[i] == "-f")
-        {
-          format = LS_BINARY;
-          save_as_floats = true;
-        }
-      else if (argv[i] == "-float-hdf5")
-        {
-#if defined (HAVE_HDF5)
-          format = LS_HDF5;
-          save_as_floats = true;
-#else
-          err_disabled_feature ("save", "HDF5");
-#endif
-        }
-#if defined (HAVE_ZLIB)
-      else if (argv[i] == "-zip" || argv[i] == "-z")
-        {
-          use_zlib = true;
-        }
-#endif
-      else if (argv[i] == "-struct")
-        {
-          retval.append (argv[i]);
-        }
-      else if (argv[i][0] == '-' && argv[i] != "-")
-        {
-          error ("save: Unrecognized option '%s'", argv[i].c_str ());
-        }
-      else
-        retval.append (argv[i]);
-    }
-
-  if (do_double)
-    {
-      if (format == LS_MAT_ASCII)
-        format.opts |= LS_MAT_ASCII_LONG;
-      else
-        warning (R"(save: "-double" option only has an effect with "-ascii")");
-    }
-
-  if (do_tabs)
-    {
-      if (format == LS_MAT_ASCII)
-        format.opts |= LS_MAT_ASCII_TABS;
-      else
-        warning (R"(save: "-tabs" option only has an effect with "-ascii")");
-    }
-
-  return retval;
-}
-
-static string_vector
-parse_save_options (const std::string& arg, load_save_format& format,
-                    bool& append, bool& save_as_floats, bool& use_zlib)
-{
-  std::istringstream is (arg);
-  std::string str;
-  string_vector argv;
-
-  while (! is.eof ())
-    {
-      is >> str;
-      argv.append (str);
-    }
-
-  return parse_save_options (argv, format, append, save_as_floats, use_zlib);
-}
-
-void
-write_header (std::ostream& os, load_save_format format)
-{
-  switch (format.type)
-    {
-    case LS_BINARY:
-      {
-        os << (octave::mach_info::words_big_endian ()
-               ? "Octave-1-B" : "Octave-1-L");
-
-        octave::mach_info::float_format flt_fmt =
-          octave::mach_info::native_float_format ();
-
-        char tmp = static_cast<char> (float_format_to_mopt_digit (flt_fmt));
-
-        os.write (&tmp, 1);
-      }
-      break;
-
-    case LS_MAT5_BINARY:
-    case LS_MAT7_BINARY:
-      {
-        char const *versionmagic;
-        char headertext[128];
-        octave::sys::gmtime now;
-
-        // ISO 8601 format date
-        const char *matlab_format = "MATLAB 5.0 MAT-file, written by Octave "
-            OCTAVE_VERSION ", %Y-%m-%d %T UTC";
-        std::string comment_string = now.strftime (matlab_format);
-
-        size_t len = std::min (comment_string.length (), static_cast<size_t> (124));
-        memset (headertext, ' ', 124);
-        memcpy (headertext, comment_string.data (), len);
-
-        // The first pair of bytes give the version of the MAT file
-        // format.  The second pair of bytes form a magic number which
-        // signals a MAT file.  MAT file data are always written in
-        // native byte order.  The order of the bytes in the second
-        // pair indicates whether the file was written by a big- or
-        // little-endian machine.  However, the version number is
-        // written in the *opposite* byte order from everything else!
-        if (octave::mach_info::words_big_endian ())
-          versionmagic = "\x01\x00\x4d\x49"; // this machine is big endian
-        else
-          versionmagic = "\x00\x01\x49\x4d"; // this machine is little endian
-
-        memcpy (headertext+124, versionmagic, 4);
-        os.write (headertext, 128);
-      }
-
-      break;
-
-#if defined (HAVE_HDF5)
-    case LS_HDF5:
-#endif
-    case LS_TEXT:
-      {
-        octave::sys::localtime now;
-
-        std::string comment_string = now.strftime (Vsave_header_format_string);
-
-        if (! comment_string.empty ())
-          {
-#if defined (HAVE_HDF5)
-            if (format == LS_HDF5)
-              {
-                hdf5_ofstream& hs = dynamic_cast<hdf5_ofstream&> (os);
-                H5Gset_comment (hs.file_id, "/", comment_string.c_str ());
-              }
-            else
-#endif
-              os << comment_string << "\n";
-          }
-      }
-      break;
-
-    default:
-      break;
-    }
-}
-
-void
-octave_prepare_hdf5 (void)
-{
-#if defined (HAVE_HDF5)
-  H5dont_atexit ();
-#endif
-}
-
-void
-octave_finalize_hdf5 (void)
-{
-#if defined (HAVE_HDF5)
-  H5close ();
-#endif
-}
-
-static void
-save_vars (const string_vector& argv, int argv_idx, int argc,
-           std::ostream& os, load_save_format fmt,
-           bool save_as_floats, bool write_header_info)
-{
-  if (write_header_info)
-    write_header (os, fmt);
-
-  if (argv_idx == argc)
-    {
-      save_vars (os, "*", fmt, save_as_floats);
-    }
-  else if (argv[argv_idx] == "-struct")
-    {
-      if (++argv_idx >= argc)
-        error ("save: missing struct name");
-
-      std::string struct_name = argv[argv_idx];
-
-      octave::symbol_scope scope = octave::__get_current_scope__ ("save_vars");
-
-      octave_value struct_var;
-
-      if (scope)
-        {
-          if (! scope.is_variable (struct_name))
-            error ("save: no such variable: '%s'", struct_name.c_str ());
-
-          struct_var = scope.varval (struct_name);
-        }
-
-      if (! struct_var.isstruct () || struct_var.numel () != 1)
-        error ("save: '%s' is not a scalar structure", struct_name.c_str ());
-
-      octave_scalar_map struct_var_map = struct_var.scalar_map_value ();
-
-      ++argv_idx;
-
-      if (argv_idx < argc)
-        {
-          for (int i = argv_idx; i < argc; i++)
-            {
-              if (! save_fields (os, struct_var_map, argv[i], fmt,
-                                 save_as_floats))
-                {
-                  warning ("save: no such field '%s.%s'",
-                           struct_name.c_str (), argv[i].c_str ());
-                }
-            }
-        }
-      else
-        save_fields (os, struct_var_map, "*", fmt, save_as_floats);
-    }
-  else
-    {
-      for (int i = argv_idx; i < argc; i++)
-        {
-          if (argv[i] == "")
-            continue;  // Skip empty vars for Matlab compatibility
-          if (! save_vars (os, argv[i], fmt, save_as_floats))
-            warning ("save: no such variable '%s'", argv[i].c_str ());
-        }
-    }
-}
-
-static void
-dump_octave_core (std::ostream& os, const char *fname, load_save_format fmt,
-                  bool save_as_floats)
-{
-  write_header (os, fmt);
-
-  octave::symbol_table& symtab =
-    octave::__get_symbol_table__ ("dump_octave_core");
-
-  octave::symbol_scope top_scope = symtab.top_scope ();
-
-  octave::symbol_record::context_id context = top_scope.current_context ();
-
-  std::list<octave::symbol_record> vars = top_scope.all_variables ();
-
-  double save_mem_size = 0;
-
-  for (const auto& var : vars)
-    {
-      octave_value val = var.varval (context);
-
-      if (val.is_defined ())
-        {
-          std::string name = var.name ();
-          std::string help;
-          bool global = var.is_global ();
-
-          double val_size = val.byte_size () / 1024;
-
-          // FIXME: maybe we should try to throw out the largest first...
-
-          if (Voctave_core_file_limit < 0
-              || save_mem_size + val_size < Voctave_core_file_limit)
-            {
-              save_mem_size += val_size;
-
-              do_save (os, val, name, help, global, fmt, save_as_floats);
-            }
-        }
-    }
-
-  message (nullptr, "save to '%s' complete", fname);
-}
-
-void
-dump_octave_core (void)
-{
-  if (Vcrash_dumps_octave_core)
-    {
-      // FIXME: should choose better filename?
-
-      const char *fname = Voctave_core_file_name.c_str ();
-
-      message (nullptr, "attempting to save variables to '%s'...", fname);
-
-      load_save_format format = LS_BINARY;
-
-      bool save_as_floats = false;
-
-      bool append = false;
-
-      bool use_zlib = false;
-
-      parse_save_options (Voctave_core_file_options, format, append,
-                          save_as_floats, use_zlib);
-
-      std::ios::openmode mode = std::ios::out;
-
-      // Matlab v7 files are always compressed
-      if (format == LS_MAT7_BINARY)
-        use_zlib = false;
-
-      if (format == LS_BINARY
-#if defined (HAVE_HDF5)
-          || format == LS_HDF5
-#endif
-          || format == LS_MAT_BINARY
-          || format == LS_MAT5_BINARY
-          || format == LS_MAT7_BINARY)
-        mode |= std::ios::binary;
-
-      mode |= append ? std::ios::ate : std::ios::trunc;
-
-#if defined (HAVE_HDF5)
-      if (format == LS_HDF5)
-        {
-          hdf5_ofstream file (fname, mode);
-
-          if (file.file_id >= 0)
-            {
-              dump_octave_core (file, fname, format, save_as_floats);
-
-              file.close ();
-            }
-          else
-            warning ("dump_octave_core: unable to open '%s' for writing...",
-                     fname);
-        }
-      else
-#endif
-        // don't insert any commands here!  The open brace below must
-        // go with the else above!
-        {
-#if defined (HAVE_ZLIB)
-          if (use_zlib)
-            {
-              gzofstream file (fname, mode);
-
-              if (file)
-                {
-                  dump_octave_core (file, fname, format, save_as_floats);
-
-                  file.close ();
-                }
-              else
-                warning ("dump_octave_core: unable to open '%s' for writing...",
-                         fname);
-            }
-          else
-#endif
-            {
-              std::ofstream file (fname, mode);
-
-              if (file)
-                {
-                  dump_octave_core (file, fname, format, save_as_floats);
-
-                  file.close ();
-                }
-              else
-                warning ("dump_octave_core: unable to open '%s' for writing...",
-                         fname);
-            }
-        }
-    }
-}
-
-DEFUN (save, args, nargout,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (save, interp, args, nargout,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {} save file
 @deftypefnx {} {} save options file
 @deftypefnx {} {} save options file @var{v1} @var{v2} @dots{}
@@ -1618,150 +1826,13 @@
 @seealso{load, save_default_options, save_header_format_string, save_precision, dlmread, csvread, fread}
 @end deftypefn */)
 {
-  // Here is where we would get the default save format if it were
-  // stored in a user preference variable.
-  load_save_format format = LS_TEXT;
-  bool save_as_floats = false;
-  bool append = false;
-  bool use_zlib = false;
-
-  // get default options
-  parse_save_options (Vsave_default_options, format, append, save_as_floats,
-                      use_zlib);
-
-  // override from command line
-  string_vector argv = args.make_argv ();
-
-  argv = parse_save_options (argv, format, append, save_as_floats, use_zlib);
-
-  int argc = argv.numel ();
-  int i = 0;
-
-  if (i == argc)
-    print_usage ();
-
-  if (save_as_floats && format == LS_TEXT)
-    error ("save: cannot specify both -text and -float-binary");
-
-  octave_value_list retval;
-
-  if (argv[i] == "-")
-    {
-      i++;
-
-#if defined (HAVE_HDF5)
-      if (format == LS_HDF5)
-        error ("save: cannot write HDF5 format to stdout");
-      else
-#endif
-        // don't insert any commands here!  the brace below must go
-        // with the "else" above!
-        {
-          if (append)
-            warning ("save: ignoring -append option for output to stdout");
-
-          if (nargout == 0)
-            save_vars (argv, i, argc, std::cout, format, save_as_floats, true);
-          else
-            {
-              std::ostringstream output_buf;
-              save_vars (argv, i, argc, output_buf, format, save_as_floats, true);
-              retval = octave_value (output_buf.str());
-            }
-        }
-    }
-
-  // Guard against things like 'save a*', which are probably mistakes...
-
-  else if (i == argc - 1 && glob_pattern_p (argv[i]))
-    print_usage ();
-  else
-    {
-      std::string fname = octave::sys::file_ops::tilde_expand (argv[i]);
-
-      i++;
-
-      // Matlab v7 files are always compressed
-      if (format == LS_MAT7_BINARY)
-        use_zlib = false;
-
-      std::ios::openmode mode
-        = (append ? (std::ios::app | std::ios::ate) : std::ios::out);
+  octave::load_save_system& load_save_sys = interp.get_load_save_system ();
 
-      if (format == LS_BINARY
-#if defined (HAVE_HDF5)
-          || format == LS_HDF5
-#endif
-          || format == LS_MAT_BINARY
-          || format == LS_MAT5_BINARY
-          || format == LS_MAT7_BINARY)
-        mode |= std::ios::binary;
-
-#if defined (HAVE_HDF5)
-      if (format == LS_HDF5)
-        {
-          // FIXME: It should be possible to append to HDF5 files.
-          if (append)
-            error ("save: appending to HDF5 files is not implemented");
-
-          std::string ascii_fname = octave::sys::get_ASCII_filename (fname);
-
-          bool write_header_info
-            = ! (append && H5Fis_hdf5 (ascii_fname.c_str ()) > 0);
-
-          hdf5_ofstream hdf5_file (fname.c_str (), mode);
-
-          if (hdf5_file.file_id == -1)
-            err_file_open ("save", fname);
-
-          save_vars (argv, i, argc, hdf5_file, format,
-                     save_as_floats, write_header_info);
-
-          hdf5_file.close ();
-        }
-      else
-#endif
-        // don't insert any statements here!  The brace below must go
-        // with the "else" above!
-        {
-#if defined (HAVE_ZLIB)
-          if (use_zlib)
-            {
-              gzofstream file (fname.c_str (), mode);
-
-              if (! file)
-                err_file_open ("save", fname);
-
-              bool write_header_info = ! file.tellp ();
-
-              save_vars (argv, i, argc, file, format,
-                         save_as_floats, write_header_info);
-
-              file.close ();
-            }
-          else
-#endif
-            {
-              std::ofstream file (fname.c_str (), mode);
-
-              if (! file)
-                err_file_open ("save", fname);
-
-              bool write_header_info = ! file.tellp ();
-
-              save_vars (argv, i, argc, file, format,
-                         save_as_floats, write_header_info);
-
-              file.close ();
-            }
-        }
-    }
-
-  return retval;
+  return load_save_sys.save (args, nargout);
 }
 
-DEFUN (crash_dumps_octave_core, args, nargout,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (crash_dumps_octave_core, interp, args, nargout,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} crash_dumps_octave_core ()
 @deftypefnx {} {@var{old_val} =} crash_dumps_octave_core (@var{new_val})
 @deftypefnx {} {} crash_dumps_octave_core (@var{new_val}, "local")
@@ -1775,11 +1846,13 @@
 @seealso{octave_core_file_limit, octave_core_file_name, octave_core_file_options}
 @end deftypefn */)
 {
-  return SET_INTERNAL_VARIABLE (crash_dumps_octave_core);
+  octave::load_save_system& load_save_sys = interp.get_load_save_system ();
+
+  return load_save_sys.crash_dumps_octave_core (args, nargout);
 }
 
-DEFUN (save_default_options, args, nargout,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (save_default_options, interp, args, nargout,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} save_default_options ()
 @deftypefnx {} {@var{old_val} =} save_default_options (@var{new_val})
 @deftypefnx {} {} save_default_options (@var{new_val}, "local")
@@ -1795,11 +1868,13 @@
 @seealso{save, save_header_format_string, save_precision}
 @end deftypefn */)
 {
-  return SET_NONEMPTY_INTERNAL_STRING_VARIABLE (save_default_options);
+  octave::load_save_system& load_save_sys = interp.get_load_save_system ();
+
+  return load_save_sys.save_default_options (args, nargout);
 }
 
-DEFUN (octave_core_file_limit, args, nargout,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (octave_core_file_limit, interp, args, nargout,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} octave_core_file_limit ()
 @deftypefnx {} {@var{old_val} =} octave_core_file_limit (@var{new_val})
 @deftypefnx {} {} octave_core_file_limit (@var{new_val}, "local")
@@ -1821,11 +1896,13 @@
 @seealso{crash_dumps_octave_core, octave_core_file_name, octave_core_file_options}
 @end deftypefn */)
 {
-  return SET_INTERNAL_VARIABLE (octave_core_file_limit);
+  octave::load_save_system& load_save_sys = interp.get_load_save_system ();
+
+  return load_save_sys.octave_core_file_limit (args, nargout);
 }
 
-DEFUN (octave_core_file_name, args, nargout,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (octave_core_file_name, interp, args, nargout,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} octave_core_file_name ()
 @deftypefnx {} {@var{old_val} =} octave_core_file_name (@var{new_val})
 @deftypefnx {} {} octave_core_file_name (@var{new_val}, "local")
@@ -1840,11 +1917,13 @@
 @seealso{crash_dumps_octave_core, octave_core_file_name, octave_core_file_options}
 @end deftypefn */)
 {
-  return SET_NONEMPTY_INTERNAL_STRING_VARIABLE (octave_core_file_name);
+  octave::load_save_system& load_save_sys = interp.get_load_save_system ();
+
+  return load_save_sys.octave_core_file_name (args, nargout);
 }
 
-DEFUN (octave_core_file_options, args, nargout,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (octave_core_file_options, interp, args, nargout,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} octave_core_file_options ()
 @deftypefnx {} {@var{old_val} =} octave_core_file_options (@var{new_val})
 @deftypefnx {} {} octave_core_file_options (@var{new_val}, "local")
@@ -1861,11 +1940,13 @@
 @seealso{crash_dumps_octave_core, octave_core_file_name, octave_core_file_limit}
 @end deftypefn */)
 {
-  return SET_NONEMPTY_INTERNAL_STRING_VARIABLE (octave_core_file_options);
+  octave::load_save_system& load_save_sys = interp.get_load_save_system ();
+
+  return load_save_sys.octave_core_file_options (args, nargout);
 }
 
-DEFUN (save_header_format_string, args, nargout,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (save_header_format_string, interp, args, nargout,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} save_header_format_string ()
 @deftypefnx {} {@var{old_val} =} save_header_format_string (@var{new_val})
 @deftypefnx {} {} save_header_format_string (@var{new_val}, "local")
@@ -1889,5 +1970,7 @@
 @seealso{strftime, save_default_options}
 @end deftypefn */)
 {
-  return SET_INTERNAL_VARIABLE (save_header_format_string);
+  octave::load_save_system& load_save_sys = interp.get_load_save_system ();
+
+  return load_save_sys.save_header_format_string (args, nargout);
 }
--- a/libinterp/corefcn/load-save.h	Wed Oct 31 14:29:03 2018 -0700
+++ b/libinterp/corefcn/load-save.h	Wed Oct 31 18:22:23 2018 -0400
@@ -29,76 +29,266 @@
 #include <string>
 
 #include "mach-info.h"
-
-namespace octave
-{
-  class symbol_record;
-}
+#include "symrec.h"
 
 class string_vector;
 class octave_value;
 
-// FIXME: maybe MAT5 and MAT7 should be options to MAT_BINARY.
-// Similarly, save_as_floats may be an option for LS_BINARY, LS_HDF5 etc.
-enum load_save_format_type
-{
-  LS_TEXT,
-  LS_BINARY,
-  LS_MAT_ASCII,
-  LS_MAT_BINARY,
-  LS_MAT5_BINARY,
-  LS_MAT7_BINARY,
-  LS_HDF5,
-  LS_UNKNOWN
-};
-
-enum load_save_format_options
+namespace octave
 {
-  // LS_MAT_ASCII options (not exclusive)
-  LS_MAT_ASCII_LONG = 1,
-  LS_MAT_ASCII_TABS = 2,
-  // LS_MAT_BINARY options
-  LS_MAT_BINARY_V5 = 1,
-  LS_MAT_BINARY_V7,
-  // zero means no option.
-  LS_NO_OPTION = 0
-};
+  class interpreter;
+  class load_save_format;
+
+  class load_save_system
+  {
+  public:
+
+    // FIXME: maybe MAT5 and MAT7 should be options to MAT_BINARY.
+    // Similarly, save_as_floats may be an option for LS_BINARY,
+    // LS_HDF5 etc.
+
+    enum format_type
+      {
+        TEXT,
+        BINARY,
+        MAT_ASCII,
+        MAT_BINARY,
+        MAT5_BINARY,
+        MAT7_BINARY,
+        HDF5,
+        UNKNOWN
+      };
+
+    enum format_options
+      {
+        // MAT_ASCII options (not exclusive)
+        MAT_ASCII_LONG = 1,
+        MAT_ASCII_TABS = 2,
+        // MAT_BINARY options
+        MAT_BINARY_V5 = 1,
+        MAT_BINARY_V7,
+        // zero means no option.
+        NO_OPTION = 0
+      };
+
+    load_save_system (interpreter& interp);
+
+    ~load_save_system (void);
+
+    load_save_system (const load_save_system&) = delete;
+
+    load_save_system& operator = (const load_save_system&) = delete;
+
+    octave_value crash_dumps_octave_core (const octave_value_list& args,
+                                          int nargout);
+
+    bool crash_dumps_octave_core (void) const
+    {
+      return m_crash_dumps_octave_core;
+    }
+
+    bool crash_dumps_octave_core (bool flag)
+    {
+      return set (m_crash_dumps_octave_core, flag);
+    }
+
+    octave_value octave_core_file_limit (const octave_value_list& args,
+                                         int nargout);
+
+    double octave_core_file_limit (void) const
+    {
+      return m_octave_core_file_limit;
+    }
+
+    double octave_core_file_limit (double limit)
+    {
+      return set (m_octave_core_file_limit, limit);
+    }
+
+    octave_value octave_core_file_name (const octave_value_list& args,
+                                        int nargout);
+
+    std::string octave_core_file_name (void) const
+    {
+      return m_octave_core_file_name;
+    }
+
+    std::string octave_core_file_name (const std::string& file)
+    {
+      return set (m_octave_core_file_name, file);
+    }
+
+    octave_value save_default_options (const octave_value_list& args,
+                                       int nargout);
+
+    std::string save_default_options (void) const
+    {
+      return m_save_default_options;
+    }
+
+    std::string save_default_options (const std::string& options)
+    {
+      return set (m_save_default_options, options);
+    }
+
+    octave_value octave_core_file_options (const octave_value_list& args,
+                                           int nargout);
+
+    std::string octave_core_file_options (void) const
+    {
+      return m_octave_core_file_options;
+    }
+
+    std::string octave_core_file_options (const std::string& options)
+    {
+      return set (m_octave_core_file_options, options);
+    }
+
+    octave_value save_header_format_string (const octave_value_list& args,
+                                            int nargout);
+
+    std::string save_header_format_string (void) const
+    {
+      return m_save_header_format_string;
+    }
+
+    std::string save_header_format_string (const std::string& format)
+    {
+      return set (m_save_header_format_string, format);
+    }
+
+    static load_save_format get_file_format (const std::string& fname,
+                                             const std::string& orig_fname,
+                                             bool& use_zlib,
+                                             bool quiet = false);
 
-class load_save_format
-{
-public:
-  load_save_format (load_save_format_type t,
-                    load_save_format_options o = LS_NO_OPTION)
-    : type (t), opts (o) { }
-  operator int (void) const
-  { return type; }
-  int type, opts;
-};
+    // FIXME: this is probably not the best public interface for
+    // loading and saving variables, but it is what is currently
+    // needed for the Fload and Fsave functions.
+
+    octave_value load_vars (std::istream& stream,
+                            const std::string& orig_fname,
+                            const load_save_format& fmt,
+                            mach_info::float_format flt_fmt,
+                            bool list_only, bool swap, bool verbose,
+                            const string_vector& argv, int argv_idx,
+                            int argc, int nargout);
+
+    static string_vector
+    parse_save_options (const string_vector& argv, load_save_format& fmt,
+                        bool& append, bool& save_as_floats, bool& use_zlib);
+
+    static string_vector
+    parse_save_options (const std::string& arg, load_save_format& fmt,
+                        bool& append, bool& save_as_floats, bool& use_zlib);
+
+    void save_vars (const string_vector& argv, int argv_idx, int argc,
+                    std::ostream& os, const load_save_format& fmt,
+                    bool save_as_floats, bool write_header_info);
+
+    void dump_octave_core (void);
+
+    octave_value_list
+    load (const octave_value_list& args = octave_value_list (),
+          int nargout = 0);
+
+    octave_value_list
+    save (const octave_value_list& args = octave_value_list (),
+          int nargout = 0);
+
+  private:
+
+    interpreter& m_interpreter;
+
+    // Write octave-workspace file if Octave crashes or is killed by a
+    // signal.
+    bool m_crash_dumps_octave_core;
+
+    // The maximum amount of memory (in kilobytes) that we will
+    // attempt to write to the Octave core file.
+    double m_octave_core_file_limit;
+
+    // The name of the Octave core file.
+    std::string m_octave_core_file_name;
+
+    // The default output format.  May be one of "binary", "text",
+    // "mat-binary", or "hdf5".
+    std::string m_save_default_options;
+
+    // The output format for Octave core files.
+    std::string m_octave_core_file_options;
+
+    // The format string for the comment line at the top of
+    // text-format save files.  Passed to strftime.  Should begin with
+    // '#' and contain no newline characters.
+    std::string m_save_header_format_string;
+
+    void write_header (std::ostream& os, const load_save_format& fmt);
+
+    size_t save_vars (std::ostream& os, const std::string& pattern,
+                      const load_save_format& fmt, bool save_as_floats);
+
+    void do_save (std::ostream& os, const octave_value& tc,
+                  const std::string& name, const std::string& help,
+                  bool global, const load_save_format& fmt,
+                  bool save_as_floats);
+
+    void do_save (std::ostream& os, const symbol_record& sr,
+                  symbol_record::context_id context,
+                  const load_save_format& fmt, bool save_as_floats);
+
+    size_t save_fields (std::ostream& os, const octave_scalar_map& m,
+                        const std::string& pattern,
+                        const load_save_format& fmt, bool save_as_floats);
+
+    void dump_octave_core (std::ostream& os, const char *fname,
+                           const load_save_format& fmt, bool save_as_floats);
+
+    void install_loaded_variable (const std::string& name,
+                                  const octave_value& val,
+                                  bool global, const std::string& /*doc*/);
+
+    static std::string init_save_header_format (void);
+
+    static load_save_format get_file_format (std::istream& file,
+                                             const std::string& filename);
+
+    template <typename T>
+    T set (T& var, const T& new_val)
+    {
+      T old_val = var;
+      var = new_val;
+      return old_val;
+    }
+  };
+
+  class load_save_format
+  {
+  public:
+
+    load_save_format (load_save_system::format_type type,
+                      load_save_system::format_options options = load_save_system::NO_OPTION)
+      : m_type (type), m_options (options)
+    { }
+
+    void set_type (load_save_system::format_type type) { m_type = type; }
+
+    load_save_system::format_type type (void) const { return m_type; }
+
+    void set_option (load_save_system::format_options option)
+    {
+      m_options |= option;
+    }
+
+    int options (void) const { return m_options; }
+
+  private:
+
+    load_save_system::format_type m_type;
+    int m_options;
+  };
+}
 
 extern void dump_octave_core (void);
 
-extern int
-read_binary_file_header (std::istream& is, bool& swap,
-                         octave::mach_info::float_format& flt_fmt,
-                         bool quiet = false);
-
-extern octave_value
-do_load (std::istream& stream, const std::string& orig_fname,
-         load_save_format format, octave::mach_info::float_format flt_fmt,
-         bool list_only, bool swap, bool verbose,
-         const string_vector& argv, int argv_idx, int argc, int nargout);
-
-extern OCTINTERP_API bool is_octave_data_file (const std::string& file);
-
-extern void
-do_save (std::ostream& os, const octave::symbol_record& sr,
-         load_save_format fmt, bool save_as_floats);
-
-extern void
-write_header (std::ostream& os, load_save_format format);
-
-extern void octave_prepare_hdf5 (void);
-
-extern void octave_finalize_hdf5 (void);
-
 #endif