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