#include "character.h"
#include "buffer.h"
-\f
-#define BUFFER_SIZE 16384
+#include <verify.h>
+
+static Lisp_Object Qzlib_dll;
+
+#ifdef WINDOWSNT
+#include <windows.h>
+#include "w32.h"
+
+/* Macro for defining functions that will be loaded from the zlib DLL. */
+#define DEF_ZLIB_FN(rettype,func,args) static rettype (FAR CDECL *fn_##func)args
+
+/* Macro for loading zlib functions from the library. */
+#define LOAD_ZLIB_FN(lib,func) { \
+ fn_##func = (void *) GetProcAddress (lib, #func); \
+ if (!fn_##func) return false; \
+ }
-struct decompress_unwind_data {
- ptrdiff_t old_point, start;
+DEF_ZLIB_FN (int, inflateInit2_,
+ (z_streamp strm, int windowBits, const char *version, int stream_size));
+
+DEF_ZLIB_FN (int, inflate,
+ (z_streamp strm, int flush));
+
+DEF_ZLIB_FN (int, inflateEnd,
+ (z_streamp strm));
+
+static bool zlib_initialized;
+
+static bool
+init_zlib_functions (void)
+{
+ HMODULE library = w32_delayed_load (Qzlib_dll);
+
+ if (!library)
+ {
+ message1 ("zlib library not found");
+ return false;
+ }
+
+ LOAD_ZLIB_FN (library, inflateInit2_);
+ LOAD_ZLIB_FN (library, inflate);
+ LOAD_ZLIB_FN (library, inflateEnd);
+ return true;
+}
+
+#define fn_inflateInit2(strm, windowBits) \
+ fn_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+
+#else /* !WINDOWSNT */
+
+#define fn_inflateInit2 inflateInit2
+#define fn_inflate inflate
+#define fn_inflateEnd inflateEnd
+
+#endif /* WINDOWSNT */
+
+\f
+struct decompress_unwind_data
+{
+ ptrdiff_t old_point, start, nbytes;
z_stream *stream;
};
static void
-unwind_decompress (void *ddata) {
+unwind_decompress (void *ddata)
+{
struct decompress_unwind_data *data = ddata;
- inflateEnd (data->stream);
- /* Delete any uncompressed data already inserted and restore
- point. */
- if (data->start) {
- del_range (data->start, PT);
- SET_PT (data->old_point);
- }
+ fn_inflateEnd (data->stream);
+
+ /* Delete any uncompressed data already inserted on error. */
+ if (data->start)
+ del_range (data->start, data->start + data->nbytes);
+
+ /* Put point where it was, or if the buffer has shrunk because the
+ compressed data is bigger than the uncompressed, at
+ point-max. */
+ SET_PT (min (data->old_point, ZV));
}
-DEFUN ("decompress-gzipped-region", Fdecompress_gzipped_region,
- Sdecompress_gzipped_region,
+DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0,
+ doc: /* Return t if zlib decompression is available in this instance of Emacs. */)
+ (void)
+{
+#ifdef WINDOWSNT
+ Lisp_Object found = Fassq (Qzlib_dll, Vlibrary_cache);
+ if (CONSP (found))
+ return XCDR (found);
+ else
+ {
+ Lisp_Object status;
+ zlib_initialized = init_zlib_functions ();
+ status = zlib_initialized ? Qt : Qnil;
+ Vlibrary_cache = Fcons (Fcons (Qzlib_dll, status), Vlibrary_cache);
+ return status;
+ }
+#else
+ return Qt;
+#endif
+}
+
+DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
+ Szlib_decompress_region,
2, 2, 0,
- doc: /* Decompress a gzip-compressed region.
-The text in the region will be replaced by the decompressed data.
-On failure, nil is returned and the data is left in place.
-This function can only be called in unibyte buffers.*/)
+ 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.
+This function can be called only in unibyte buffers. */)
(Lisp_Object start, Lisp_Object end)
{
- ptrdiff_t istart, iend, point = PT;
+ ptrdiff_t istart, iend, pos_byte;
z_stream stream;
- int decompressed;
- char out[16384];
+ int inflate_status;
struct decompress_unwind_data unwind_data;
ptrdiff_t count = SPECPDL_INDEX ();
validate_region (&start, &end);
- move_gap_both (iend, iend);
if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
- error ("This function can only be called in unibyte buffers");
+ error ("This function can be called only in unibyte buffers");
+
+#ifdef WINDOWSNT
+ if (!zlib_initialized)
+ zlib_initialized = init_zlib_functions ();
+ if (!zlib_initialized)
+ return Qnil;
+#endif
/* This is a unibyte buffer, so character positions and bytes are
- the same. */
+ the same. */
istart = XINT (start);
iend = XINT (end);
+ move_gap_both (iend, iend);
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.avail_in = 0;
stream.next_in = Z_NULL;
- /* This magic number apparently means "this is gzip". */
- if (inflateInit2 (&stream, 16 + MAX_WBITS) != Z_OK)
+ /* The magic number 32 apparently means "autodetect both the gzip and
+ zlib formats" according to zlib.h. */
+ if (fn_inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK)
return Qnil;
- /* We're inserting the decompressed data at the end of the
- compressed data. */
- SET_PT (iend);
-
- stream.avail_in = iend - istart;
- stream.next_in = (char *) BYTE_POS_ADDR (istart);
-
unwind_data.start = iend;
unwind_data.stream = &stream;
- unwind_data.old_point = point;
+ unwind_data.old_point = PT;
+ unwind_data.nbytes = 0;
record_unwind_protect_ptr (unwind_decompress, &unwind_data);
- immediate_quit = 1;
-
- /* Run inflate() on input until the output buffer isn't full. */
- do {
- stream.avail_out = BUFFER_SIZE;
- stream.next_out = out;
- switch (inflate (&stream, Z_NO_FLUSH)) {
- case Z_STREAM_ERROR:
- case Z_NEED_DICT:
- case Z_DATA_ERROR:
- case Z_MEM_ERROR:
- unbind_to (count, Qnil);
- return Qnil;
- }
+ /* Insert the decompressed data at the end of the compressed data. */
+ SET_PT (iend);
- decompressed = BUFFER_SIZE - stream.avail_out;
- insert_1_both (out, decompressed, decompressed, 0, 0, 0);
- QUIT;
- } while (stream.avail_out == 0);
+ pos_byte = istart;
+
+ /* Keep calling 'inflate' until it reports an error or end-of-input. */
+ do
+ {
+ /* Maximum number of bytes that one 'inflate' call should read and write.
+ Do not make avail_out too large, as that might unduly delay C-g.
+ zlib requires that avail_in and avail_out not exceed UINT_MAX. */
+ ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
+ int avail_out = 16 * 1024;
+ int decompressed;
+
+ if (GAP_SIZE < avail_out)
+ make_gap (avail_out - GAP_SIZE);
+ stream.next_in = BYTE_POS_ADDR (pos_byte);
+ stream.avail_in = avail_in;
+ stream.next_out = GPT_ADDR;
+ stream.avail_out = avail_out;
+ inflate_status = fn_inflate (&stream, Z_NO_FLUSH);
+ pos_byte += avail_in - stream.avail_in;
+ decompressed = avail_out - stream.avail_out;
+ insert_from_gap (decompressed, decompressed, 0);
+ unwind_data.nbytes += decompressed;
+ QUIT;
+ }
+ while (inflate_status == Z_OK);
- immediate_quit = 0;
+ if (inflate_status != Z_STREAM_END)
+ return unbind_to (count, Qnil);
unwind_data.start = 0;
- unbind_to (count, Qnil);
- /* Delete the compressed data. */
+ /* Delete the compressed data. */
del_range (istart, iend);
- return Qt;
+ return unbind_to (count, Qt);
}
\f
void
syms_of_decompress (void)
{
- defsubr (&Sdecompress_gzipped_region);
+ DEFSYM (Qzlib_dll, "zlib");
+ defsubr (&Szlib_decompress_region);
+ defsubr (&Szlib_available_p);
}
#endif /* HAVE_ZLIB */