Mercurial > octave
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