summaryrefslogtreecommitdiff
path: root/src/decompress.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/decompress.c')
-rw-r--r--src/decompress.c164
1 files changed, 150 insertions, 14 deletions
diff --git a/src/decompress.c b/src/decompress.c
index 12b1f6ca094..dbdc9104a37 100644
--- a/src/decompress.c
+++ b/src/decompress.c
@@ -1,5 +1,5 @@
/* Interface to zlib.
- Copyright (C) 2013-2017 Free Software Foundation, Inc.
+ Copyright (C) 2013-2022 Free Software Foundation, Inc.
This file is part of GNU Emacs.
@@ -24,11 +24,14 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "lisp.h"
#include "buffer.h"
+#include "composite.h"
+#include "md5.h"
#include <verify.h>
#ifdef WINDOWSNT
# include <windows.h>
+# include "w32common.h"
# include "w32.h"
DEF_DLL_FN (int, inflateInit2_,
@@ -64,9 +67,112 @@ init_zlib_functions (void)
#endif /* WINDOWSNT */
+#ifdef HAVE_NATIVE_COMP
+
+# define MD5_BLOCKSIZE 32768 /* From md5.c */
+
+static char acc_buff[2 * MD5_BLOCKSIZE];
+static size_t acc_size;
+
+static void
+accumulate_and_process_md5 (void *data, size_t len, struct md5_ctx *ctxt)
+{
+ eassert (len <= MD5_BLOCKSIZE);
+ /* We may optimize this saving some of these memcpy/move using
+ directly the outer buffers but so far don't bother. */
+ memcpy (acc_buff + acc_size, data, len);
+ acc_size += len;
+ if (acc_size >= MD5_BLOCKSIZE)
+ {
+ acc_size -= MD5_BLOCKSIZE;
+ md5_process_block (acc_buff, MD5_BLOCKSIZE, ctxt);
+ memmove (acc_buff, acc_buff + MD5_BLOCKSIZE, acc_size);
+ }
+}
+
+static void
+final_process_md5 (struct md5_ctx *ctxt)
+{
+ if (acc_size)
+ {
+ md5_process_bytes (acc_buff, acc_size, ctxt);
+ acc_size = 0;
+ }
+}
+
+int
+md5_gz_stream (FILE *source, void *resblock)
+{
+ z_stream stream;
+ unsigned char in[MD5_BLOCKSIZE];
+ unsigned char out[MD5_BLOCKSIZE];
+
+# ifdef WINDOWSNT
+ if (!zlib_initialized)
+ zlib_initialized = init_zlib_functions ();
+ if (!zlib_initialized)
+ {
+ message1 ("zlib library not found");
+ return -1;
+ }
+# endif
+
+ eassert (!acc_size);
+
+ struct md5_ctx ctx;
+ md5_init_ctx (&ctx);
+
+ /* allocate inflate state */
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+ stream.avail_in = 0;
+ stream.next_in = Z_NULL;
+ int res = inflateInit2 (&stream, MAX_WBITS + 32);
+ if (res != Z_OK)
+ return -1;
+
+ do {
+ stream.avail_in = fread (in, 1, MD5_BLOCKSIZE, source);
+ if (ferror (source)) {
+ inflateEnd (&stream);
+ return -1;
+ }
+ if (stream.avail_in == 0)
+ break;
+ stream.next_in = in;
+
+ do {
+ stream.avail_out = MD5_BLOCKSIZE;
+ stream.next_out = out;
+ res = inflate (&stream, Z_NO_FLUSH);
+
+ if (res != Z_OK && res != Z_STREAM_END)
+ return -1;
+
+ accumulate_and_process_md5 (out, MD5_BLOCKSIZE - stream.avail_out, &ctx);
+ } while (!stream.avail_out);
+
+ } while (res != Z_STREAM_END);
+
+ final_process_md5 (&ctx);
+ inflateEnd (&stream);
+
+ if (res != Z_STREAM_END)
+ return -1;
+
+ md5_finish_ctx (&ctx, resblock);
+
+ return 0;
+}
+# undef MD5_BLOCKSIZE
+#endif
+
+
+
struct decompress_unwind_data
{
- ptrdiff_t old_point, start, nbytes;
+ ptrdiff_t old_point, orig, start, nbytes;
z_stream *stream;
};
@@ -76,10 +182,19 @@ unwind_decompress (void *ddata)
struct decompress_unwind_data *data = ddata;
inflateEnd (data->stream);
- /* Delete any uncompressed data already inserted on error. */
+ /* Delete any uncompressed data already inserted on error, but
+ without calling the change hooks. */
if (data->start)
- del_range (data->start, data->start + data->nbytes);
-
+ {
+ del_range_2 (data->start, data->start, /* byte, char offsets the same */
+ data->start + data->nbytes, data->start + data->nbytes,
+ 0);
+ update_compositions (data->start, data->start, CHECK_HEAD);
+ /* "Balance" the before-change-functions call, which would
+ otherwise be left "hanging". */
+ signal_after_change (data->orig, data->start - data->orig,
+ data->start - data->orig);
+ }
/* Put point where it was, or if the buffer has shrunk because the
compressed data is bigger than the uncompressed, at
point-max. */
@@ -109,18 +224,24 @@ DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0,
DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
Szlib_decompress_region,
- 2, 2, 0,
+ 2, 3, 0,
doc: /* Decompress a gzip- or zlib-compressed region.
Replace the text in the region by the decompressed data.
-On failure, return nil and leave the data in place.
+
+If optional parameter ALLOW-PARTIAL is nil or omitted, then on
+failure, return nil and leave the data in place. Otherwise, return
+the number of bytes that were not decompressed and replace the region
+text by whatever data was successfully decompressed (similar to gzip).
+If decompression is completely successful return t.
+
This function can be called only in unibyte buffers. */)
- (Lisp_Object start, Lisp_Object end)
+ (Lisp_Object start, Lisp_Object end, Lisp_Object allow_partial)
{
ptrdiff_t istart, iend, pos_byte;
z_stream stream;
int inflate_status;
struct decompress_unwind_data unwind_data;
- ptrdiff_t count = SPECPDL_INDEX ();
+ specpdl_ref count = SPECPDL_INDEX ();
validate_region (&start, &end);
@@ -139,8 +260,12 @@ This function can be called only in unibyte buffers. */)
/* This is a unibyte buffer, so character positions and bytes are
the same. */
- istart = XINT (start);
- iend = XINT (end);
+ istart = XFIXNUM (start);
+ iend = XFIXNUM (end);
+
+ /* Do the following before manipulating the gap. */
+ modify_text (istart, iend);
+
move_gap_both (iend, iend);
stream.zalloc = Z_NULL;
@@ -154,6 +279,7 @@ This function can be called only in unibyte buffers. */)
if (inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK)
return Qnil;
+ unwind_data.orig = istart;
unwind_data.start = iend;
unwind_data.stream = &stream;
unwind_data.old_point = PT;
@@ -190,15 +316,25 @@ This function can be called only in unibyte buffers. */)
}
while (inflate_status == Z_OK);
+ Lisp_Object ret = Qt;
if (inflate_status != Z_STREAM_END)
- return unbind_to (count, Qnil);
+ {
+ if (!NILP (allow_partial))
+ ret = make_int (iend - pos_byte);
+ else
+ return unbind_to (count, Qnil);
+ }
unwind_data.start = 0;
/* Delete the compressed data. */
- del_range (istart, iend);
+ del_range_2 (istart, istart, /* byte and char offsets are the same */
+ iend, iend, 0);
+
+ signal_after_change (istart, iend - istart, unwind_data.nbytes);
+ update_compositions (istart, istart, CHECK_HEAD);
- return unbind_to (count, Qt);
+ return unbind_to (count, ret);
}