diff options
author | Lars Magne Ingebrigtsen <larsi@gnus.org> | 2013-08-11 21:43:36 +0200 |
---|---|---|
committer | Lars Magne Ingebrigtsen <larsi@gnus.org> | 2013-08-11 21:43:36 +0200 |
commit | 313546eb796f4588c1c9af60f08f2bd122ef0bdb (patch) | |
tree | 011785b2e4c70e7f0ec25e28ff752e4279f2a97d /src/decompress.c | |
parent | f90e3ebd13de8169ada56274f3abaa1ea8dc6d05 (diff) | |
download | emacs-313546eb796f4588c1c9af60f08f2bd122ef0bdb.tar.gz emacs-313546eb796f4588c1c9af60f08f2bd122ef0bdb.tar.bz2 emacs-313546eb796f4588c1c9af60f08f2bd122ef0bdb.zip |
Add zlib support via the `decompress-gzipped-region' function
This adds a new file, src/decompress.c, as well as tests for the
presence of -lz.
Diffstat (limited to 'src/decompress.c')
-rw-r--r-- | src/decompress.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/src/decompress.c b/src/decompress.c new file mode 100644 index 00000000000..18f7884a4c2 --- /dev/null +++ b/src/decompress.c @@ -0,0 +1,139 @@ +/* Interface to zlib. + Copyright (C) 2013 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#ifdef HAVE_ZLIB + +#include <zlib.h> + +#include "lisp.h" +#include "character.h" +#include "buffer.h" + + +#define BUFFER_SIZE 16384 + +struct decompress_unwind_data { + ptrdiff_t old_point, start; + z_stream *stream; +}; + +static void +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); + } +} + +DEFUN ("decompress-gzipped-region", Fdecompress_gzipped_region, + Sdecompress_gzipped_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.*/) + (Lisp_Object start, Lisp_Object end) +{ + ptrdiff_t istart, iend, point = PT; + z_stream stream; + int decompressed; + char out[16384]; + 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"); + + /* This is a unibyte buffer, so character positions and bytes are + the same. */ + istart = XINT (start); + iend = XINT (end); + + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = 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) + 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; + 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; + } + + decompressed = BUFFER_SIZE - stream.avail_out; + insert_1_both (out, decompressed, decompressed, 0, 0, 0); + QUIT; + } while (stream.avail_out == 0); + + immediate_quit = 0; + + unwind_data.start = 0; + unbind_to (count, Qnil); + + /* Delete the compressed data. */ + del_range (istart, iend); + + return Qt; +} + + +/*********************************************************************** + Initialization + ***********************************************************************/ +void +syms_of_decompress (void) +{ + defsubr (&Sdecompress_gzipped_region); +} + +#endif /* HAVE_ZLIB */ |