aboutsummaryrefslogtreecommitdiffstats
path: root/src/decompress.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/decompress.c')
-rw-r--r--src/decompress.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/src/decompress.c b/src/decompress.c
new file mode 100644
index 00000000000..dc9f4b72d93
--- /dev/null
+++ b/src/decompress.c
@@ -0,0 +1,232 @@
1/* Interface to zlib.
2 Copyright (C) 2013 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18
19#include <config.h>
20
21#ifdef HAVE_ZLIB
22
23#include <zlib.h>
24
25#include "lisp.h"
26#include "character.h"
27#include "buffer.h"
28
29#include <verify.h>
30
31static Lisp_Object Qzlib_dll;
32
33#ifdef WINDOWSNT
34#include <windows.h>
35#include "w32.h"
36
37/* Macro for defining functions that will be loaded from the zlib DLL. */
38#define DEF_ZLIB_FN(rettype,func,args) static rettype (FAR CDECL *fn_##func)args
39
40/* Macro for loading zlib functions from the library. */
41#define LOAD_ZLIB_FN(lib,func) { \
42 fn_##func = (void *) GetProcAddress (lib, #func); \
43 if (!fn_##func) return false; \
44 }
45
46DEF_ZLIB_FN (int, inflateInit2_,
47 (z_streamp strm, int windowBits, const char *version, int stream_size));
48
49DEF_ZLIB_FN (int, inflate,
50 (z_streamp strm, int flush));
51
52DEF_ZLIB_FN (int, inflateEnd,
53 (z_streamp strm));
54
55static bool zlib_initialized;
56
57static bool
58init_zlib_functions (void)
59{
60 HMODULE library = w32_delayed_load (Qzlib_dll);
61
62 if (!library)
63 {
64 message1 ("zlib library not found");
65 return false;
66 }
67
68 LOAD_ZLIB_FN (library, inflateInit2_);
69 LOAD_ZLIB_FN (library, inflate);
70 LOAD_ZLIB_FN (library, inflateEnd);
71 return true;
72}
73
74#define fn_inflateInit2(strm, windowBits) \
75 fn_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
76
77#else /* !WINDOWSNT */
78
79#define fn_inflateInit2 inflateInit2
80#define fn_inflate inflate
81#define fn_inflateEnd inflateEnd
82
83#endif /* WINDOWSNT */
84
85
86struct decompress_unwind_data
87{
88 ptrdiff_t old_point, start, nbytes;
89 z_stream *stream;
90};
91
92static void
93unwind_decompress (void *ddata)
94{
95 struct decompress_unwind_data *data = ddata;
96 fn_inflateEnd (data->stream);
97
98 /* Delete any uncompressed data already inserted on error. */
99 if (data->start)
100 del_range (data->start, data->start + data->nbytes);
101
102 /* Put point where it was, or if the buffer has shrunk because the
103 compressed data is bigger than the uncompressed, at
104 point-max. */
105 SET_PT (min (data->old_point, ZV));
106}
107
108DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0,
109 doc: /* Return t if zlib decompression is available in this instance of Emacs. */)
110 (void)
111{
112#ifdef WINDOWSNT
113 Lisp_Object found = Fassq (Qzlib_dll, Vlibrary_cache);
114 if (CONSP (found))
115 return XCDR (found);
116 else
117 {
118 Lisp_Object status;
119 zlib_initialized = init_zlib_functions ();
120 status = zlib_initialized ? Qt : Qnil;
121 Vlibrary_cache = Fcons (Fcons (Qzlib_dll, status), Vlibrary_cache);
122 return status;
123 }
124#else
125 return Qt;
126#endif
127}
128
129DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
130 Szlib_decompress_region,
131 2, 2, 0,
132 doc: /* Decompress a gzip- or zlib-compressed region.
133Replace the text in the region by the decompressed data.
134On failure, return nil and leave the data in place.
135This function can be called only in unibyte buffers. */)
136 (Lisp_Object start, Lisp_Object end)
137{
138 ptrdiff_t istart, iend, pos_byte;
139 z_stream stream;
140 int inflate_status;
141 struct decompress_unwind_data unwind_data;
142 ptrdiff_t count = SPECPDL_INDEX ();
143
144 validate_region (&start, &end);
145
146 if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
147 error ("This function can be called only in unibyte buffers");
148
149#ifdef WINDOWSNT
150 if (!zlib_initialized)
151 zlib_initialized = init_zlib_functions ();
152 if (!zlib_initialized)
153 return Qnil;
154#endif
155
156 /* This is a unibyte buffer, so character positions and bytes are
157 the same. */
158 istart = XINT (start);
159 iend = XINT (end);
160 move_gap_both (iend, iend);
161
162 stream.zalloc = Z_NULL;
163 stream.zfree = Z_NULL;
164 stream.opaque = Z_NULL;
165 stream.avail_in = 0;
166 stream.next_in = Z_NULL;
167
168 /* The magic number 32 apparently means "autodetect both the gzip and
169 zlib formats" according to zlib.h. */
170 if (fn_inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK)
171 return Qnil;
172
173 unwind_data.start = iend;
174 unwind_data.stream = &stream;
175 unwind_data.old_point = PT;
176 unwind_data.nbytes = 0;
177 record_unwind_protect_ptr (unwind_decompress, &unwind_data);
178
179 /* Insert the decompressed data at the end of the compressed data. */
180 SET_PT (iend);
181
182 pos_byte = istart;
183
184 /* Keep calling 'inflate' until it reports an error or end-of-input. */
185 do
186 {
187 /* Maximum number of bytes that one 'inflate' call should read and write.
188 Do not make avail_out too large, as that might unduly delay C-g.
189 zlib requires that avail_in and avail_out not exceed UINT_MAX. */
190 ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
191 int avail_out = 16 * 1024;
192 int decompressed;
193
194 if (GAP_SIZE < avail_out)
195 make_gap (avail_out - GAP_SIZE);
196 stream.next_in = BYTE_POS_ADDR (pos_byte);
197 stream.avail_in = avail_in;
198 stream.next_out = GPT_ADDR;
199 stream.avail_out = avail_out;
200 inflate_status = fn_inflate (&stream, Z_NO_FLUSH);
201 pos_byte += avail_in - stream.avail_in;
202 decompressed = avail_out - stream.avail_out;
203 insert_from_gap (decompressed, decompressed, 0);
204 unwind_data.nbytes += decompressed;
205 QUIT;
206 }
207 while (inflate_status == Z_OK);
208
209 if (inflate_status != Z_STREAM_END)
210 return unbind_to (count, Qnil);
211
212 unwind_data.start = 0;
213
214 /* Delete the compressed data. */
215 del_range (istart, iend);
216
217 return unbind_to (count, Qt);
218}
219
220
221/***********************************************************************
222 Initialization
223 ***********************************************************************/
224void
225syms_of_decompress (void)
226{
227 DEFSYM (Qzlib_dll, "zlib");
228 defsubr (&Szlib_decompress_region);
229 defsubr (&Szlib_available_p);
230}
231
232#endif /* HAVE_ZLIB */