Mercurial > octave
diff libinterp/corefcn/load-save.cc @ 31608:23664317f0d3
maint: merge stable to default
author | Rik <rik@octave.org> |
---|---|
date | Thu, 01 Dec 2022 20:05:44 -0800 |
parents | dfa5d9c3ae72 aac27ad79be6 |
children | 5f11de0e7440 |
line wrap: on
line diff
--- a/libinterp/corefcn/load-save.cc Thu Dec 01 14:28:07 2022 -0800 +++ b/libinterp/corefcn/load-save.cc Thu Dec 01 20:05:44 2022 -0800 @@ -92,1460 +92,1460 @@ OCTAVE_BEGIN_NAMESPACE(octave) - 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 ()); - } +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. +// Return TRUE if NAME matches one of the given globbing PATTERNS. - 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 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; - } + if (pattern.match (name)) + return true; + } - return false; - } + return false; +} - 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 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'; - 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"); + 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"); - return -1; - } + return -1; + } - char tmp = 0; - is.read (&tmp, 1); + char tmp = 0; + is.read (&tmp, 1); - flt_fmt = mopt_digit_to_float_format (tmp); + 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 == 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::ifstream file = sys::ifstream (fname.c_str (), - std::ios::in | std::ios::binary); + std::ifstream file = sys::ifstream (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 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); +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); - std::size_t dot_pos = fname.rfind ('.'); - std::size_t sep_pos = fname.find_last_of (sys::file_ops::dir_sep_chars ()); + std::size_t dot_pos = fname.rfind ('.'); + std::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. + 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); + sys::file_stat fs (fname); - if (! (fs.exists () && fs.is_reg ())) - fname = find_file_to_load (fname + ".mat", orig_name); - } - else - { - sys::file_stat fs (fname); + if (! (fs.exists () && fs.is_reg ())) + fname = find_file_to_load (fname + ".mat", orig_name); + } + else + { + sys::file_stat fs (fname); - if (! (fs.exists () && fs.is_reg ())) - { - fname = ""; + if (! (fs.exists () && fs.is_reg ())) + { + fname = ""; - error ("load: unable to find file %s", orig_name.c_str ()); - } - } + error ("load: unable to find file %s", orig_name.c_str ()); + } + } - return fname; - } + return fname; +} - // Return TRUE if PATTERN has any special globbing chars in it. +// Return TRUE if PATTERN has any special globbing chars in it. - static bool - glob_pattern_p (const std::string& pattern) - { - int open = 0; +static bool +glob_pattern_p (const std::string& pattern) +{ + int open = 0; - int len = pattern.length (); + int len = pattern.length (); - for (int i = 0; i < len; i++) - { - char c = pattern[i]; + for (int i = 0; i < len; i++) + { + char c = pattern[i]; - switch (c) - { - case '?': - case '*': - return true; + 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 '[': // 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 (open) + return true; + continue; - case '\\': - if (i == len - 1) - return false; - continue; + case '\\': + if (i == len - 1) + return false; + continue; - default: - continue; - } - } + default: + continue; + } + } - return false; - } + return false; +} - 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 ()) - { +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 (); + H5dont_atexit (); #endif - } +} - load_save_system::~load_save_system (void) - { +load_save_system::~load_save_system (void) +{ #if defined (HAVE_HDF5) - H5close (); + H5close (); #endif - } +} - 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::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"); - } +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"); +} - 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::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); - } +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); +} - 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); - } +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); +} - 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"); - } +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"); +} - 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; +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; #if defined (HAVE_HDF5_UTF8) - std::string ascii_fname = fname; + std::string ascii_fname = fname; #else - std::string ascii_fname = sys::get_ASCII_filename (fname); + std::string ascii_fname = sys::get_ASCII_filename (fname); #endif #if defined (HAVE_HDF5) - // check this before we open the file - if (H5Fis_hdf5 (ascii_fname.c_str ()) > 0) - return 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 = sys::ifstream (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 (! use_zlib) + { + std::ifstream file = sys::ifstream (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); - } + 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; - } + 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_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; + octave_scalar_map retstruct; - std::ostringstream output_buf; - std::list<std::string> symbol_names; + std::ostringstream output_buf; + std::list<std::string> symbol_names; - octave_idx_type count = 0; + octave_idx_type count = 0; - for (;;) - { - bool global = false; - octave_value tc; + for (;;) + { + bool global = false; + octave_value tc; - std::string name; - std::string doc; + std::string name; + std::string doc; - switch (fmt.type ()) - { - case TEXT: - name = read_text_data (stream, orig_fname, global, tc, count); - break; + 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 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_ASCII: + name = read_mat_ascii_data (stream, orig_fname, tc); + break; - case MAT_BINARY: - name = read_mat_binary_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; + case HDF5: + name = read_hdf5_data (stream, orig_fname, global, tc, doc, + argv, argv_idx, argc); + break; #endif - case MAT5_BINARY: - case MAT7_BINARY: - name = read_mat5_binary_element (stream, orig_fname, swap, - global, tc); - break; + 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 ()) + default: + err_unrecognized_data_fmt ("load"); 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 (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 - || matches_patterns (argv, argv_idx, argc, name)) - { - count++; - if (list_only) - { - if (verbose) - { - if (count == 1) - output_buf + 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 + 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 - - 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") - { - 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] == "-v7.3" || argv[i] == "-V7.3" || argv[i] == "-7.3") - { - error ("save: Matlab file format -v7.3 is not yet implemented"); - } -#if defined (HAVE_ZLIB) - else if (argv[i] == "-v7" || argv[i] == "-V7" || argv[i] == "-7" - || argv[i] == "-mat7-binary") - { - fmt.set_type (MAT7_BINARY); - } -#endif - else if (argv[i] == "-mat" || argv[i] == "-m" - || argv[i] == "-v6" || argv[i] == "-V6" || argv[i] == "-6" - || argv[i] == "-mat-binary") - { - fmt.set_type (MAT5_BINARY); - } - else if (argv[i] == "-v4" || argv[i] == "-V4" || argv[i] == "-4" - || argv[i] == "-mat4-binary") - { - 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]); - } - - 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")"); - } - - 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")"); - } - - if (append && use_zlib - && (fmt.type () != TEXT && fmt.type () != MAT_ASCII)) - error ("save: -append and -zip options can only be used together with a text format (-text or -ascii)"); - - return retval; - } - - 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); - - 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]; - - if (! m_interpreter.is_variable (struct_name)) - error ("save: no such variable: '%s'", struct_name.c_str ()); - - octave_value struct_var = m_interpreter.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 ()); - } - } - } - - void load_save_system::dump_octave_core (void) - { - if (m_crash_dumps_octave_core) - { - // FIXME: should choose better filename? - - 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); - - std::ios::openmode mode = std::ios::out; - - // Matlab v7 files are always compressed - if (fmt.type () == MAT7_BINARY) - use_zlib = false; - - 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) - 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 = sys::ofstream (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); - } - } - } - } - - 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; - - 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); - - std::size_t len = std::min (comment_string.length (), - static_cast<std::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); - } - - break; - -#if defined (HAVE_HDF5) - case HDF5: -#endif - case TEXT: - { - sys::localtime now; - - std::string comment_string = now.strftime (m_save_header_format_string); - - if (! comment_string.empty ()) - { -#if defined (HAVE_HDF5) - if (fmt.type () == HDF5) - { - hdf5_ofstream& hs = dynamic_cast<hdf5_ofstream&> (os); - H5Gset_comment (hs.file_id, "/", comment_string.c_str ()); + } + else + symbol_names.push_back (name); } else -#endif - os << comment_string << "\n"; + { + 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"; } } - break; - - default: - break; - } - } - - // Save variables with names matching PATTERN on stream OS in the - // format specified by FMT. + } + else if (retstruct.nfields () != 0) + retval = retstruct; - std::size_t load_save_system::save_vars (std::ostream& os, - const std::string& pattern, - const load_save_format& fmt, - bool save_as_floats) - { - tree_evaluator& tw = m_interpreter.get_evaluator (); - - symbol_info_list syminfo_list = tw.glob_symbol_info (pattern); - - std::size_t saved = 0; - - for (const auto& syminfo : syminfo_list) - { - do_save (os, syminfo, fmt, save_as_floats); - - saved++; - } - - return saved; - } + return retval; +} - 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; +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 - 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. + string_vector retval; + int argc = argv.numel (); - void load_save_system::do_save (std::ostream& os, - const symbol_info& syminfo, - const load_save_format& fmt, - bool save_as_floats) - { - octave_value val = syminfo.value (); - - if (val.is_defined ()) - { - std::string name = syminfo.name (); - std::string help; - bool global = syminfo.is_global (); - - do_save (os, val, name, help, global, fmt, save_as_floats); - } - } + bool do_double = false; + bool do_tabs = false; - // save fields of a scalar structure STR matching PATTERN on stream OS - // in the format specified by FMT. - - std::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); - - std::size_t saved = 0; - - for (auto it = m.begin (); it != m.end (); it++) - { - std::string empty_str; + 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] == "-v7.3" || argv[i] == "-V7.3" || argv[i] == "-7.3") + { + error ("save: Matlab file format -v7.3 is not yet implemented"); + } +#if defined (HAVE_ZLIB) + else if (argv[i] == "-v7" || argv[i] == "-V7" || argv[i] == "-7" + || argv[i] == "-mat7-binary") + { + fmt.set_type (MAT7_BINARY); + } +#endif + else if (argv[i] == "-mat" || argv[i] == "-m" + || argv[i] == "-v6" || argv[i] == "-V6" || argv[i] == "-6" + || argv[i] == "-mat-binary") + { + fmt.set_type (MAT5_BINARY); + } + else if (argv[i] == "-v4" || argv[i] == "-V4" || argv[i] == "-4" + || argv[i] == "-mat4-binary") + { + 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]); + } - if (pat.match (m.key (it))) - { - do_save (os, m.contents (it), m.key (it), empty_str, - 0, fmt, save_as_floats); + 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")"); + } - saved++; - } - } + 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")"); + } + + if (append && use_zlib + && (fmt.type () != TEXT && fmt.type () != MAT_ASCII)) + error ("save: -append and -zip options can only be used together with a text format (-text or -ascii)"); + + return retval; +} - return saved; - } +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; - void load_save_system::dump_octave_core (std::ostream& os, - const char *fname, - const load_save_format& fmt, - bool save_as_floats) - { + 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); - tree_evaluator& tw = m_interpreter.get_evaluator (); - - symbol_info_list syminfo_list = tw.top_scope_symbol_info (); - - double save_mem_size = 0; - - for (const auto& syminfo : syminfo_list) - { - octave_value val = syminfo.value (); - - std::string name = syminfo.name (); - std::string help; - bool global = syminfo.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; + 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"); - 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*/) - { - m_interpreter.install_variable (name, val, global); - } - - 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 () - + '>'); - } + std::string struct_name = argv[argv_idx]; - 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 (! m_interpreter.is_variable (struct_name)) + error ("save: no such variable: '%s'", struct_name.c_str ()); - 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); + octave_value struct_var = m_interpreter.varval (struct_name); - 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); + if (! struct_var.isstruct () || struct_var.numel () != 1) + error ("save: '%s' is not a scalar structure", struct_name.c_str ()); - std::string name_val = extract_keyword (file, "name"); - std::string type_val = extract_keyword (file, "type"); + octave_scalar_map struct_var_map = struct_var.scalar_map_value (); - 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. + ++argv_idx; - 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 = ""; + 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 ()); + } + } +} - // Function called with Matlab-style ["filename", options] syntax - if (argc > 1 && ! argv[1].empty () && argv[1].at (0) != '-') - { - orig_fname = argv[1]; - i++; - } +void load_save_system::dump_octave_core (void) +{ + if (m_crash_dumps_octave_core) + { + // FIXME: should choose better filename? + + const char *fname = m_octave_core_file_name.c_str (); - // 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. + message (nullptr, "attempting to save variables to '%s'...", fname); + + load_save_format fmt (BINARY); - load_save_format format = UNKNOWN; + bool save_as_floats = false; - bool list_only = false; - bool verbose = false; + bool append = false; - for (; i < argc; i++) - { - if (argv[i] == "-text" || argv[i] == "-t") - { - format = TEXT; - } - else if (argv[i] == "-binary" || argv[i] == "-b") - { - format = BINARY; - } - else if (argv[i] == "-hdf5" || argv[i] == "-h") - { + bool use_zlib = false; + + load_save_system::parse_save_options (m_octave_core_file_options, + fmt, append, save_as_floats, + use_zlib); + + std::ios::openmode mode = std::ios::out; + + // Matlab v7 files are always compressed + if (fmt.type () == MAT7_BINARY) + use_zlib = false; + + if (fmt.type () == BINARY #if defined (HAVE_HDF5) - format = HDF5; -#else - err_disabled_feature ("load", "HDF5"); + || fmt.type () == HDF5 #endif - } - else if (argv[i] == "-ascii" || argv[i] == "-a") - { - format = MAT_ASCII; - } - else if (argv[i] == "-v7.3" || argv[i] == "-V7.3" || argv[i] == "-7.3") - { - error ("load: Matlab file format -v7.3 is not yet implemented"); - } - else if (argv[i] == "-v7" || argv[i] == "-V7" || argv[i] == "-7" - || argv[i] == "-mat7-binary") - { - format = MAT7_BINARY; - } - else if (argv[i] == "-mat" || argv[i] == "-m" - || argv[i] == "-v6" || argv[i] == "-V6" || argv[i] == "-6" - || argv[i] == "-mat-binary") - { - format = MAT5_BINARY; - } - else if (argv[i] == "-v4" || argv[i] == "-V4" || argv[i] == "-4" - || argv[i] == "-mat4-binary") - { - format = MAT_BINARY; - } - else if (argv[i] == "-force" || argv[i] == "-f") - { - // Silently ignore this - // warning ("load: -force ignored"); - } - else if (argv[i] == "-import" || argv[i] == "-i") - { - warning ("load: -import ignored"); - } - else if (argv[i] == "-list" || argv[i] == "-l") - { - list_only = true; - } - else if (argv[i] == "-verbose" || argv[i] == "-v") - { - verbose = true; - } - else - break; - } + || fmt.type () == MAT_BINARY + || fmt.type () == MAT5_BINARY + || fmt.type () == MAT7_BINARY) + mode |= std::ios::binary; - 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++; + mode |= append ? std::ios::ate : std::ios::trunc; #if defined (HAVE_HDF5) - if (format.type () == HDF5) - error ("load: cannot read HDF5 format from stdin"); - else -#endif - if (format.type () != UNKNOWN) + if (fmt.type () == HDF5) + { + hdf5_ofstream file (fname, mode); + + if (file.file_id >= 0) { - // 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. + dump_octave_core (file, fname, fmt, save_as_floats); - retval = load_vars (std::cin, orig_fname, format, flt_fmt, - list_only, swap, verbose, argv, i, - argc, nargout); + file.close (); } 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 + warning ("dump_octave_core: unable to open '%s' for writing...", + fname); + } + else #endif - // don't insert any statements here; the "else" above has to - // go with the "if" below!!!!! - if (format.type () != UNKNOWN) + // don't insert any commands here! The open brace below must + // go with the else above! + { +#if defined (HAVE_ZLIB) + if (use_zlib) { - i++; - - // Always open in binary mode and handle various - // line-endings explicitly. - std::ios::openmode mode = std::ios::in | std::ios::binary; + gzofstream file (fname, mode); -#if defined (HAVE_ZLIB) - if (use_zlib) + if (file) { - gzifstream file (fname.c_str (), mode); - - if (! file) - err_file_open ("load", orig_fname); + dump_octave_core (file, fname, fmt, save_as_floats); - 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; - } - } + file.close (); + } + else + warning ("dump_octave_core: unable to open '%s' for writing...", + fname); + } + else +#endif + { + std::ofstream file = sys::ofstream (fname, mode); - retval = load_vars (file, orig_fname, format, flt_fmt, - list_only, swap, verbose, argv, i, - argc, nargout); + if (file) + { + dump_octave_core (file, fname, fmt, save_as_floats); file.close (); } else -#endif - { - std::ifstream file = sys::ifstream (fname.c_str (), mode); + warning ("dump_octave_core: unable to open '%s' for writing...", + fname); + } + } + } +} - if (! file) - error ("load: unable to open input file '%s'", - orig_fname.c_str ()); +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; - 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; - } - } + 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); - retval = load_vars (file, orig_fname, format, flt_fmt, - list_only, swap, verbose, argv, i, - argc, nargout); + std::size_t len = std::min (comment_string.length (), + static_cast<std::size_t> (124)); + memset (headertext, ' ', 124); + memcpy (headertext, comment_string.data (), len); - file.close (); - } - } - else - error ("load: unable to determine file format of '%s'", - orig_fname.c_str ()); + // 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); } - return retval; - } + break; + +#if defined (HAVE_HDF5) + case HDF5: +#endif + case TEXT: + { + sys::localtime now; + + std::string comment_string = now.strftime (m_save_header_format_string); - 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; + if (! comment_string.empty ()) + { +#if defined (HAVE_HDF5) + if (fmt.type () == 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; + } +} - // get default options - parse_save_options (save_default_options (), format, append, - save_as_floats, use_zlib); +// Save variables with names matching PATTERN on stream OS in the +// format specified by FMT. + +std::size_t load_save_system::save_vars (std::ostream& os, + const std::string& pattern, + const load_save_format& fmt, + bool save_as_floats) +{ + tree_evaluator& tw = m_interpreter.get_evaluator (); - // override from command line - string_vector argv = args.make_argv (); + symbol_info_list syminfo_list = tw.glob_symbol_info (pattern); + + std::size_t saved = 0; - argv = parse_save_options (argv, format, append, save_as_floats, use_zlib); + for (const auto& syminfo : syminfo_list) + { + do_save (os, syminfo, fmt, save_as_floats); - int argc = argv.numel (); - int i = 0; + saved++; + } + + return saved; +} - if (i == argc) - print_usage (); - - if (save_as_floats && format.type () == TEXT) - error ("save: cannot specify both -text and -float-binary"); +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; - octave_value_list retval; + case BINARY: + save_binary_data (os, tc, name, help, global, save_as_floats); + break; - if (argv[i] == "-") - { - i++; + 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) - if (format.type () == HDF5) - error ("save: cannot write HDF5 format to stdout"); - else + case HDF5: + save_hdf5_data (os, tc, name, help, global, save_as_floats); + break; #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"); + + 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_info& syminfo, + const load_save_format& fmt, + bool save_as_floats) +{ + octave_value val = syminfo.value (); + + if (val.is_defined ()) + { + std::string name = syminfo.name (); + std::string help; + bool global = syminfo.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. + +std::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); + + std::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); + + tree_evaluator& tw = m_interpreter.get_evaluator (); + + symbol_info_list syminfo_list = tw.top_scope_symbol_info (); + + double save_mem_size = 0; + + for (const auto& syminfo : syminfo_list) + { + octave_value val = syminfo.value (); + + std::string name = syminfo.name (); + std::string help; + bool global = syminfo.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*/) +{ + m_interpreter.install_variable (name, val, global); +} + +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 (nargout == 0) - save_vars (argv, i, argc, octave_stdout, 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()); - } - } - } + 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. - // Guard against things like 'save a*', which are probably mistakes... + 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; - else if (i == argc - 1 && glob_pattern_p (argv[i])) - print_usage (); - else - { - std::string fname = sys::file_ops::tilde_expand (argv[i]); - - i++; + for (; i < argc; i++) + { + if (argv[i] == "-text" || argv[i] == "-t") + { + format = TEXT; + } + else if (argv[i] == "-binary" || argv[i] == "-b") + { + format = 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] == "-ascii" || argv[i] == "-a") + { + format = MAT_ASCII; + } + else if (argv[i] == "-v7.3" || argv[i] == "-V7.3" || argv[i] == "-7.3") + { + error ("load: Matlab file format -v7.3 is not yet implemented"); + } + else if (argv[i] == "-v7" || argv[i] == "-V7" || argv[i] == "-7" + || argv[i] == "-mat7-binary") + { + format = MAT7_BINARY; + } + else if (argv[i] == "-mat" || argv[i] == "-m" + || argv[i] == "-v6" || argv[i] == "-V6" || argv[i] == "-6" + || argv[i] == "-mat-binary") + { + format = MAT5_BINARY; + } + else if (argv[i] == "-v4" || argv[i] == "-V4" || argv[i] == "-4" + || argv[i] == "-mat4-binary") + { + format = MAT_BINARY; + } + else if (argv[i] == "-force" || argv[i] == "-f") + { + // Silently ignore this + // warning ("load: -force ignored"); + } + else if (argv[i] == "-import" || argv[i] == "-i") + { + warning ("load: -import ignored"); + } + else if (argv[i] == "-list" || argv[i] == "-l") + { + list_only = true; + } + else if (argv[i] == "-verbose" || argv[i] == "-v") + { + verbose = true; + } + else + break; + } - // Matlab v7 files are always compressed - if (format.type () == MAT7_BINARY) - use_zlib = false; + if (orig_fname == "") + { + if (i == argc) + print_usage (); - std::ios::openmode mode - = (append ? (std::ios::app | std::ios::ate) : std::ios::out); + orig_fname = argv[i]; + } + else + i--; - // Always open in binary mode to save line endings as is. - mode |= std::ios::binary; + 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) + if (format.type () == HDF5) + error ("load: cannot read HDF5 format from stdin"); + else +#endif + if (format.type () != UNKNOWN) { - // FIXME: It should be possible to append to HDF5 files. - if (append) - error ("save: appending to HDF5 files is not implemented"); - -# if defined (HAVE_HDF5_UTF8) - bool write_header_info - = ! (append && H5Fis_hdf5 (fname.c_str ()) > 0); -# else - std::string ascii_fname = sys::get_ASCII_filename (fname); + // 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. - bool write_header_info - = ! (append && H5Fis_hdf5 (ascii_fname.c_str ()) > 0); -# endif - - 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 (); + 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 brace below must go - // with the "else" above! + // 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) { - gzofstream file (fname.c_str (), mode); + gzifstream file (fname.c_str (), mode); if (! file) - err_file_open ("save", fname); + err_file_open ("load", orig_fname); - bool write_header_info = ! file.tellp (); + 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; + } + } - save_vars (argv, i, argc, file, format, save_as_floats, - write_header_info); + retval = load_vars (file, orig_fname, format, flt_fmt, + list_only, swap, verbose, argv, i, + argc, nargout); file.close (); } else #endif { - std::ofstream file = sys::ofstream (fname.c_str (), mode); + std::ifstream file = sys::ifstream (fname.c_str (), mode); if (! file) - err_file_open ("save", fname); + error ("load: unable to open input file '%s'", + orig_fname.c_str ()); - bool write_header_info = ! file.tellp (); + 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; + } + } - save_vars (argv, i, argc, file, format, save_as_floats, - write_header_info); + 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 ()); + + } + + 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"); + + 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 (nargout == 0) + save_vars (argv, i, argc, octave_stdout, 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++; - return retval; - } + // 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); + + // Always open in binary mode to save line endings as is. + 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"); + +# if defined (HAVE_HDF5_UTF8) + bool write_header_info + = ! (append && H5Fis_hdf5 (fname.c_str ()) > 0); +# else + std::string ascii_fname = sys::get_ASCII_filename (fname); + + bool write_header_info + = ! (append && H5Fis_hdf5 (ascii_fname.c_str ()) > 0); +# endif + + 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 = sys::ofstream (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; +} DEFMETHOD (load, interp, args, nargout, doc: /* -*- texinfo -*-