diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h index 8fb22f70505808..d9d40a3c63e238 100644 --- a/Include/internal/pycore_global_objects_fini_generated.h +++ b/Include/internal/pycore_global_objects_fini_generated.h @@ -940,6 +940,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fix_imports)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(flags)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(flush)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(fname)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(follow_symlinks)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(format)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(frequency)); @@ -1059,6 +1060,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) { _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(modules)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mro)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(msg)); + _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mtime)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mycmp)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(n)); _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(n_arg)); diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h index 39ffda8419a77d..ff9d868ee4de0c 100644 --- a/Include/internal/pycore_global_strings.h +++ b/Include/internal/pycore_global_strings.h @@ -429,6 +429,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(fix_imports) STRUCT_FOR_ID(flags) STRUCT_FOR_ID(flush) + STRUCT_FOR_ID(fname) STRUCT_FOR_ID(follow_symlinks) STRUCT_FOR_ID(format) STRUCT_FOR_ID(frequency) @@ -548,6 +549,7 @@ struct _Py_global_strings { STRUCT_FOR_ID(modules) STRUCT_FOR_ID(mro) STRUCT_FOR_ID(msg) + STRUCT_FOR_ID(mtime) STRUCT_FOR_ID(mycmp) STRUCT_FOR_ID(n) STRUCT_FOR_ID(n_arg) diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h index 43a8243bf41b7e..20a39e0df055a7 100644 --- a/Include/internal/pycore_runtime_init_generated.h +++ b/Include/internal/pycore_runtime_init_generated.h @@ -938,6 +938,7 @@ extern "C" { INIT_ID(fix_imports), \ INIT_ID(flags), \ INIT_ID(flush), \ + INIT_ID(fname), \ INIT_ID(follow_symlinks), \ INIT_ID(format), \ INIT_ID(frequency), \ @@ -1057,6 +1058,7 @@ extern "C" { INIT_ID(modules), \ INIT_ID(mro), \ INIT_ID(msg), \ + INIT_ID(mtime), \ INIT_ID(mycmp), \ INIT_ID(n), \ INIT_ID(n_arg), \ diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h index 729d54bbb951e7..d076e3d3d8bc46 100644 --- a/Include/internal/pycore_unicodeobject_generated.h +++ b/Include/internal/pycore_unicodeobject_generated.h @@ -1128,6 +1128,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(flush); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(fname); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(follow_symlinks); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); @@ -1485,6 +1488,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) { string = &_Py_ID(msg); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); + string = &_Py_ID(mtime); + assert(_PyUnicode_CheckConsistency(string, 1)); + _PyUnicode_InternInPlace(interp, &string); string = &_Py_ID(mycmp); assert(_PyUnicode_CheckConsistency(string, 1)); _PyUnicode_InternInPlace(interp, &string); diff --git a/Lib/gzip.py b/Lib/gzip.py index 177f9080dc5af8..65450df9a35a47 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -217,12 +217,15 @@ def __init__(self, filename=None, mode=None, FutureWarning, 2) self.mode = WRITE self._init_write(filename) + if mtime is None: + mtime = int(time.time()) self.compress = zlib.compressobj(compresslevel, zlib.DEFLATED, - -zlib.MAX_WBITS, + 16 + zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, - 0) - self._write_mtime = mtime + 0, + mtime=mtime, + fname=self._encode_fname()) self._buffer_size = _WRITE_BUFFER_SIZE self._buffer = io.BufferedWriter(_WriteBufferStream(self), buffer_size=self._buffer_size) @@ -231,9 +234,6 @@ def __init__(self, filename=None, mode=None, self.fileobj = fileobj - if self.mode == WRITE: - self._write_gzip_header(compresslevel) - @property def mtime(self): """Last modification time read from stream, or None""" @@ -245,7 +245,6 @@ def __repr__(self): def _init_write(self, filename): self.name = filename - self.crc = zlib.crc32(b"") self.size = 0 self.writebuf = [] self.bufsize = 0 @@ -256,9 +255,7 @@ def tell(self): self._buffer.flush() return super().tell() - def _write_gzip_header(self, compresslevel): - self.fileobj.write(b'\037\213') # magic header - self.fileobj.write(b'\010') # compression method + def _encode_fname(self): try: # RFC 1952 requires the FNAME field to be Latin-1. Do not # include filenames that cannot be represented that way. @@ -269,24 +266,7 @@ def _write_gzip_header(self, compresslevel): fname = fname[:-3] except UnicodeEncodeError: fname = b'' - flags = 0 - if fname: - flags = FNAME - self.fileobj.write(chr(flags).encode('latin-1')) - mtime = self._write_mtime - if mtime is None: - mtime = time.time() - write32u(self.fileobj, int(mtime)) - if compresslevel == _COMPRESS_LEVEL_BEST: - xfl = b'\002' - elif compresslevel == _COMPRESS_LEVEL_FAST: - xfl = b'\004' - else: - xfl = b'\000' - self.fileobj.write(xfl) - self.fileobj.write(b'\377') - if fname: - self.fileobj.write(fname + b'\000') + return fname def write(self,data): self._check_not_closed() @@ -311,7 +291,6 @@ def _write_raw(self, data): if length > 0: self.fileobj.write(self.compress.compress(data)) self.size += length - self.crc = zlib.crc32(data, self.crc) self.offset += length return length @@ -355,9 +334,6 @@ def close(self): if self.mode == WRITE: self._buffer.flush() fileobj.write(self.compress.flush()) - write32u(fileobj, self.crc) - # self.size may exceed 2 GiB, or even 4 GiB - write32u(fileobj, self.size & 0xffffffff) elif self.mode == READ: self._buffer.close() finally: @@ -427,78 +403,17 @@ def readline(self, size=-1): return self._buffer.readline(size) -def _read_exact(fp, n): - '''Read exactly *n* bytes from `fp` - - This method is required because fp may be unbuffered, - i.e. return short reads. - ''' - data = fp.read(n) - while len(data) < n: - b = fp.read(n - len(data)) - if not b: - raise EOFError("Compressed file ended before the " - "end-of-stream marker was reached") - data += b - return data - - -def _read_gzip_header(fp): - '''Read a gzip header from `fp` and progress to the end of the header. - - Returns last mtime if header was present or None otherwise. - ''' - magic = fp.read(2) - if magic == b'': - return None - - if magic != b'\037\213': - raise BadGzipFile('Not a gzipped file (%r)' % magic) - - (method, flag, last_mtime) = struct.unpack(" bytes: - """ - Write a simple gzip header with no extra fields. - :param compresslevel: Compresslevel used to determine the xfl bytes. - :param mtime: The mtime (must support conversion to a 32-bit integer). - :return: A bytes object representing the gzip header. - """ - if mtime is None: - mtime = time.time() - if compresslevel == _COMPRESS_LEVEL_BEST: - xfl = 2 - elif compresslevel == _COMPRESS_LEVEL_FAST: - xfl = 4 - else: - xfl = 0 - # Pack ID1 and ID2 magic bytes, method (8=deflate), header flags (no extra - # fields added to header), mtime, xfl and os (255 for unknown OS). - return struct.pack("= 16)."); #define ZLIB_COMPRESS_METHODDEF \ {"compress", _PyCFunction_CAST(zlib_compress), METH_FASTCALL|METH_KEYWORDS, zlib_compress__doc__}, static PyObject * -zlib_compress_impl(PyObject *module, Py_buffer *data, int level, int wbits); +zlib_compress_impl(PyObject *module, Py_buffer *data, int level, int wbits, + long mtime); static PyObject * zlib_compress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -33,14 +37,14 @@ zlib_compress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 2 + #define NUM_KEYWORDS 3 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD PyObject *ob_item[NUM_KEYWORDS]; } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(level), &_Py_ID(wbits), }, + .ob_item = { &_Py_ID(level), &_Py_ID(wbits), &_Py_ID(mtime), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -49,20 +53,21 @@ zlib_compress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"", "level", "wbits", NULL}; + static const char * const _keywords[] = {"", "level", "wbits", "mtime", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "compress", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[3]; + PyObject *argsbuf[4]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; Py_buffer data = {NULL, NULL}; int level = Z_DEFAULT_COMPRESSION; int wbits = MAX_WBITS; + long mtime = 0; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); if (!args) { goto exit; } @@ -85,12 +90,21 @@ zlib_compress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObjec goto skip_optional_pos; } } - wbits = PyLong_AsInt(args[2]); - if (wbits == -1 && PyErr_Occurred()) { + if (args[2]) { + wbits = PyLong_AsInt(args[2]); + if (wbits == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + mtime = PyLong_AsLong(args[3]); + if (mtime == -1 && PyErr_Occurred()) { goto exit; } skip_optional_pos: - return_value = zlib_compress_impl(module, &data, level, wbits); + return_value = zlib_compress_impl(module, &data, level, wbits, mtime); exit: /* Cleanup for data */ @@ -206,7 +220,8 @@ zlib_decompress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj PyDoc_STRVAR(zlib_compressobj__doc__, "compressobj($module, /, level=Z_DEFAULT_COMPRESSION, method=DEFLATED,\n" " wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL,\n" -" strategy=Z_DEFAULT_STRATEGY, zdict=None)\n" +" strategy=Z_DEFAULT_STRATEGY, zdict=None, mtime=0,\n" +" fname=None)\n" "--\n" "\n" "Return a compressor object.\n" @@ -231,14 +246,19 @@ PyDoc_STRVAR(zlib_compressobj__doc__, " Z_DEFAULT_STRATEGY, Z_FILTERED, and Z_HUFFMAN_ONLY.\n" " zdict\n" " The predefined compression dictionary - a sequence of bytes\n" -" containing subsequences that are likely to occur in the input data."); +" containing subsequences that are likely to occur in the input data.\n" +" mtime\n" +" Last modification time. Valid only for gzip archives.\n" +" fname\n" +" File name. Valid only for gzip archives."); #define ZLIB_COMPRESSOBJ_METHODDEF \ {"compressobj", _PyCFunction_CAST(zlib_compressobj), METH_FASTCALL|METH_KEYWORDS, zlib_compressobj__doc__}, static PyObject * zlib_compressobj_impl(PyObject *module, int level, int method, int wbits, - int memLevel, int strategy, Py_buffer *zdict); + int memLevel, int strategy, Py_buffer *zdict, + long mtime, Py_buffer *fname); static PyObject * zlib_compressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) @@ -246,14 +266,14 @@ zlib_compressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *return_value = NULL; #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) - #define NUM_KEYWORDS 6 + #define NUM_KEYWORDS 8 static struct { PyGC_Head _this_is_not_used; PyObject_VAR_HEAD PyObject *ob_item[NUM_KEYWORDS]; } _kwtuple = { .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS) - .ob_item = { &_Py_ID(level), &_Py_ID(method), &_Py_ID(wbits), &_Py_ID(memLevel), &_Py_ID(strategy), &_Py_ID(zdict), }, + .ob_item = { &_Py_ID(level), &_Py_ID(method), &_Py_ID(wbits), &_Py_ID(memLevel), &_Py_ID(strategy), &_Py_ID(zdict), &_Py_ID(mtime), &_Py_ID(fname), }, }; #undef NUM_KEYWORDS #define KWTUPLE (&_kwtuple.ob_base.ob_base) @@ -262,14 +282,14 @@ zlib_compressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb # define KWTUPLE NULL #endif // !Py_BUILD_CORE - static const char * const _keywords[] = {"level", "method", "wbits", "memLevel", "strategy", "zdict", NULL}; + static const char * const _keywords[] = {"level", "method", "wbits", "memLevel", "strategy", "zdict", "mtime", "fname", NULL}; static _PyArg_Parser _parser = { .keywords = _keywords, .fname = "compressobj", .kwtuple = KWTUPLE, }; #undef KWTUPLE - PyObject *argsbuf[6]; + PyObject *argsbuf[8]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int level = Z_DEFAULT_COMPRESSION; int method = DEFLATED; @@ -277,8 +297,10 @@ zlib_compressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb int memLevel = DEF_MEM_LEVEL; int strategy = Z_DEFAULT_STRATEGY; Py_buffer zdict = {NULL, NULL}; + long mtime = 0; + Py_buffer fname = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 6, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 8, 0, argsbuf); if (!args) { goto exit; } @@ -330,21 +352,46 @@ zlib_compressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb goto skip_optional_pos; } } - if (PyObject_GetBuffer(args[5], &zdict, PyBUF_SIMPLE) != 0) { + if (args[5]) { + if (PyObject_GetBuffer(args[5], &zdict, PyBUF_SIMPLE) != 0) { + goto exit; + } + if (!PyBuffer_IsContiguous(&zdict, 'C')) { + _PyArg_BadArgument("compressobj", "argument 'zdict'", "contiguous buffer", args[5]); + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[6]) { + mtime = PyLong_AsLong(args[6]); + if (mtime == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (PyObject_GetBuffer(args[7], &fname, PyBUF_SIMPLE) != 0) { goto exit; } - if (!PyBuffer_IsContiguous(&zdict, 'C')) { - _PyArg_BadArgument("compressobj", "argument 'zdict'", "contiguous buffer", args[5]); + if (!PyBuffer_IsContiguous(&fname, 'C')) { + _PyArg_BadArgument("compressobj", "argument 'fname'", "contiguous buffer", args[7]); goto exit; } skip_optional_pos: - return_value = zlib_compressobj_impl(module, level, method, wbits, memLevel, strategy, &zdict); + return_value = zlib_compressobj_impl(module, level, method, wbits, memLevel, strategy, &zdict, mtime, &fname); exit: /* Cleanup for zdict */ if (zdict.obj) { PyBuffer_Release(&zdict); } + /* Cleanup for fname */ + if (fname.obj) { + PyBuffer_Release(&fname); + } return return_value; } @@ -1129,4 +1176,4 @@ zlib_crc32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) #ifndef ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #define ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF #endif /* !defined(ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF) */ -/*[clinic end generated code: output=6d90c72ba2dd04c5 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6124e51888f71a3c input=a9049054013a1b77]*/ diff --git a/Modules/zlibmodule.c b/Modules/zlibmodule.c index 9b76afa0e56f76..762ae3303d6e0c 100644 --- a/Modules/zlibmodule.c +++ b/Modules/zlibmodule.c @@ -19,6 +19,7 @@ // Blocks output buffer wrappers #include "pycore_blocks_output_buffer.h" +#include "pycore_pymem.h" #if OUTPUT_BUFFER_MAX_BLOCK_SIZE > UINT32_MAX #error "The maximum block size accepted by zlib is UINT32_MAX." @@ -219,6 +220,7 @@ typedef struct bool is_initialised; PyObject *zdict; PyThread_type_lock lock; + gz_header gz; } compobject; static void @@ -250,6 +252,13 @@ zlib_error(zlibstate *state, z_stream zst, int err, const char *msg) PyErr_Format(state->ZlibError, "Error %d %s: %.200s", err, msg, zmsg); } +static void +init_gz_header(gz_header *gz) +{ + memset(gz, 0, sizeof(*gz)); + gz->os = 0xff; // unknown OS +} + /*[clinic input] module zlib class zlib.Compress "compobject *" "&Comptype" @@ -283,6 +292,7 @@ newcompobject(PyTypeObject *type) PyErr_SetString(PyExc_MemoryError, "Unable to allocate lock"); return NULL; } + init_gz_header(&self->gz); return self; } @@ -319,18 +329,22 @@ zlib.compress Compression level, in 0-9 or -1. wbits: int(c_default="MAX_WBITS") = MAX_WBITS The window buffer size and container format. + mtime: long(c_default="0") = 0 + Last modification time. Valid only for gzip archives (wbits >= 16). Returns a bytes object containing compressed data. [clinic start generated code]*/ static PyObject * -zlib_compress_impl(PyObject *module, Py_buffer *data, int level, int wbits) -/*[clinic end generated code: output=46bd152fadd66df2 input=c4d06ee5782a7e3f]*/ +zlib_compress_impl(PyObject *module, Py_buffer *data, int level, int wbits, + long mtime) +/*[clinic end generated code: output=11cd0e9d320110f0 input=f566658037d72992]*/ { PyObject *return_value; int flush; z_stream zst; _BlocksOutputBuffer buffer = {.list = NULL}; + gz_header gz; zlibstate *state = get_zlib_state(module); @@ -364,6 +378,17 @@ zlib_compress_impl(PyObject *module, Py_buffer *data, int level, int wbits) goto error; } + if (wbits >= 16) { + init_gz_header(&gz); + gz.time = mtime; + err = deflateSetHeader(&zst, &gz); + if (err != Z_OK) { + zlib_error(state, zst, err, "while setting mtime"); + deflateEnd(&zst); + goto error; + } + } + do { arrange_input_buffer(&zst, &ibuflen); flush = ibuflen == 0 ? Z_FINISH : Z_NO_FLUSH; @@ -555,14 +580,19 @@ zlib.compressobj zdict: Py_buffer = None The predefined compression dictionary - a sequence of bytes containing subsequences that are likely to occur in the input data. + mtime: long(c_default="0") = 0 + Last modification time. Valid only for gzip archives. + fname: Py_buffer = None + File name. Valid only for gzip archives. Return a compressor object. [clinic start generated code]*/ static PyObject * zlib_compressobj_impl(PyObject *module, int level, int method, int wbits, - int memLevel, int strategy, Py_buffer *zdict) -/*[clinic end generated code: output=8b5bed9c8fc3814d input=2fa3d026f90ab8d5]*/ + int memLevel, int strategy, Py_buffer *zdict, + long mtime, Py_buffer *fname) +/*[clinic end generated code: output=36c8d93984d14271 input=e996fed90b0ca45d]*/ { zlibstate *state = get_zlib_state(module); if (zdict->buf != NULL && (size_t)zdict->len > UINT_MAX) { @@ -583,6 +613,23 @@ zlib_compressobj_impl(PyObject *module, int level, int method, int wbits, switch (err) { case Z_OK: self->is_initialised = 1; + if (wbits >= 16) { + self->gz.time = mtime; + if (fname->buf != NULL) { + self->gz.name = PyMem_Malloc(fname->len + 1); + if (self->gz.name == NULL) { + PyErr_SetNone(PyExc_MemoryError); + goto error; + } + memcpy(self->gz.name, fname->buf, fname->len); + self->gz.name[fname->len] = 0; + } + err = deflateSetHeader(&self->zst, &self->gz); + if (err != Z_OK) { + PyErr_SetString(PyExc_ValueError, "Invalid header"); + goto error; + } + } if (zdict->buf == NULL) { goto success; } else { @@ -718,8 +765,10 @@ Dealloc(compobject *self) static void Comp_dealloc(compobject *self) { - if (self->is_initialised) + if (self->is_initialised) { deflateEnd(&self->zst); + PyMem_Free(self->gz.name); + } Dealloc(self); } @@ -1095,6 +1144,13 @@ zlib_Compress_copy_impl(compobject *self, PyTypeObject *cls) Py_XSETREF(return_value->unconsumed_tail, Py_NewRef(self->unconsumed_tail)); Py_XSETREF(return_value->zdict, Py_XNewRef(self->zdict)); return_value->eof = self->eof; + if (self->gz.name != NULL) { + return_value->gz.name = (Bytef *)_PyMem_Strdup((char *)self->gz.name); + if (return_value->gz.name == NULL) { + PyErr_SetNone(PyExc_MemoryError); + goto error; + } + } /* Mark it as being initialized */ return_value->is_initialised = 1; @@ -1351,6 +1407,7 @@ typedef struct { bool is_initialised; char eof; /* Py_T_BOOL expects a char */ char needs_input; + gz_header gz; } ZlibDecompressor; /*[clinic input] @@ -1740,6 +1797,7 @@ ZlibDecompressor__new__(PyTypeObject *cls, self->zst.next_in = NULL; self->zst.avail_in = 0; self->unused_data = PyBytes_FromStringAndSize(NULL, 0); + init_gz_header(&self->gz); if (self->unused_data == NULL) { Py_CLEAR(self); return NULL; @@ -1760,6 +1818,10 @@ ZlibDecompressor__new__(PyTypeObject *cls, return NULL; } } + if (wbits >= 16 && inflateGetHeader(&self->zst, &self->gz) < 0) { + Py_DECREF(self); + return NULL; + } return (PyObject *)self; case Z_STREAM_ERROR: Py_DECREF(self); @@ -1821,6 +1883,13 @@ PyDoc_STRVAR(ZlibDecompressor_unused_data__doc__, PyDoc_STRVAR(ZlibDecompressor_needs_input_doc, "True if more input is needed before more decompressed data can be produced."); +PyDoc_STRVAR(ZlibDecompressor_gz_header_mtime_doc, +"Modification time if decompressing a gzip archive, otherwise 0."); + +PyDoc_STRVAR(ZlibDecompressor_gz_header_done_doc, +"1 if decompressing a gzip archive and the header is fully read, " +"-1 if the header is partially read, otherwise 0."); + static PyMemberDef ZlibDecompressor_members[] = { {"eof", Py_T_BOOL, offsetof(ZlibDecompressor, eof), Py_READONLY, ZlibDecompressor_eof__doc__}, @@ -1828,6 +1897,10 @@ static PyMemberDef ZlibDecompressor_members[] = { Py_READONLY, ZlibDecompressor_unused_data__doc__}, {"needs_input", Py_T_BOOL, offsetof(ZlibDecompressor, needs_input), Py_READONLY, ZlibDecompressor_needs_input_doc}, + {"gz_header_mtime", Py_T_ULONG, offsetof(ZlibDecompressor, gz.time), Py_READONLY, + ZlibDecompressor_gz_header_mtime_doc}, + {"gz_header_done", Py_T_INT, offsetof(ZlibDecompressor, gz.done), Py_READONLY, + ZlibDecompressor_gz_header_done_doc}, {NULL}, };