1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING.  If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 #  include "config.h"
28 #endif
29 
30 #if defined (HAVE_HDF5)
31 
32 #include <cctype>
33 
34 #include <iomanip>
35 #include <istream>
36 #include <limits>
37 #include <ostream>
38 #include <string>
39 #include <vector>
40 
41 #include "byte-swap.h"
42 #include "data-conv.h"
43 #include "file-ops.h"
44 #include "glob-match.h"
45 #include "lo-mappers.h"
46 #include "mach-info.h"
47 #include "oct-env.h"
48 #include "oct-time.h"
49 #include "quit.h"
50 #include "str-vec.h"
51 #include "oct-locbuf.h"
52 
53 #include "Cell.h"
54 #include "defun.h"
55 #include "error.h"
56 #include "errwarn.h"
57 #include "interpreter.h"
58 #include "interpreter-private.h"
59 #include "load-save.h"
60 #include "oct-hdf5.h"
61 #include "ovl.h"
62 #include "oct-map.h"
63 #include "ov-cell.h"
64 #include "pager.h"
65 #include "sysdep.h"
66 #include "unwind-prot.h"
67 #include "utils.h"
68 #include "variables.h"
69 #include "version.h"
70 #include "dMatrix.h"
71 #include "ov-lazy-idx.h"
72 
73 #include "ls-utils.h"
74 #include "ls-hdf5.h"
75 
76 #if defined (HAVE_HDF5)
77 
78 static hid_t
check_hdf5_id_value(octave_hdf5_id id,const char * who)79 check_hdf5_id_value (octave_hdf5_id id, const char *who)
80 {
81   if (id > std::numeric_limits<hid_t>::max ())
82     error ("%s: internal error: ID too large for hid_t", who);
83 
84   return static_cast<hid_t> (id);
85 }
86 
87 #endif
88 
hdf5_fstreambase(const char * name,int mode,int)89 hdf5_fstreambase::hdf5_fstreambase (const char *name, int mode, int /* prot */)
90   : file_id (-1), current_item (-1)
91 {
92 #if defined (HAVE_HDF5)
93   open_create (name, mode);
94 
95   current_item = 0;
96 
97 #else
98   err_disabled_feature ("hdf5_fstreambase", "HDF5");
99 #endif
100 }
101 
102 void
close(void)103 hdf5_fstreambase::close (void)
104 {
105 #if defined (HAVE_HDF5)
106 
107   if (file_id >= 0)
108     {
109       if (H5Fclose (file_id) < 0)
110         std::ios::setstate (std::ios::badbit);
111       file_id = -1;
112     }
113 
114 #else
115   // This shouldn't happen because construction of hdf5_fstreambase
116   // objects is supposed to be impossible if HDF5 is not available.
117 
118   panic_impossible ();
119 #endif
120 }
121 
122 void
open(const char * name,int mode,int)123 hdf5_fstreambase::open (const char *name, int mode, int)
124 {
125 #if defined (HAVE_HDF5)
126 
127   clear ();
128 
129   open_create (name, mode);
130 
131   current_item = 0;
132 
133 #else
134   // This shouldn't happen because construction of hdf5_fstreambase
135   // objects is supposed to be impossible if HDF5 is not available.
136 
137   panic_impossible ();
138 #endif
139 }
140 
141 void
open_create(const char * name,int mode)142 hdf5_fstreambase::open_create (const char *name, int mode)
143 {
144 #if defined (HAVE_HDF5)
145   // Open the HDF5 file NAME.  If it does not exist, create the file.
146 
147   std::string fname_str (name);
148   std::string ascii_fname_str = octave::sys::get_ASCII_filename (fname_str);
149   const char *ascii_fname = ascii_fname_str.c_str ();
150 
151   if (mode & std::ios::in)
152     file_id = H5Fopen (ascii_fname, H5F_ACC_RDONLY, octave_H5P_DEFAULT);
153   else if (mode & std::ios::out)
154     {
155       if (mode & std::ios::app && H5Fis_hdf5 (ascii_fname) > 0)
156         file_id = H5Fopen (ascii_fname, H5F_ACC_RDWR, octave_H5P_DEFAULT);
157       else
158 #  if defined (OCTAVE_USE_WINDOWS_API)
159         {
160           // Check whether file already exists
161           std::string abs_ascii_fname
162             = octave::sys::canonicalize_file_name (ascii_fname_str);
163           if (! abs_ascii_fname.empty ())
164             {
165               // Use the existing file
166               file_id = H5Fcreate (ascii_fname, H5F_ACC_TRUNC,
167                                    octave_H5P_DEFAULT, octave_H5P_DEFAULT);
168               if (file_id < 0)
169                 std::ios::setstate (std::ios::badbit);
170 
171               return;
172             }
173 
174           // Check whether filename contains non-ASCII (UTF-8) characters.
175           std::string::const_iterator first_non_ASCII
176             = std::find_if (fname_str.begin (), fname_str.end (),
177                             [](char c) { return (c < 0 || c >= 128); });
178           if (first_non_ASCII == fname_str.end ())
179             {
180               // No non-ASCII characters
181               file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
182                                    octave_H5P_DEFAULT);
183               if (file_id < 0)
184                 std::ios::setstate (std::ios::badbit);
185 
186               return;
187             }
188 
189           // Create file in temp folder
190           std::string tmp_name = octave::sys::tempnam ("", "oct-");
191           octave_hdf5_id hdf5_fid = H5Fcreate (tmp_name.c_str (), H5F_ACC_TRUNC,
192                                                octave_H5P_DEFAULT,
193                                                octave_H5P_DEFAULT);
194           if (hdf5_fid < 0)
195             {
196               file_id = -1;
197               std::ios::setstate (std::ios::badbit);
198               return;
199             }
200 
201           // Close file
202           H5Fclose (hdf5_fid);
203 
204           // Move temporary file to final destination
205           std::string msg;
206           int res = octave::sys::rename (tmp_name, name, msg);
207           if (res < 0)
208             {
209               std::ios::setstate (std::ios::badbit);
210               file_id = -1;
211               return;
212             }
213 
214           // Open file at final location
215           ascii_fname_str = octave::sys::get_ASCII_filename (fname_str);
216           ascii_fname = ascii_fname_str.c_str ();
217           file_id = H5Fopen (ascii_fname, H5F_ACC_RDWR, octave_H5P_DEFAULT);
218         }
219 #  else
220         file_id = H5Fcreate (name, H5F_ACC_TRUNC, octave_H5P_DEFAULT,
221                              octave_H5P_DEFAULT);
222 #  endif
223     }
224   if (file_id < 0)
225     std::ios::setstate (std::ios::badbit);
226 
227   return;
228 
229 #else
230   // This shouldn't happen because construction of hdf5_fstreambase
231   // objects is supposed to be impossible if HDF5 is not available.
232 
233   panic_impossible ();
234 #endif
235 }
236 
237 static std::string
make_valid_identifier(const std::string & nm)238 make_valid_identifier (const std::string& nm)
239 {
240   std::string retval;
241 
242   std::size_t nm_len = nm.length ();
243 
244   if (nm_len > 0)
245     {
246       if (! isalpha (nm[0]))
247         retval += '_';
248 
249       for (std::size_t i = 0; i < nm_len; i++)
250         {
251           char c = nm[i];
252           retval += (isalnum (c) || c == '_') ? c : '_';
253         }
254     }
255 
256   return retval;
257 }
258 
259 // Given two compound types t1 and t2, determine whether they
260 // are compatible for reading/writing.  This function only
261 // works for non-nested types composed of simple elements (ints, floats...),
262 // which is all we need it for
263 
264 bool
hdf5_types_compatible(octave_hdf5_id t1,octave_hdf5_id t2)265 hdf5_types_compatible (octave_hdf5_id t1, octave_hdf5_id t2)
266 {
267 #if defined (HAVE_HDF5)
268 
269   int n;
270   if ((n = H5Tget_nmembers (t1)) != H5Tget_nmembers (t2))
271     return false;
272 
273   for (int i = 0; i < n; ++i)
274     {
275       hid_t mt1 = H5Tget_member_type (t1, i);
276       hid_t mt2 = H5Tget_member_type (t2, i);
277 
278       if (H5Tget_class (mt1) != H5Tget_class (mt2))
279         return false;
280 
281       H5Tclose (mt2);
282       H5Tclose (mt1);
283     }
284 
285   return true;
286 
287 #else
288   err_disabled_feature ("hdf5_types_compatible", "HDF5");
289 #endif
290 }
291 
292 // Return true if loc_id has the attribute named attr_name, and false
293 // otherwise.
294 
295 bool
hdf5_check_attr(octave_hdf5_id loc_id,const char * attr_name)296 hdf5_check_attr (octave_hdf5_id loc_id, const char *attr_name)
297 {
298 #if defined (HAVE_HDF5)
299 
300   bool retval = false;
301 
302   // we have to pull some shenanigans here to make sure
303   // HDF5 doesn't print out all sorts of error messages if we
304   // call H5Aopen for a non-existing attribute
305 
306   H5E_auto_t err_func;
307   void *err_func_data;
308 
309   // turn off error reporting temporarily, but save the error
310   // reporting function:
311 
312 #if defined (HAVE_HDF5_18)
313   H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data);
314   H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
315 #else
316   H5Eget_auto (&err_func, &err_func_data);
317   H5Eset_auto (nullptr, nullptr);
318 #endif
319 
320   hid_t attr_id = H5Aopen_name (loc_id, attr_name);
321 
322   if (attr_id >= 0)
323     {
324       // successful
325       retval = true;
326       H5Aclose (attr_id);
327     }
328 
329   // restore error reporting:
330 #if defined (HAVE_HDF5_18)
331   H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data);
332 #else
333   H5Eset_auto (err_func, err_func_data);
334 #endif
335   return retval;
336 
337 #else
338   err_disabled_feature ("hdf5_check_attr", "HDF5");
339 #endif
340 }
341 
342 bool
hdf5_get_scalar_attr(octave_hdf5_id loc_id,octave_hdf5_id type_id,const char * attr_name,void * buf)343 hdf5_get_scalar_attr (octave_hdf5_id loc_id, octave_hdf5_id type_id,
344                       const char *attr_name, void *buf)
345 {
346 #if defined (HAVE_HDF5)
347 
348   bool retval = false;
349 
350   // we have to pull some shenanigans here to make sure
351   // HDF5 doesn't print out all sorts of error messages if we
352   // call H5Aopen for a non-existing attribute
353 
354   H5E_auto_t err_func;
355   void *err_func_data;
356 
357   // turn off error reporting temporarily, but save the error
358   // reporting function:
359 
360 #if defined (HAVE_HDF5_18)
361   H5Eget_auto (octave_H5E_DEFAULT, &err_func, &err_func_data);
362   H5Eset_auto (octave_H5E_DEFAULT, nullptr, nullptr);
363 #else
364   H5Eget_auto (&err_func, &err_func_data);
365   H5Eset_auto (nullptr, nullptr);
366 #endif
367 
368   hid_t attr_id = H5Aopen_name (loc_id, attr_name);
369 
370   if (attr_id >= 0)
371     {
372       hid_t space_id = H5Aget_space (attr_id);
373 
374       hsize_t rank = H5Sget_simple_extent_ndims (space_id);
375 
376       if (rank == 0)
377         retval = H5Aread (attr_id, type_id, buf) >= 0;
378       H5Aclose (attr_id);
379     }
380 
381   // restore error reporting:
382 #if defined (HAVE_HDF5_18)
383   H5Eset_auto (octave_H5E_DEFAULT, err_func, err_func_data);
384 #else
385   H5Eset_auto (err_func, err_func_data);
386 #endif
387   return retval;
388 
389 #else
390   err_disabled_feature ("hdf5_get_scalar_attr", "HDF5");
391 #endif
392 }
393 
394 // The following subroutines creates an HDF5 representations of the way
395 // we will store Octave complex types (pairs of floating-point numbers).
396 // NUM_TYPE is the HDF5 numeric type to use for storage (e.g.
397 // H5T_NATIVE_DOUBLE to save as 'double').  Note that any necessary
398 // conversions are handled automatically by HDF5.
399 
400 octave_hdf5_id
hdf5_make_complex_type(octave_hdf5_id num_type)401 hdf5_make_complex_type (octave_hdf5_id num_type)
402 {
403 #if defined (HAVE_HDF5)
404 
405   hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 2);
406 
407   H5Tinsert (type_id, "real", 0 * sizeof (double), num_type);
408   H5Tinsert (type_id, "imag", 1 * sizeof (double), num_type);
409 
410   return type_id;
411 
412 #else
413   err_disabled_feature ("hdf5_make_complex_type", "HDF5");
414 #endif
415 }
416 
417 #if defined (HAVE_HDF5)
418 
419 // The following subroutine creates an HDF5 representation of the way
420 // we will store Octave range types (triplets of floating-point numbers).
421 // NUM_TYPE is the HDF5 numeric type to use for storage
422 // (e.g., H5T_NATIVE_DOUBLE to save as 'double').
423 // Note that any necessary conversions are handled automatically by HDF5.
424 
425 static hid_t
hdf5_make_range_type(hid_t num_type)426 hdf5_make_range_type (hid_t num_type)
427 {
428   hid_t type_id = H5Tcreate (H5T_COMPOUND, sizeof (double) * 3);
429 
430   H5Tinsert (type_id, "base", 0 * sizeof (double), num_type);
431   H5Tinsert (type_id, "limit", 1 * sizeof (double), num_type);
432   H5Tinsert (type_id, "increment", 2 * sizeof (double), num_type);
433 
434   return type_id;
435 }
436 
437 static herr_t
load_inline_fcn(hid_t loc_id,const char * name,octave_value & retval)438 load_inline_fcn (hid_t loc_id, const char *name, octave_value& retval)
439 {
440 #if defined (HAVE_HDF5)
441 
442   hid_t group_hid, data_hid, space_hid, type_hid, type_class_hid, st_id;
443   hsize_t rank;
444   int slen;
445 
446 #if defined (HAVE_HDF5_18)
447   group_hid = H5Gopen (loc_id, name, octave_H5P_DEFAULT);
448 #else
449   group_hid = H5Gopen (loc_id, name);
450 #endif
451   if (group_hid < 0) return -1;
452 
453 #if defined (HAVE_HDF5_18)
454   data_hid = H5Dopen (group_hid, "args", octave_H5P_DEFAULT);
455 #else
456   data_hid = H5Dopen (group_hid, "args");
457 #endif
458   space_hid = H5Dget_space (data_hid);
459   rank = H5Sget_simple_extent_ndims (space_hid);
460 
461   if (rank != 2)
462     {
463       H5Dclose (data_hid);
464       H5Sclose (space_hid);
465       H5Gclose (group_hid);
466       return -1;
467     }
468 
469   OCTAVE_LOCAL_BUFFER (hsize_t, hdims, rank);
470   OCTAVE_LOCAL_BUFFER (hsize_t, maxdims, rank);
471 
472   H5Sget_simple_extent_dims (space_hid, hdims, maxdims);
473 
474   octave_value_list args (hdims[1]+1);
475 
476   OCTAVE_LOCAL_BUFFER (char, s1, hdims[0] * hdims[1]);
477 
478   if (H5Dread (data_hid, H5T_NATIVE_UCHAR, octave_H5S_ALL, octave_H5S_ALL,
479                octave_H5P_DEFAULT, s1) < 0)
480     {
481       H5Dclose (data_hid);
482       H5Sclose (space_hid);
483       H5Gclose (group_hid);
484       return -1;
485     }
486 
487   H5Dclose (data_hid);
488   H5Sclose (space_hid);
489 
490   for (std::size_t i = 0; i < hdims[1]; i++)
491     args(i+1) = std::string (s1 + i*hdims[0]);
492 
493 #if defined (HAVE_HDF5_18)
494   data_hid = H5Dopen (group_hid, "nm", octave_H5P_DEFAULT);
495 #else
496   data_hid = H5Dopen (group_hid, "nm");
497 #endif
498 
499   if (data_hid < 0)
500     {
501       H5Gclose (group_hid);
502       return -1;
503     }
504 
505   type_hid = H5Dget_type (data_hid);
506   type_class_hid = H5Tget_class (type_hid);
507 
508   if (type_class_hid != H5T_STRING)
509     {
510       H5Tclose (type_hid);
511       H5Dclose (data_hid);
512       H5Gclose (group_hid);
513       return -1;
514     }
515 
516   space_hid = H5Dget_space (data_hid);
517   rank = H5Sget_simple_extent_ndims (space_hid);
518 
519   if (rank != 0)
520     {
521       H5Sclose (space_hid);
522       H5Tclose (type_hid);
523       H5Dclose (data_hid);
524       H5Gclose (group_hid);
525       return -1;
526     }
527 
528   slen = H5Tget_size (type_hid);
529   if (slen < 0)
530     {
531       H5Sclose (space_hid);
532       H5Tclose (type_hid);
533       H5Dclose (data_hid);
534       H5Gclose (group_hid);
535       return -1;
536     }
537 
538   OCTAVE_LOCAL_BUFFER (char, nm_tmp, slen);
539 
540   // create datatype for (null-terminated) string to read into:
541   st_id = H5Tcopy (H5T_C_S1);
542   H5Tset_size (st_id, slen);
543 
544   if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
545                octave_H5P_DEFAULT, nm_tmp) < 0)
546     {
547       H5Sclose (space_hid);
548       H5Tclose (type_hid);
549       H5Gclose (group_hid);
550       return -1;
551     }
552   H5Tclose (st_id);
553   H5Dclose (data_hid);
554   // NAME is obsolete and unused.
555   // std::string name (nm_tmp);
556 
557 #if defined (HAVE_HDF5_18)
558   data_hid = H5Dopen (group_hid, "iftext", octave_H5P_DEFAULT);
559 #else
560   data_hid = H5Dopen (group_hid, "iftext");
561 #endif
562 
563   if (data_hid < 0)
564     {
565       H5Gclose (group_hid);
566       return -1;
567     }
568 
569   type_hid = H5Dget_type (data_hid);
570   type_class_hid = H5Tget_class (type_hid);
571 
572   if (type_class_hid != H5T_STRING)
573     {
574       H5Tclose (type_hid);
575       H5Dclose (data_hid);
576       H5Gclose (group_hid);
577       return -1;
578     }
579 
580   space_hid = H5Dget_space (data_hid);
581   rank = H5Sget_simple_extent_ndims (space_hid);
582 
583   if (rank != 0)
584     {
585       H5Sclose (space_hid);
586       H5Tclose (type_hid);
587       H5Dclose (data_hid);
588       H5Gclose (group_hid);
589       return -1;
590     }
591 
592   slen = H5Tget_size (type_hid);
593   if (slen < 0)
594     {
595       H5Sclose (space_hid);
596       H5Tclose (type_hid);
597       H5Dclose (data_hid);
598       H5Gclose (group_hid);
599       return -1;
600     }
601 
602   OCTAVE_LOCAL_BUFFER (char, iftext_tmp, slen);
603 
604   // create datatype for (null-terminated) string to read into:
605   st_id = H5Tcopy (H5T_C_S1);
606   H5Tset_size (st_id, slen);
607 
608   if (H5Dread (data_hid, st_id, octave_H5S_ALL, octave_H5S_ALL,
609                octave_H5P_DEFAULT, iftext_tmp) < 0)
610     {
611       H5Sclose (space_hid);
612       H5Tclose (type_hid);
613       H5Gclose (group_hid);
614       return -1;
615     }
616   H5Tclose (st_id);
617   H5Dclose (data_hid);
618 
619   args(0) = std::string (iftext_tmp);
620 
621   octave::interpreter& interp
622     = octave::__get_interpreter__ ("load_inline_fcn");
623 
624   octave_value_list tmp = interp.feval ("inline", args, 1);
625 
626   if (tmp.length () > 0)
627     {
628       retval = tmp(0);
629       return 1;
630     }
631 
632 #else
633   octave_unused_parameter (loc_id);
634   octave_unused_parameter (name);
635   octave_unused_parameter (retval);
636 
637   warn_load ("hdf5");
638 #endif
639 
640   return -1;
641 }
642 
643 // This function is designed to be passed to H5Giterate, which calls it
644 // on each data item in an HDF5 file.  For the item whose name is NAME in
645 // the group GROUP_ID, this function sets dv->tc to an Octave representation
646 // of that item.  (dv must be a pointer to hdf5_callback_data.)  (It also
647 // sets the other fields of dv).
648 //
649 // It returns 1 on success (in which case H5Giterate stops and returns),
650 // -1 on error, and 0 to tell H5Giterate to continue on to the next item
651 // (e.g., if NAME was a data type we don't recognize).
652 //
653 // This function must not throw an exception.
654 
655 static herr_t
hdf5_read_next_data_internal(hid_t group_id,const char * name,void * dv)656 hdf5_read_next_data_internal (hid_t group_id, const char *name, void *dv)
657 {
658   hdf5_callback_data *d = static_cast<hdf5_callback_data *> (dv);
659   hid_t type_id = -1;
660   hid_t type_class_id = -1;
661   hid_t data_id = -1;
662   hid_t subgroup_id = -1;
663   hid_t space_id = -1;;
664 
665   H5G_stat_t info;
666   herr_t retval = 0;
667   bool ident_valid = octave::valid_identifier (name);
668 
669   std::string vname = name;
670 
671   octave::type_info& type_info
672     = octave::__get_type_info__ ("hdf5_read_next_data_internal");
673 
674   // Allow identifiers as all digits so we can load lists saved by
675   // earlier versions of Octave.
676 
677   if (! ident_valid)
678     {
679       // fix the identifier, replacing invalid chars with underscores
680       vname = make_valid_identifier (vname);
681 
682       // check again (in case vname was null, empty, or some such thing):
683       ident_valid = octave::valid_identifier (vname);
684     }
685 
686   H5Gget_objinfo (group_id, name, 1, &info);
687 
688   if (info.type == H5G_GROUP && ident_valid)
689     {
690 #if defined (HAVE_HDF5_18)
691       subgroup_id = H5Gopen (group_id, name, octave_H5P_DEFAULT);
692 #else
693       subgroup_id = H5Gopen (group_id, name);
694 #endif
695 
696       if (subgroup_id < 0)
697         {
698           retval = subgroup_id;
699           goto done;
700         }
701 
702       if (hdf5_check_attr (subgroup_id, "OCTAVE_NEW_FORMAT"))
703         {
704 #if defined (HAVE_HDF5_18)
705           data_id = H5Dopen (subgroup_id, "type", octave_H5P_DEFAULT);
706 #else
707           data_id = H5Dopen (subgroup_id, "type");
708 #endif
709 
710           if (data_id < 0)
711             {
712               retval = data_id;
713               goto done;
714             }
715 
716           type_id = H5Dget_type (data_id);
717 
718           type_class_id = H5Tget_class (type_id);
719 
720           if (type_class_id != H5T_STRING)
721             goto done;
722 
723           space_id = H5Dget_space (data_id);
724           hsize_t rank = H5Sget_simple_extent_ndims (space_id);
725 
726           if (rank != 0)
727             goto done;
728 
729           int slen = H5Tget_size (type_id);
730           if (slen < 0)
731             goto done;
732 
733           OCTAVE_LOCAL_BUFFER (char, typ, slen);
734 
735           // create datatype for (null-terminated) string to read into:
736           hid_t st_id = H5Tcopy (H5T_C_S1);
737           H5Tset_size (st_id, slen);
738 
739           if (H5Dread (data_id, st_id, octave_H5S_ALL, octave_H5S_ALL,
740                        octave_H5P_DEFAULT, typ) < 0)
741             goto done;
742 
743           H5Tclose (st_id);
744           H5Dclose (data_id);
745 
746           if (std::string (typ, slen-1) == "inline function")
747             {
748               retval = load_inline_fcn (subgroup_id, name, d->tc);
749             }
750           else
751             {
752               d->tc = type_info.lookup_type (std::string (typ, slen-1));
753 
754               try
755                 {
756                   retval = (d->tc.load_hdf5 (subgroup_id, "value") ? 1 : -1);
757                 }
758               catch (const octave::execution_exception& ee)
759                 {
760                   retval = -1;
761                 }
762             }
763 
764           // check for OCTAVE_GLOBAL attribute:
765           d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL");
766 
767           H5Gclose (subgroup_id);
768         }
769       else
770         {
771           // It seems that this block only applies to an old list type
772           // and that we shouldn't need to handle the old inline
773           // function type here.
774 
775           // an HDF5 group is treated as an octave structure by
776           // default (since that preserves name information), and an
777           // octave list otherwise.
778 
779           if (hdf5_check_attr (subgroup_id, "OCTAVE_LIST"))
780             d->tc = type_info.lookup_type ("list");
781           else
782             d->tc = type_info.lookup_type ("struct");
783 
784           // check for OCTAVE_GLOBAL attribute:
785           d->global = hdf5_check_attr (subgroup_id, "OCTAVE_GLOBAL");
786 
787           H5Gclose (subgroup_id);
788 
789           try
790             {
791               retval = (d->tc.load_hdf5 (group_id, name) ? 1 : -1);
792             }
793           catch (const octave::execution_exception& ee)
794             {
795               retval = -1;
796             }
797         }
798 
799     }
800   else if (info.type == H5G_DATASET && ident_valid)
801     {
802       // It seems that this block only applies to an old version of
803       // Octave HDF5 files and that it is probably not important to
804       // handle the old inline function type here.
805 
806       // For backwards compatibility.
807 #if defined (HAVE_HDF5_18)
808       data_id = H5Dopen (group_id, name, octave_H5P_DEFAULT);
809 #else
810       data_id = H5Dopen (group_id, name);
811 #endif
812 
813       if (data_id < 0)
814         {
815           retval = data_id;
816           goto done;
817         }
818 
819       type_id = H5Dget_type (data_id);
820 
821       type_class_id = H5Tget_class (type_id);
822 
823       if (type_class_id == H5T_FLOAT)
824         {
825           space_id = H5Dget_space (data_id);
826 
827           hsize_t rank = H5Sget_simple_extent_ndims (space_id);
828 
829           if (rank == 0)
830             d->tc = type_info.lookup_type ("scalar");
831           else
832             d->tc = type_info.lookup_type ("matrix");
833 
834           H5Sclose (space_id);
835         }
836       else if (type_class_id == H5T_INTEGER)
837         {
838           // What integer type do we really have..
839           std::string int_typ;
840 #if defined (HAVE_H5T_GET_NATIVE_TYPE)
841           // FIXME: test this code and activated with an autoconf
842           // test!! It is also incorrect for 64-bit indexing!!
843 
844           switch (H5Tget_native_type (type_id, H5T_DIR_ASCEND))
845             {
846             case H5T_NATIVE_CHAR:
847               int_typ = "int8 ";
848               break;
849 
850             case H5T_NATIVE_SHORT:
851               int_typ = "int16 ";
852               break;
853 
854             case H5T_NATIVE_INT:
855             case H5T_NATIVE_LONG:
856               int_typ = "int32 ";
857               break;
858 
859             case H5T_NATIVE_LLONG:
860               int_typ = "int64 ";
861               break;
862 
863             case H5T_NATIVE_UCHAR:
864               int_typ = "uint8 ";
865               break;
866 
867             case H5T_NATIVE_USHORT:
868               int_typ = "uint16 ";
869               break;
870 
871             case H5T_NATIVE_UINT:
872             case H5T_NATIVE_ULONG:
873               int_typ = "uint32 ";
874               break;
875 
876             case H5T_NATIVE_ULLONG:
877               int_typ = "uint64 ";
878               break;
879             }
880 #else
881           hid_t int_sign = H5Tget_sign (type_id);
882 
883           if (int_sign == H5T_SGN_ERROR)
884             warning ("load: can't read '%s' (unknown datatype)", name);
885           else
886             {
887               if (int_sign == H5T_SGN_NONE)
888                 int_typ.push_back ('u');
889               int_typ.append ("int");
890 
891               int slen = H5Tget_size (type_id);
892               if (slen < 0)
893                 warning ("load: can't read '%s' (unknown datatype)", name);
894               else
895                 {
896                   switch (slen)
897                     {
898                     case 1:
899                       int_typ.append ("8 ");
900                       break;
901 
902                     case 2:
903                       int_typ.append ("16 ");
904                       break;
905 
906                     case 4:
907                       int_typ.append ("32 ");
908                       break;
909 
910                     case 8:
911                       int_typ.append ("64 ");
912                       break;
913 
914                     default:
915                       warning ("load: can't read '%s' (unknown datatype)",
916                                name);
917                       int_typ = "";
918                       break;
919                     }
920                 }
921             }
922 #endif
923           if (int_typ == "")
924             warning ("load: can't read '%s' (unknown datatype)", name);
925           else
926             {
927               // Matrix or scalar?
928               space_id = H5Dget_space (data_id);
929 
930               hsize_t rank = H5Sget_simple_extent_ndims (space_id);
931 
932               if (rank == 0)
933                 int_typ.append ("scalar");
934               else
935                 int_typ.append ("matrix");
936 
937               d->tc = type_info.lookup_type (int_typ);
938               H5Sclose (space_id);
939             }
940         }
941       else if (type_class_id == H5T_STRING)
942         d->tc = type_info.lookup_type ("string");
943       else if (type_class_id == H5T_COMPOUND)
944         {
945           hid_t complex_type = hdf5_make_complex_type (H5T_NATIVE_DOUBLE);
946           hid_t range_type = hdf5_make_range_type (H5T_NATIVE_DOUBLE);
947 
948           if (hdf5_types_compatible (type_id, complex_type))
949             {
950               // read complex matrix or scalar variable
951               space_id = H5Dget_space (data_id);
952               hsize_t rank = H5Sget_simple_extent_ndims (space_id);
953 
954               if (rank == 0)
955                 d->tc = type_info.lookup_type ("complex scalar");
956               else
957                 d->tc = type_info.lookup_type ("complex matrix");
958 
959               H5Sclose (space_id);
960             }
961           else if (hdf5_types_compatible (type_id, range_type))
962             {
963               // If it's not a complex, check if it's a range
964               d->tc = octave_value_typeinfo::lookup_type ("range");
965             }
966           else // Otherwise, just ignore it with a warning.
967             {
968               warning ("load: can't read '%s' (unknown datatype)", name);
969               retval = 0;  // unknown datatype; skip
970               return retval;
971             }
972 
973           H5Tclose (range_type);
974           H5Tclose (complex_type);
975         }
976       else
977         {
978           warning ("load: can't read '%s' (unknown datatype)", name);
979           retval = 0;  // unknown datatype; skip
980           return retval;
981         }
982 
983       // check for OCTAVE_GLOBAL attribute:
984       d->global = hdf5_check_attr (data_id, "OCTAVE_GLOBAL");
985 
986       try
987         {
988           retval = (d->tc.load_hdf5 (group_id, name) ? 1 : -1);
989         }
990       catch (const octave::execution_exception& ee)
991         {
992           retval = -1;
993         }
994 
995       H5Tclose (type_id);
996       H5Dclose (data_id);
997     }
998 
999   if (! ident_valid)
1000     {
1001       // should we attempt to handle invalid identifiers by converting
1002       // bad characters to '_', say?
1003       warning ("load: skipping invalid identifier '%s' in hdf5 file",
1004                name);
1005     }
1006 
1007 done:
1008   if (retval < 0)
1009     {
1010       // Must be warning.  A call to error aborts and leaves H5Giterate in
1011       // a mangled state that causes segfault on exit (bug #56149).
1012       warning ("load: error while reading hdf5 item '%s'", name);
1013     }
1014 
1015   if (retval > 0)
1016     {
1017       // get documentation string, if any:
1018       int comment_length = H5Gget_comment (group_id, name, 0, nullptr);
1019 
1020       if (comment_length > 1)
1021         {
1022           OCTAVE_LOCAL_BUFFER (char, tdoc, comment_length);
1023           H5Gget_comment (group_id, name, comment_length, tdoc);
1024           d->doc = tdoc;
1025         }
1026       else if (vname != name)
1027         {
1028           // the name was changed; store the original name
1029           // as the documentation string:
1030           d->doc = name;
1031         }
1032 
1033       // copy name (actually, vname):
1034       d->name = vname;
1035     }
1036 
1037   return retval;
1038 }
1039 
1040 #endif
1041 
1042 octave_hdf5_err
hdf5_read_next_data(octave_hdf5_id group_id,const char * name,void * dv)1043 hdf5_read_next_data (octave_hdf5_id group_id, const char *name, void *dv)
1044 {
1045 #if defined (HAVE_HDF5)
1046 
1047   hid_t new_id = check_hdf5_id_value (group_id, "hdf5_read_next_data");
1048 
1049   return hdf5_read_next_data_internal (new_id, name, dv);
1050 
1051 #else
1052   err_disabled_feature ("hdf5_read_next_data", "HDF5");
1053 #endif
1054 }
1055 
1056 octave_hdf5_err
hdf5_h5g_iterate(octave_hdf5_id loc_id,const char * name,int * idx,void * operator_data)1057 hdf5_h5g_iterate (octave_hdf5_id loc_id, const char *name, int *idx,
1058                   void *operator_data)
1059 {
1060 #if defined (HAVE_HDF5)
1061 
1062   hid_t new_id = check_hdf5_id_value (loc_id, "hdf5_h5g_iterate");
1063 
1064   return H5Giterate (new_id, name, idx, hdf5_read_next_data_internal,
1065                      operator_data);
1066 
1067 #else
1068   err_disabled_feature ("hdf5_h5g_iterate", "HDF5");
1069 #endif
1070 }
1071 
1072 // Read the next Octave variable from the stream IS, which must really be an
1073 // hdf5_ifstream.  Return the variable value in tc, its docstring in doc, and
1074 // whether it is global in global.  The return value is the name of the
1075 // variable, or NULL if none were found or there was an error.
1076 std::string
read_hdf5_data(std::istream & is,const std::string &,bool & global,octave_value & tc,std::string & doc,const string_vector & argv,int argv_idx,int argc)1077 read_hdf5_data (std::istream& is, const std::string& /* filename */,
1078                 bool& global, octave_value& tc, std::string& doc,
1079                 const string_vector& argv, int argv_idx, int argc)
1080 {
1081 #if defined (HAVE_HDF5)
1082 
1083   check_hdf5_types ();
1084 
1085   std::string retval;
1086 
1087   doc.clear ();
1088 
1089   hdf5_ifstream& hs = dynamic_cast<hdf5_ifstream&> (is);
1090   hdf5_callback_data d;
1091 
1092   herr_t H5Giterate_retval = -1;
1093 
1094   hsize_t num_obj = 0;
1095 #if defined (HAVE_HDF5_18)
1096   hid_t group_id = H5Gopen (hs.file_id, "/", octave_H5P_DEFAULT);
1097 #else
1098   hid_t group_id = H5Gopen (hs.file_id, "/");
1099 #endif
1100   H5Gget_num_objs (group_id, &num_obj);
1101   H5Gclose (group_id);
1102 
1103   // For large datasets and out-of-core functionality,
1104   // check if only parts of the data is requested
1105   bool load_named_vars = argv_idx < argc;
1106   while (load_named_vars && hs.current_item < static_cast<int> (num_obj))
1107     {
1108       std::vector<char> var_name;
1109       bool found = false;
1110       std::size_t len = 0;
1111 
1112       len = H5Gget_objname_by_idx (hs.file_id, hs.current_item, nullptr, 0);
1113       var_name.resize (len+1);
1114       H5Gget_objname_by_idx (hs.file_id, hs.current_item, &var_name[0], len+1);
1115 
1116       for (int i = argv_idx; i < argc; i++)
1117         {
1118           glob_match pattern (argv[i]);
1119           if (pattern.match (std::string (&var_name[0])))
1120             {
1121               found = true;
1122               break;
1123             }
1124         }
1125 
1126       if (found)
1127         break;
1128 
1129       hs.current_item++;
1130     }
1131 
1132   if (hs.current_item < static_cast<int> (num_obj))
1133     H5Giterate_retval = H5Giterate (hs.file_id, "/", &hs.current_item,
1134                                     hdf5_read_next_data_internal, &d);
1135 
1136   if (H5Giterate_retval > 0)
1137     {
1138       global = d.global;
1139       tc = d.tc;
1140       doc = d.doc;
1141     }
1142   else
1143     {
1144       // An error occurred (H5Giterate_retval < 0),
1145       // or there are no more datasets (H5Giterate_retval == 0).
1146       // hdf5_read_next_data_internal has already printed a warning msg.
1147     }
1148 
1149   if (! d.name.empty ())
1150     retval = d.name;
1151 
1152   return retval;
1153 
1154 #else
1155   err_disabled_feature ("read_hdf5_data", "HDF5");
1156 #endif
1157 }
1158 
1159 // Add an attribute named attr_name to loc_id (a simple scalar
1160 // attribute with value 1).  Return value is >= 0 on success.
1161 octave_hdf5_err
hdf5_add_attr(octave_hdf5_id loc_id,const char * attr_name)1162 hdf5_add_attr (octave_hdf5_id loc_id, const char *attr_name)
1163 {
1164 #if defined (HAVE_HDF5)
1165 
1166   herr_t retval = 0;
1167 
1168   hid_t as_id = H5Screate (H5S_SCALAR);
1169 
1170   if (as_id >= 0)
1171     {
1172 #if defined (HAVE_HDF5_18)
1173       hid_t a_id = H5Acreate (loc_id, attr_name, H5T_NATIVE_UCHAR,
1174                               as_id, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1175 #else
1176       hid_t a_id = H5Acreate (loc_id, attr_name,
1177                               H5T_NATIVE_UCHAR, as_id, octave_H5P_DEFAULT);
1178 #endif
1179       if (a_id >= 0)
1180         {
1181           unsigned char attr_val = 1;
1182 
1183           retval = H5Awrite (a_id, H5T_NATIVE_UCHAR, &attr_val);
1184 
1185           H5Aclose (a_id);
1186         }
1187       else
1188         retval = a_id;
1189 
1190       H5Sclose (as_id);
1191     }
1192   else
1193     retval = as_id;
1194 
1195   return retval;
1196 
1197 #else
1198   err_disabled_feature ("hdf5_add_attr", "HDF5");
1199 #endif
1200 }
1201 
1202 octave_hdf5_err
hdf5_add_scalar_attr(octave_hdf5_id loc_id,octave_hdf5_id type_id,const char * attr_name,void * buf)1203 hdf5_add_scalar_attr (octave_hdf5_id loc_id, octave_hdf5_id type_id,
1204                       const char *attr_name, void *buf)
1205 {
1206 #if defined (HAVE_HDF5)
1207 
1208   herr_t retval = 0;
1209 
1210   hid_t as_id = H5Screate (H5S_SCALAR);
1211 
1212   if (as_id >= 0)
1213     {
1214 #if defined (HAVE_HDF5_18)
1215       hid_t a_id = H5Acreate (loc_id, attr_name, type_id,
1216                               as_id, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1217 #else
1218       hid_t a_id = H5Acreate (loc_id, attr_name,
1219                               type_id, as_id, octave_H5P_DEFAULT);
1220 #endif
1221       if (a_id >= 0)
1222         {
1223           retval = H5Awrite (a_id, type_id, buf);
1224 
1225           H5Aclose (a_id);
1226         }
1227       else
1228         retval = a_id;
1229 
1230       H5Sclose (as_id);
1231     }
1232   else
1233     retval = as_id;
1234 
1235   return retval;
1236 
1237 #else
1238   err_disabled_feature ("hdf5_add_scalar_attr", "HDF5");
1239 #endif
1240 }
1241 
1242 // Save an empty matrix, if needed.  Returns
1243 //    > 0  Saved empty matrix
1244 //    = 0  Not an empty matrix; did nothing
1245 //    < 0  Error condition
1246 int
save_hdf5_empty(octave_hdf5_id loc_id,const char * name,const dim_vector & d)1247 save_hdf5_empty (octave_hdf5_id loc_id, const char *name, const dim_vector& d)
1248 {
1249 #if defined (HAVE_HDF5)
1250 
1251   hsize_t sz = d.length ();
1252   OCTAVE_LOCAL_BUFFER (octave_idx_type, dims, sz);
1253   bool empty = false;
1254   hid_t space_hid = -1;
1255   hid_t data_hid = -1;
1256   int retval;
1257   for (hsize_t i = 0; i < sz; i++)
1258     {
1259       dims[i] = d(i);
1260       if (dims[i] < 1)
1261         empty = true;
1262     }
1263 
1264   if (! empty)
1265     return 0;
1266 
1267   space_hid = H5Screate_simple (1, &sz, nullptr);
1268   if (space_hid < 0) return space_hid;
1269 #if defined (HAVE_HDF5_18)
1270   data_hid = H5Dcreate (loc_id, name, H5T_NATIVE_IDX, space_hid,
1271                         octave_H5P_DEFAULT, octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1272 #else
1273   data_hid = H5Dcreate (loc_id, name, H5T_NATIVE_IDX, space_hid,
1274                         octave_H5P_DEFAULT);
1275 #endif
1276   if (data_hid < 0)
1277     {
1278       H5Sclose (space_hid);
1279       return data_hid;
1280     }
1281 
1282   retval = H5Dwrite (data_hid, H5T_NATIVE_IDX, octave_H5S_ALL, octave_H5S_ALL,
1283                      octave_H5P_DEFAULT, dims) >= 0;
1284 
1285   H5Dclose (data_hid);
1286   H5Sclose (space_hid);
1287 
1288   if (retval)
1289     retval = hdf5_add_attr (loc_id, "OCTAVE_EMPTY_MATRIX");
1290 
1291   return (retval == 0 ? 1 : retval);
1292 
1293 #else
1294   err_disabled_feature ("save_hdf5_empty", "HDF5");
1295 #endif
1296 }
1297 
1298 // Load an empty matrix, if needed.  Returns
1299 //    > 0  loaded empty matrix, dimensions returned
1300 //    = 0  Not an empty matrix; did nothing
1301 //    < 0  Error condition
1302 int
load_hdf5_empty(octave_hdf5_id loc_id,const char * name,dim_vector & d)1303 load_hdf5_empty (octave_hdf5_id loc_id, const char *name, dim_vector& d)
1304 {
1305 #if defined (HAVE_HDF5)
1306 
1307   if (! hdf5_check_attr (loc_id, "OCTAVE_EMPTY_MATRIX"))
1308     return 0;
1309 
1310   hsize_t hdims, maxdims;
1311 #if defined (HAVE_HDF5_18)
1312   hid_t data_hid = H5Dopen (loc_id, name, octave_H5P_DEFAULT);
1313 #else
1314   hid_t data_hid = H5Dopen (loc_id, name);
1315 #endif
1316   hid_t space_id = H5Dget_space (data_hid);
1317   H5Sget_simple_extent_dims (space_id, &hdims, &maxdims);
1318   int retval;
1319 
1320   OCTAVE_LOCAL_BUFFER (octave_idx_type, dims, hdims);
1321 
1322   retval = H5Dread (data_hid, H5T_NATIVE_IDX, octave_H5S_ALL, octave_H5S_ALL,
1323                     octave_H5P_DEFAULT, dims);
1324   if (retval >= 0)
1325     {
1326       d.resize (hdims);
1327       for (hsize_t i = 0; i < hdims; i++)
1328         d(i) = dims[i];
1329     }
1330 
1331   H5Sclose (space_id);
1332   H5Dclose (data_hid);
1333 
1334   return (retval == 0 ? hdims : retval);
1335 
1336 #else
1337   err_disabled_feature ("load_hdf5_empty", "HDF5");
1338 #endif
1339 }
1340 
1341 // save_type_to_hdf5 is not currently used, since hdf5 doesn't yet support
1342 // automatic float<->integer conversions:
1343 
1344 // return the HDF5 type id corresponding to the Octave save_type
1345 
1346 octave_hdf5_id
save_type_to_hdf5(save_type st)1347 save_type_to_hdf5 (save_type st)
1348 {
1349 #if defined (HAVE_HDF5)
1350 #  if defined (HAVE_HDF5_INT2FLOAT_CONVERSIONS)
1351 
1352   switch (st)
1353     {
1354     case LS_U_CHAR:
1355       return H5T_NATIVE_UCHAR;
1356 
1357     case LS_U_SHORT:
1358       return H5T_NATIVE_USHORT;
1359 
1360     case LS_U_INT:
1361       return H5T_NATIVE_UINT;
1362 
1363     case LS_CHAR:
1364       return H5T_NATIVE_CHAR;
1365 
1366     case LS_SHORT:
1367       return H5T_NATIVE_SHORT;
1368 
1369     case LS_INT:
1370       return H5T_NATIVE_INT;
1371 
1372     case LS_FLOAT:
1373       return H5T_NATIVE_FLOAT;
1374 
1375     case LS_DOUBLE:
1376     default:
1377       return H5T_NATIVE_DOUBLE;
1378     }
1379 
1380 #  else
1381 
1382   octave_unused_parameter (st);
1383 
1384   return -1;
1385 
1386 #  endif
1387 
1388 #else
1389 
1390   octave_unused_parameter (st);
1391 
1392   err_disabled_feature ("save_type_to_hdf5", "HDF5");
1393 
1394 #endif
1395 }
1396 
1397 // Add the data from TC to the HDF5 location loc_id, which could
1398 // be either a file or a group within a file.  Return true if
1399 // successful.  This function calls itself recursively for lists
1400 // (stored as HDF5 groups).
1401 
1402 bool
add_hdf5_data(octave_hdf5_id loc_id,const octave_value & tc,const std::string & name,const std::string & doc,bool mark_global,bool save_as_floats)1403 add_hdf5_data (octave_hdf5_id loc_id, const octave_value& tc,
1404                const std::string& name, const std::string& doc,
1405                bool mark_global, bool save_as_floats)
1406 {
1407 #if defined (HAVE_HDF5)
1408 
1409   hsize_t dims[3];
1410   hid_t type_id, space_id, data_id, data_type_id;
1411   type_id = space_id = data_id = data_type_id = -1;
1412 
1413   bool retval = false;
1414   octave_value val = tc;
1415   // FIXME: diagonal & permutation matrices currently don't know how to save
1416   // themselves, so we convert them first to normal matrices using A = A(:,:).
1417   // This is a temporary hack.
1418   if (val.is_diag_matrix () || val.is_perm_matrix ()
1419       || val.type_id () == octave_lazy_index::static_type_id ())
1420     val = val.full_value ();
1421 
1422   std::string t = val.type_name ();
1423 #if defined (HAVE_HDF5_18)
1424   data_id = H5Gcreate (loc_id, name.c_str (), octave_H5P_DEFAULT,
1425                        octave_H5P_DEFAULT, octave_H5P_DEFAULT);
1426 #else
1427   data_id = H5Gcreate (loc_id, name.c_str (), 0);
1428 #endif
1429   if (data_id < 0)
1430     goto error_cleanup;
1431 
1432   // attach the type of the variable
1433   type_id = H5Tcopy (H5T_C_S1); H5Tset_size (type_id, t.length () + 1);
1434   if (type_id < 0)
1435     goto error_cleanup;
1436 
1437   dims[0] = 0;
1438   space_id = H5Screate_simple (0, dims, nullptr);
1439   if (space_id < 0)
1440     goto error_cleanup;
1441 #if defined (HAVE_HDF5_18)
1442   data_type_id = H5Dcreate (data_id, "type",  type_id, space_id,
1443                             octave_H5P_DEFAULT, octave_H5P_DEFAULT,
1444                             octave_H5P_DEFAULT);
1445 #else
1446   data_type_id = H5Dcreate (data_id, "type",  type_id, space_id,
1447                             octave_H5P_DEFAULT);
1448 #endif
1449   if (data_type_id < 0
1450       || H5Dwrite (data_type_id, type_id, octave_H5S_ALL, octave_H5S_ALL,
1451                    octave_H5P_DEFAULT, t.c_str ()) < 0)
1452     goto error_cleanup;
1453 
1454   // Now call the real function to save the variable
1455   retval = val.save_hdf5 (data_id, "value", save_as_floats);
1456 
1457   // attach doc string as comment:
1458   if (retval && doc.length () > 0
1459       && H5Gset_comment (loc_id, name.c_str (), doc.c_str ()) < 0)
1460     retval = false;
1461 
1462   // if it's global, add an attribute "OCTAVE_GLOBAL" with value 1
1463   if (retval && mark_global)
1464     retval = hdf5_add_attr (data_id, "OCTAVE_GLOBAL") >= 0;
1465 
1466   // We are saving in the new variable format, so mark it
1467   if (retval)
1468     retval = hdf5_add_attr (data_id, "OCTAVE_NEW_FORMAT") >= 0;
1469 
1470 error_cleanup:
1471 
1472   if (data_type_id >= 0)
1473     H5Dclose (data_type_id);
1474 
1475   if (type_id >= 0)
1476     H5Tclose (type_id);
1477 
1478   if (space_id >= 0)
1479     H5Sclose (space_id);
1480 
1481   if (data_id >= 0)
1482     H5Gclose (data_id);
1483 
1484   if (! retval)
1485     error ("save: error while writing '%s' to hdf5 file", name.c_str ());
1486 
1487   return retval;
1488 
1489 #else
1490   err_disabled_feature ("add_hdf5_data", "HDF5");
1491 #endif
1492 }
1493 
1494 // Write data from TC in HDF5 (binary) format to the stream OS,
1495 // which must be an hdf5_ofstream, returning true on success.
1496 
1497 bool
save_hdf5_data(std::ostream & os,const octave_value & tc,const std::string & name,const std::string & doc,bool mark_global,bool save_as_floats)1498 save_hdf5_data (std::ostream& os, const octave_value& tc,
1499                 const std::string& name, const std::string& doc,
1500                 bool mark_global, bool save_as_floats)
1501 {
1502 #if defined (HAVE_HDF5)
1503 
1504   check_hdf5_types ();
1505 
1506   hdf5_ofstream& hs = dynamic_cast<hdf5_ofstream&> (os);
1507 
1508   return add_hdf5_data (hs.file_id, tc, name, doc,
1509                         mark_global, save_as_floats);
1510 
1511 #else
1512   err_disabled_feature ("save_hdf5_data", "HDF5");
1513 #endif
1514 }
1515 
1516 #endif
1517