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