summaryrefslogtreecommitdiff
path: root/ext/zlib
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2019-11-26 17:31:47 -0800
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2020-09-14 17:52:20 +0900
commitd52dffd817d9285f7600138e2f69f46891fff845 (patch)
tree73a0c3b29b5dd48a499618307feead2c273f1441 /ext/zlib
parentf1d32010e6e263ae94ee480a0d537727e91485ed (diff)
[ruby/zlib] Add Zlib::GzipReader.zcat for handling multiple gzip streams in gz file
Most gzip tools support concatenated gz streams in a gz file. This offers a way to handle such gz files in Ruby. Fixes [Bug #9790] Fixes [Bug #11180] Fixes [Bug #14804] https://github.com/ruby/zlib/commit/e2ce56de7d
Diffstat (limited to 'ext/zlib')
-rw-r--r--ext/zlib/zlib.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 5c8aab24af..bc41b55491 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -3724,6 +3724,60 @@ rb_gzreader_s_open(int argc, VALUE *argv, VALUE klass)
}
/*
+ * Document-method: Zlib::GzipReader.zcat
+ *
+ * call-seq:
+ * Zlib::GzipReader.zcat(io, options = {}, &block) => nil
+ * Zlib::GzipReader.zcat(io, options = {}) => string
+ *
+ * Decompresses all gzip data in the +io+, handling multiple gzip
+ * streams until the end of the +io+. There should not be any non-gzip
+ * data after the gzip streams.
+ *
+ * If a block is given, it is yielded strings of uncompressed data,
+ * and the method returns +nil+.
+ * If a block is not given, the method returns the concatenation of
+ * all uncompressed data in all gzip streams.
+ */
+static VALUE
+rb_gzreader_s_zcat(int argc, VALUE *argv, VALUE klass)
+{
+ VALUE io, unused, obj, buf=0, tmpbuf;
+ long pos;
+
+ rb_check_arity(argc, 1, 2);
+ io = argv[0];
+
+ do {
+ obj = rb_funcallv(klass, rb_intern("new"), argc, argv);
+ if (rb_block_given_p()) {
+ rb_gzreader_each(0, 0, obj);
+ }
+ else {
+ if (!buf) {
+ buf = rb_str_new(0, 0);
+ }
+ tmpbuf = gzfile_read_all(get_gzfile(obj));
+ rb_str_cat(buf, RSTRING_PTR(tmpbuf), RSTRING_LEN(tmpbuf));
+ }
+
+ rb_gzreader_read(0, 0, obj);
+ pos = NUM2LONG(rb_funcall(io, rb_intern("pos"), 0));
+ unused = rb_gzreader_unused(obj);
+ rb_gzfile_finish(obj);
+ if (!NIL_P(unused)) {
+ pos -= NUM2LONG(rb_funcall(unused, rb_intern("length"), 0));
+ rb_funcall(io, rb_intern("pos="), 1, LONG2NUM(pos));
+ }
+ } while (pos < NUM2LONG(rb_funcall(io, rb_intern("size"), 0)));
+
+ if (rb_block_given_p()) {
+ return Qnil;
+ }
+ return buf;
+}
+
+/*
* Document-method: Zlib::GzipReader.new
*
* call-seq:
@@ -4696,6 +4750,7 @@ Init_zlib(void)
rb_define_method(cGzipWriter, "puts", rb_gzwriter_puts, -1);
rb_define_singleton_method(cGzipReader, "open", rb_gzreader_s_open,-1);
+ rb_define_singleton_method(cGzipReader, "zcat", rb_gzreader_s_zcat, -1);
rb_define_alloc_func(cGzipReader, rb_gzreader_s_allocate);
rb_define_method(cGzipReader, "initialize", rb_gzreader_initialize, -1);
rb_define_method(cGzipReader, "rewind", rb_gzreader_rewind, 0);