GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
gzip.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2016-2018 CarnĂ« Draug
4 
5 This file is part of Octave.
6 
7 Octave is free software: you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 //! @file gzip.cc
24 //! Octave interface to the compression and uncompression libraries.
25 //!
26 //! This was originally implemented as an m file which directly called
27 //! bzip2 and gzip applications. This may look simpler but causes some
28 //! issues (see bug #43431) because we have no control over the output
29 //! file:
30 //!
31 //! - created file is always in the same directory as the original file;
32 //! - automatically skip files that already have gz/bz2/etc extension;
33 //! - some older versions lack the --keep option.
34 //!
35 //! In addition, because system() does not have a method that allows
36 //! passing a list of arguments, there is the issue of having to escape
37 //! filenames.
38 //!
39 //! A solution is to pipe file contents into the applications instead of
40 //! filenames. However, that solution causes:
41 //!
42 //! # missing file header with original file information;
43 //! # implementing ourselves the recursive transversion of directories;
44 //! # do the above in a m file which will be slow;
45 //! # popen2 is frail on windows.
46 
47 #if defined (HAVE_CONFIG_H)
48 # include "config.h"
49 #endif
50 
51 #include <cstdio>
52 #include <cstring>
53 
54 #include <functional>
55 #include <list>
56 #include <stdexcept>
57 #include <string>
58 
59 #include "Array.h"
60 #include "dir-ops.h"
61 #include "file-ops.h"
62 #include "file-stat.h"
63 #include "glob-match.h"
64 #include "oct-env.h"
65 #include "str-vec.h"
66 
67 #include "Cell.h"
68 #include "defun-dld.h"
69 #include "defun-int.h"
70 #include "errwarn.h"
71 #include "ov.h"
72 #include "ovl.h"
73 
74 #if defined (HAVE_BZLIB_H)
75 # include <bzlib.h>
76 #endif
77 
78 #if defined (HAVE_ZLIB_H)
79 # include <zlib.h>
80 #endif
81 
82 namespace octave
83 {
84  //! RIIA wrapper for std::FILE*.
85  //!
86  //! If error handling is available for failing to close the file, use
87  //! the close method which throws.
88  //!
89  //! If the file has been closed, fp is set to nullptr. Remember that
90  //! behavior is undefined if the value of the pointer stream is used
91  //! after fclose.
92 
93  class CFile
94  {
95  public:
96 
97  CFile (void) = delete;
98 
99  CFile (const std::string& path, const std::string& mode)
100  : m_fp (std::fopen (path.c_str (), mode.c_str ()))
101  {
102  if (! m_fp)
103  throw std::runtime_error ("unable to open file");
104  }
105 
106  CFile (const CFile&) = delete;
107 
108  CFile& operator = (const CFile&) = delete;
109 
110  ~CFile (void)
111  {
112  if (m_fp)
113  std::fclose (m_fp);
114  }
115 
116  void close (void)
117  {
118  if (std::fclose (m_fp))
119  throw std::runtime_error ("unable to close file");
120 
121  m_fp = nullptr;
122  }
123 
124  std::FILE *m_fp;
125  };
126 
127 #if defined (HAVE_BZ2)
128 
129  class bz2
130  {
131  public:
132 
133  static const constexpr char *extension = ".bz2";
134 
135  static void zip (const std::string& source_path,
136  const std::string& dest_path)
137  {
138  bz2::zipper z (source_path, dest_path);
139  z.deflate ();
140  z.close ();
141  }
142 
143  private:
144 
145  class zipper
146  {
147  public:
148 
149  zipper (void) = delete;
150 
151  zipper (const std::string& source_path, const std::string& dest_path)
152  : m_status (BZ_OK), m_source (source_path, "rb"),
153  m_dest (dest_path, "wb"),
154  m_bz (BZ2_bzWriteOpen (&m_status, m_dest.m_fp, 9, 0, 30))
155  {
156  if (m_status != BZ_OK)
157  throw std::runtime_error ("failed to open bzip2 stream");
158  }
159 
160  zipper (const zipper&) = delete;
161 
162  zipper& operator = (const zipper&) = delete;
163 
164  ~zipper (void)
165  {
166  if (m_bz != nullptr)
167  BZ2_bzWriteClose (&m_status, m_bz, 1, nullptr, nullptr);
168  }
169 
170  void deflate (void)
171  {
172  const std::size_t buf_len = 8192;
173  char buf[buf_len];
174  std::size_t n_read;
175  while ((n_read = std::fread (buf, sizeof (buf[0]), buf_len, m_source.m_fp)) != 0)
176  {
177  if (std::ferror (m_source.m_fp))
178  throw std::runtime_error ("failed to read from source file");
179  BZ2_bzWrite (&m_status, m_bz, buf, n_read);
180  if (m_status == BZ_IO_ERROR)
181  throw std::runtime_error ("failed to write or compress");
182  }
183  if (std::ferror (m_source.m_fp))
184  throw std::runtime_error ("failed to read from source file");
185  }
186 
187  void close (void)
188  {
189  int abandon = (m_status == BZ_IO_ERROR) ? 1 : 0;
190  BZ2_bzWriteClose (&m_status, m_bz, abandon, nullptr, nullptr);
191  if (m_status != BZ_OK)
192  throw std::runtime_error ("failed to close bzip2 stream");
193  m_bz = nullptr;
194 
195  // We have no error handling for failing to close source, let
196  // the destructor close it.
197  m_dest.close ();
198  }
199 
200  private:
201 
202  int m_status;
203  CFile m_source;
204  CFile m_dest;
205  BZFILE *m_bz;
206  };
207  };
208 
209 #endif
210 
211  // Note about zlib and gzip
212  //
213  // gzip is a format for compressed single files. zlib is a format
214  // designed for in-memory and communication channel applications.
215  // gzip uses the same format internally for the compressed data but
216  // has different headers and trailers.
217  //
218  // zlib is also a library but gzip is not. Very old versions of zlib do
219  // not include functions to create useful gzip headers and trailers:
220  //
221  // Note that you cannot specify special gzip header contents (e.g.
222  // a file name or modification date), nor will inflate tell you what
223  // was in the gzip header. If you need to customize the header or
224  // see what's in it, you can use the raw deflate and inflate
225  // operations and the crc32() function and roll your own gzip
226  // encoding and decoding. Read the gzip RFC 1952 for details of the
227  // header and trailer format.
228  // zlib FAQ
229  //
230  // Recent versions (on which we are already dependent) have deflateInit2()
231  // to do it. We still need to get the right metadata for the header
232  // ourselves though.
233  //
234  // The header is defined in RFC #1952
235  // GZIP file format specification version 4.3
236 
237 
238 #if defined (HAVE_Z)
239 
240  class gz
241  {
242  public:
243 
244  static const constexpr char *extension = ".gz";
245 
246  static void zip (const std::string& source_path,
247  const std::string& dest_path)
248  {
249  gz::zipper z (source_path, dest_path);
250  z.deflate ();
251  z.close ();
252  }
253 
254  private:
255 
256  // Util class to get a non-const char*
257  class uchar_array
258  {
259  public:
260 
261  // Bytef is a typedef for unsigned char
262  unsigned char *p;
263 
264  uchar_array (void) = delete;
265 
266  uchar_array (const std::string& str)
267  {
268  p = new Bytef[str.length () + 1];
269  std::strcpy (reinterpret_cast<char *> (p), str.c_str ());
270  }
271 
272  uchar_array (const uchar_array&) = delete;
273 
274  uchar_array& operator = (const uchar_array&) = delete;
275 
276  ~uchar_array (void) { delete[] p; }
277  };
278 
279  class gzip_header : public gz_header
280  {
281  public:
282 
283  gzip_header (void) = delete;
284 
285  gzip_header (const std::string& source_path)
286  : m_basename (sys::env::base_pathname (source_path))
287  {
288  const sys::file_stat source_stat (source_path);
289  if (! source_stat)
290  throw std::runtime_error ("unable to stat source file");
291 
292  // time_t may be a signed int in which case it will be a
293  // positive number so it is safe to uLong. Or is it? Can
294  // unix_time really never be negative?
295  time = uLong (source_stat.mtime ().unix_time ());
296 
297  // If FNAME is set, an original file name is present,
298  // terminated by a zero byte. The name must consist of ISO
299  // 8859-1 (LATIN-1) characters; on operating systems using
300  // EBCDIC or any other character set for file names, the name
301  // must be translated to the ISO LATIN-1 character set. This
302  // is the original name of the file being compressed, with any
303  // directory components removed, and, if the file being
304  // compressed is on a file system with case insensitive names,
305  // forced to lower case.
306  name = m_basename.p;
307 
308  // If we don't set it to Z_NULL, then it will set FCOMMENT (4th bit)
309  // on the FLG byte, and then write {0, 3} comment.
310  comment = Z_NULL;
311 
312  // Seems to already be the default but we are not taking chances.
313  extra = Z_NULL;
314 
315  // We do not want a CRC for the header. That would be only 2 more
316  // bytes, and maybe it would be a good thing but we want to generate
317  // gz files similar to the default gzip application.
318  hcrc = 0;
319 
320  // OS (Operating System):
321  // 0 - FAT filesystem (MS-DOS, OS/2, NT/Win32)
322  // 1 - Amiga
323  // 2 - VMS (or OpenVMS)
324  // 3 - Unix
325  // 4 - VM/CMS
326  // 5 - Atari TOS
327  // 6 - HPFS filesystem (OS/2, NT)
328  // 7 - Macintosh
329  // 8 - Z-System
330  // 9 - CP/M
331  // 10 - TOPS-20
332  // 11 - NTFS filesystem (NT)
333  // 12 - QDOS
334  // 13 - Acorn RISCOS
335  // 255 - unknown
336  //
337  // The list is problematic because it mixes OS and filesystem. It
338  // also does not specify whether filesystem relates to source or
339  // destination file.
340 
341 #if defined (__WIN32__)
342  // Or should it be 11?
343  os = 0;
344 #elif defined (__APPLE__)
345  os = 7;
346 #else
347  // Unix by default?
348  os = 3;
349 #endif
350  }
351 
352  gzip_header (const gzip_header&) = delete;
353 
354  gzip_header& operator = (const gzip_header&) = delete;
355 
356  ~gzip_header (void) = default;
357 
358  private:
359 
360  // This must be kept for gz_header.name
361  uchar_array m_basename;
362  };
363 
364  class zipper
365  {
366  public:
367 
368  zipper (void) = delete;
369 
370  zipper (const std::string& source_path, const std::string& dest_path)
371  : m_source (source_path, "rb"), m_dest (dest_path, "wb"),
372  m_header (source_path), m_strm (new z_stream)
373  {
374  m_strm->zalloc = Z_NULL;
375  m_strm->zfree = Z_NULL;
376  m_strm->opaque = Z_NULL;
377  }
378 
379  zipper (const zipper&) = delete;
380 
381  zipper& operator = (const zipper&) = delete;
382 
383  ~zipper (void)
384  {
385  if (m_strm)
386  deflateEnd (m_strm);
387  delete m_strm;
388  }
389 
390  void deflate (void)
391  {
392  // int deflateInit2 (z_streamp m_strm,
393  // int level, // compression level (default is 8)
394  // int method,
395  // int windowBits, // 15 (default) + 16 (gzip format)
396  // int memLevel, // memory usage (default is 8)
397  // int strategy);
398  int status = deflateInit2 (m_strm, 8, Z_DEFLATED, 31, 8,
399  Z_DEFAULT_STRATEGY);
400  if (status != Z_OK)
401  throw std::runtime_error ("failed to open zlib stream");
402 
403  deflateSetHeader (m_strm, &m_header);
404 
405  const std::size_t buf_len = 8192;
406  unsigned char buf_in[buf_len];
407  unsigned char buf_out[buf_len];
408 
409  int flush;
410 
411  do
412  {
413  m_strm->avail_in = std::fread (buf_in, sizeof (buf_in[0]),
414  buf_len, m_source.m_fp);
415 
416  if (std::ferror (m_source.m_fp))
417  throw std::runtime_error ("failed to read source file");
418 
419  m_strm->next_in = buf_in;
420  flush = (std::feof (m_source.m_fp) ? Z_FINISH : Z_NO_FLUSH);
421 
422  // If deflate returns Z_OK and with zero avail_out, it must be
423  // called again after making room in the output buffer because
424  // there might be more output pending.
425  do
426  {
427  m_strm->avail_out = buf_len;
428  m_strm->next_out = buf_out;
429  status = ::deflate (m_strm, flush);
430  if (status == Z_STREAM_ERROR)
431  throw std::runtime_error ("failed to deflate");
432 
433  std::fwrite (buf_out, sizeof (buf_out[0]),
434  buf_len - m_strm->avail_out, m_dest.m_fp);
435  if (std::ferror (m_dest.m_fp))
436  throw std::runtime_error ("failed to write file");
437  }
438  while (m_strm->avail_out == 0);
439 
440  if (m_strm->avail_in != 0)
441  throw std::runtime_error ("failed to write file");
442 
443  } while (flush != Z_FINISH);
444 
445  if (status != Z_STREAM_END)
446  throw std::runtime_error ("failed to write file");
447  }
448 
449  void close (void)
450  {
451  if (deflateEnd (m_strm) != Z_OK)
452  throw std::runtime_error ("failed to close zlib stream");
453  m_strm = nullptr;
454 
455  // We have no error handling for failing to close source, let
456  // the destructor close it.
457  m_dest.close ();
458  }
459 
460  private:
461 
462  CFile m_source;
463  CFile m_dest;
464  gzip_header m_header;
465  z_stream *m_strm;
466  };
467  };
468 
469 #endif
470 
471 
472  template<typename X>
474  xzip (const Array<std::string>& source_patterns,
475  const std::function<std::string(const std::string&)>& mk_dest_path)
476  {
477  std::list<std::string> dest_paths;
478 
479  std::function<void(const std::string&)> walk;
480  walk = [&walk, &mk_dest_path, &dest_paths] (const std::string& path) -> void
481  {
482  const sys::file_stat fs (path);
483  // is_dir and is_reg will return false if failed to stat.
484  if (fs.is_dir ())
485  {
486  sys::dir_entry dir (path);
487  if (dir)
488  {
489  // Collect the whole list of filenames first, before recursion
490  // to avoid issues with infinite loop if the action generates
491  // files in the same directory (highly likely).
492  string_vector dirlist = dir.read ();
493  for (octave_idx_type i = 0; i < dirlist.numel (); i++)
494  if (dirlist(i) != "." && dirlist(i) != "..")
495  walk (sys::file_ops::concat (path, dirlist(i)));
496  }
497  // Note that we skip any problem with directories.
498  }
499  else if (fs.is_reg ())
500  {
501  const std::string dest_path = mk_dest_path (path);
502  try
503  {
504  X::zip (path, dest_path);
505  }
506  catch (...)
507  {
508  // Error "handling" is not including filename on the output list.
509  // Also remove created file which maybe was not even created
510  // in the first place. Note that it is possible for the file
511  // to exist in the first place and for X::zip to not have
512  // clobber it yet but we remove it anyway by design.
513  sys::unlink (dest_path);
514  return;
515  }
516  dest_paths.push_front (dest_path);
517  }
518  // Skip all other file types and errors.
519  return;
520  };
521 
522  for (octave_idx_type i = 0; i < source_patterns.numel (); i++)
523  {
524  const glob_match pattern (sys::file_ops::tilde_expand (source_patterns(i)));
525  const string_vector filepaths = pattern.glob ();
526  for (octave_idx_type j = 0; j < filepaths.numel (); j++)
527  walk (filepaths(j));
528  }
529  return string_vector (dest_paths);
530  }
531 
532 
533  template<typename X>
535  xzip (const Array<std::string>& source_patterns)
536  {
537  const std::string ext = X::extension;
538  const std::function<std::string(const std::string&)> mk_dest_path
539  = [&ext] (const std::string& source_path) -> std::string
540  {
541  return source_path + ext;
542  };
543  return xzip<X> (source_patterns, mk_dest_path);
544  }
545 
546  template<typename X>
548  xzip (const Array<std::string>& source_patterns, const std::string& out_dir)
549  {
550  const std::string ext = X::extension;
551  const std::function<std::string(const std::string&)> mk_dest_path
552  = [&out_dir, &ext] (const std::string& source_path) -> std::string
553  {
554  const std::string basename = sys::env::base_pathname (source_path);
555  return sys::file_ops::concat (out_dir, basename + ext);
556  };
557 
558  // We don't care if mkdir fails. Maybe it failed because it already
559  // exists, or maybe it can't bre created. If the first, then there's
560  // nothing to do, if the later, then it will be handled later. Any
561  // is to be handled by not listing files in the output.
562  sys::mkdir (out_dir, 0777);
563  return xzip<X> (source_patterns, mk_dest_path);
564  }
565 
566  template<typename X>
567  static octave_value_list
568  xzip (const std::string& func_name, const octave_value_list& args)
569  {
570  const octave_idx_type nargin = args.length ();
572  print_usage ();
573 
574  const Array<std::string> source_patterns
575  = args(0).xcellstr_value ("%s: FILES must be a character array or cellstr",
576  func_name.c_str ());
577  if (nargin == 1)
578  return octave_value (Cell (xzip<X> (source_patterns)));
579  else // nargin == 2
580  {
581  const std::string out_dir = args(1).string_value ();
582  return octave_value (Cell (xzip<X> (source_patterns, out_dir)));
583  }
584  }
585 }
586 
587 DEFUN_DLD (gzip, args, ,
588  doc: /* -*- texinfo -*-
589 @deftypefn {} {@var{filelist} =} gzip (@var{files})
590 @deftypefnx {} {@var{filelist} =} gzip (@var{files}, @var{dir})
591 Compress the list of files and directories specified in @var{files}.
592 
593 @var{files} is a character array or cell array of strings. Shell wildcards
594 in the filename such as @samp{*} or @samp{?} are accepted and expanded.
595 Each file is compressed separately and a new file with a @file{".gz"}
596 extension is created. The original files are not modified, but existing
597 compressed files will be silently overwritten. If a directory is
598 specified then @code{gzip} recursively compresses all files in the
599 directory.
600 
601 If @var{dir} is defined the compressed files are placed in this directory,
602 rather than the original directory where the uncompressed file resides.
603 Note that this does not replicate a directory tree in @var{dir} which may
604 lead to files overwriting each other if there are multiple files with the
605 same name.
606 
607 If @var{dir} does not exist it is created.
608 
609 The optional output @var{filelist} is a list of the compressed files.
610 @seealso{gunzip, unpack, bzip2, zip, tar}
611 @end deftypefn */)
612 {
613 #if defined (HAVE_Z)
614 
615  return octave::xzip<octave::gz> ("gzip", args);
616 
617 #else
618 
619  octave_unused_parameter (args);
620 
621  err_disabled_feature ("gzip", "gzip");
622 
623 #endif
624 }
625 
626 /*
627 %!error gzip ()
628 %!error gzip ("1", "2", "3")
629 %!error <FILES must be a character array or cellstr|was unavailable or disabled> gzip (1)
630 */
631 
632 DEFUN_DLD (bzip2, args, ,
633  doc: /* -*- texinfo -*-
634 @deftypefn {} {@var{filelist} =} bzip2 (@var{files})
635 @deftypefnx {} {@var{filelist} =} bzip2 (@var{files}, @var{dir})
636 Compress the list of files specified in @var{files}.
637 
638 @var{files} is a character array or cell array of strings. Shell wildcards
639 in the filename such as @samp{*} or @samp{?} are accepted and expanded.
640 Each file is compressed separately and a new file with a @file{".bz2"}
641 extension is created. The original files are not modified, but existing
642 compressed files will be silently overwritten.
643 
644 If @var{dir} is defined the compressed files are placed in this directory,
645 rather than the original directory where the uncompressed file resides.
646 Note that this does not replicate a directory tree in @var{dir} which may
647 lead to files overwriting each other if there are multiple files with the
648 same name.
649 
650 If @var{dir} does not exist it is created.
651 
652 The optional output @var{filelist} is a list of the compressed files.
653 @seealso{bunzip2, unpack, gzip, zip, tar}
654 @end deftypefn */)
655 {
656 #if defined (HAVE_BZ2)
657 
658  return octave::xzip<octave::bz2> ("bzip2", args);
659 
660 #else
661 
662  octave_unused_parameter (args);
663 
664  err_disabled_feature ("bzip2", "bzip2");
665 
666 #endif
667 }
668 
669 // Tests for both gzip/bzip2 and gunzip/bunzip2
670 /*
671 
672 ## Takes a single argument, a function handle for the test. This other
673 ## function must accept two arguments, a directory for the tests, and
674 ## a cell array with zip function, unzip function, and file extension.
675 
676 %!function run_test_function (test_function)
677 %! enabled_zippers = struct ("zip", {}, "unzip", {}, "ext", {});
678 %! if (__octave_config_info__ ().build_features.BZ2)
679 %! enabled_zippers(end+1).zip = @bzip2;
680 %! enabled_zippers(end).unzip = @bunzip2;
681 %! enabled_zippers(end).ext = ".bz2";
682 %! endif
683 %! if (__octave_config_info__ ().build_features.Z)
684 %! enabled_zippers(end+1).zip = @gzip;
685 %! enabled_zippers(end).unzip = @gunzip;
686 %! enabled_zippers(end).ext = ".gz";
687 %! endif
688 %!
689 %! for z = enabled_zippers
690 %! test_dir = tempname ();
691 %! if (! mkdir (test_dir))
692 %! error ("unable to create directory for tests");
693 %! endif
694 %! unwind_protect
695 %! test_function (test_dir, z)
696 %! unwind_protect_cleanup
697 %! confirm_recursive_rmdir (false, "local");
698 %! rmdir (test_dir, "s");
699 %! end_unwind_protect
700 %! endfor
701 %!endfunction
702 
703 %!function create_file (fpath, data)
704 %! fid = fopen (fpath, "wb");
705 %! if (fid < 0)
706 %! error ("unable to open file for writing");
707 %! endif
708 %! if (fwrite (fid, data, class (data)) != numel (data))
709 %! error ("unable to write to file");
710 %! endif
711 %! if (fflush (fid) || fclose (fid))
712 %! error ("unable to flush or close file");
713 %! endif
714 %!endfunction
715 
716 %!function unlink_or_error (filepath)
717 %! [err, msg] = unlink (filepath);
718 %! if (err)
719 %! error ("unable to remove file required for the test");
720 %! endif
721 %!endfunction
722 
723 ## Test with large files because of varied buffer size
724 %!function test_large_file (test_dir, z)
725 %! test_file = tempname (test_dir);
726 %! create_file (test_file, rand (500000, 1));
727 %! md5 = hash ("md5", fileread (test_file));
728 %!
729 %! z_file = [test_file z.ext];
730 %! z_filelist = z.zip (test_file);
731 %! assert (z_filelist, {z_file})
732 %!
733 %! unlink_or_error (test_file);
734 %! uz_filelist = z.unzip (z_file);
735 %! assert (uz_filelist, {test_file})
736 %!
737 %! assert (hash ("md5", fileread (test_file)), md5)
738 %!endfunction
739 %!test run_test_function (@test_large_file)
740 
741 ## Test that xzipped files are rexzipped (hits bug #48597, #48598)
742 %!function test_z_z (test_dir, z)
743 %! ori_file = tempname (test_dir);
744 %! create_file (ori_file, rand (100, 1));
745 %! md5_ori = hash ("md5", fileread (ori_file));
746 %!
747 %! z_file = [ori_file z.ext];
748 %! z_filelist = z.zip (ori_file);
749 %! assert (z_filelist, {z_file}) # check output
750 %! assert (exist (z_file), 2) # confirm file exists
751 %! assert (exist (ori_file), 2) # and did not remove original file
752 %!
753 %! unlink_or_error (ori_file);
754 %! uz_filelist = z.unzip (z_file);
755 %! assert (uz_filelist, {ori_file}) # bug #48598
756 %! assert (hash ("md5", fileread (ori_file)), md5_ori)
757 %! assert (exist (z_file), 2) # bug #48597
758 %!
759 %! ## xzip should dutifully re-xzip files even if they already are zipped
760 %! z_z_file = [z_file z.ext];
761 %! z_z_filelist = z.zip (z_file);
762 %! assert (z_z_filelist, {z_z_file}) # check output
763 %! assert (exist (z_z_file), 2) # confirm file exists
764 %! assert (exist (z_file), 2)
765 %!
766 %! md5_z = hash ("md5", fileread (z_file));
767 %! unlink_or_error (z_file);
768 %! uz_z_filelist = z.unzip (z_z_file);
769 %! assert (uz_z_filelist, {z_file}) # bug #48598
770 %! assert (exist (z_z_file), 2) # bug #48597
771 %! assert (hash ("md5", fileread (z_file)), md5_z)
772 %!endfunction
773 %!test <48597> run_test_function (@test_z_z)
774 
775 %!function test_xzip_dir (test_dir, z) # bug #43431
776 %! fpaths = fullfile (test_dir, {"test1", "test2", "test3"});
777 %! md5s = cell (1, 3);
778 %! for idx = 1:numel(fpaths)
779 %! create_file (fpaths{idx}, rand (100, 1));
780 %! md5s(idx) = hash ("md5", fileread (fpaths{idx}));
781 %! endfor
782 %!
783 %! test_dir = [test_dir filesep()];
784 %!
785 %! z_files = strcat (fpaths, z.ext);
786 %! z_filelist = z.zip (test_dir);
787 %! assert (sort (z_filelist), z_files(:))
788 %! for idx = 1:numel(fpaths)
789 %! assert (exist (z_files{idx}), 2)
790 %! unlink_or_error (fpaths{idx});
791 %! endfor
792 %!
793 %! ## only gunzip handles directory (bunzip2 should too though)
794 %! if (z.unzip == @gunzip)
795 %! uz_filelist = z.unzip (test_dir);
796 %! else
797 %! uz_filelist = cell (1, numel (z_filelist));
798 %! for idx = 1:numel(z_filelist)
799 %! uz_filelist(idx) = z.unzip (z_filelist{idx});
800 %! endfor
801 %! endif
802 %! assert (sort (uz_filelist), fpaths(:)) # bug #48598
803 %! for idx = 1:numel(fpaths)
804 %! assert (hash ("md5", fileread (fpaths{idx})), md5s{idx})
805 %! endfor
806 %!endfunction
807 %!test <48598> run_test_function (@test_xzip_dir)
808 
809 %!function test_save_to_dir (test_dir, z)
810 %! filename = "test-file";
811 %! filepath = fullfile (test_dir, filename);
812 %! create_file (filepath, rand (100, 1));
813 %! md5 = hash ("md5", fileread (filepath));
814 %!
815 %! ## test with existing and non-existing directory
816 %! out_dirs = {tempname (test_dir), tempname (test_dir)};
817 %! if (! mkdir (out_dirs{1}))
818 %! error ("unable to create directory for test");
819 %! endif
820 %! unwind_protect
821 %! for idx = 1:numel(out_dirs)
822 %! out_dir = out_dirs{idx};
823 %! uz_file = fullfile (out_dir, filename);
824 %! z_file = [uz_file z.ext];
825 %!
826 %! z_filelist = z.zip (filepath, out_dir);
827 %! assert (z_filelist, {z_file})
828 %! assert (exist (z_file, "file"), 2)
829 %!
830 %! uz_filelist = z.unzip (z_file);
831 %! assert (uz_filelist, {uz_file}) # bug #48598
832 %!
833 %! assert (hash ("md5", fileread (uz_file)), md5)
834 %! endfor
835 %! unwind_protect_cleanup
836 %! confirm_recursive_rmdir (false, "local");
837 %! for idx = 1:numel(out_dirs)
838 %! rmdir (out_dirs{idx}, "s");
839 %! endfor
840 %! end_unwind_protect
841 %!endfunction
842 %!test run_test_function (@test_save_to_dir)
843 */
Definition: Cell.h:37
string_vector xzip(const Array< std::string > &source_patterns, const std::function< std::string(const std::string &)> &mk_dest_path)
Definition: gzip.cc:474
int unlink(const std::string &name)
Definition: file-ops.cc:618
OCTINTERP_API void print_usage(void)
Definition: defun.cc:54
Return the CPU time used by your Octave session The first output is the total time spent executing your process and is equal to the sum of second and third which are the number of CPU seconds spent executing in user mode and the number of CPU seconds spent executing in system mode
Definition: data.cc:6348
std::string tilde_expand(const std::string &name)
Definition: file-ops.cc:276
STL namespace.
static std::string basename(const std::string &s, bool strip_path=false)
Definition: dir-ops.h:36
std::FILE * m_fp
Definition: gzip.cc:124
CFile & operator=(const CFile &)=delete
bool is_dir(void) const
Definition: file-stat.cc:57
string_vector read(void)
Definition: dir-ops.cc:70
~CFile(void)
Definition: gzip.cc:110
CFile(void)=delete
std::string concat(const std::string &dir, const std::string &file)
Definition: file-ops.cc:344
nd deftypefn *std::string name
Definition: sysdep.cc:647
OCTAVE_EXPORT octave_value_list or both For fclose
Definition: file-io.cc:676
RIIA wrapper for std::FILE*.
Definition: gzip.cc:93
std::string str
Definition: hash.cc:118
void close(void)
Definition: gzip.cc:116
static std::string base_pathname(const std::string &s)
Definition: oct-env.cc:122
is longer than or if then or only for unique occurrences of the complete pattern(false). The default is true. If a cell array of strings ar
Definition: strfind.cc:190
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
bool is_reg(void) const
Definition: file-stat.cc:75
int mkdir(const std::string &nm, mode_t md)
Definition: file-ops.cc:394
p
Definition: lu.cc:138
octave_idx_type length(void) const
Definition: ovl.h:96
octave::sys::file_stat fs(filename)
args.length() nargin
Definition: file-io.cc:589
for i
Definition: data.cc:5264
#define DEFUN_DLD(name, args_name, nargout_name, doc)
Macro to define an at run time dynamically loadable builtin function.
Definition: defun-dld.h:58
is a function function
Definition: bsxfun.cc:337
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:366
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:50
If this string is the system will ring the terminal sometimes it is useful to be able to print the original representation of the string
Definition: utils.cc:888
octave::stream os
Definition: file-io.cc:627
CFile(const std::string &path, const std::string &mode)
Definition: gzip.cc:99