use dynwind_begin and dynwind_end
[bpt/emacs.git] / src / decompress.c
1 /* Interface to zlib.
2 Copyright (C) 2013-2014 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along 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
31 static 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
46 DEF_ZLIB_FN (int, inflateInit2_,
47 (z_streamp strm, int windowBits, const char *version, int stream_size));
48
49 DEF_ZLIB_FN (int, inflate,
50 (z_streamp strm, int flush));
51
52 DEF_ZLIB_FN (int, inflateEnd,
53 (z_streamp strm));
54
55 static bool zlib_initialized;
56
57 static bool
58 init_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 \f
86 struct decompress_unwind_data
87 {
88 ptrdiff_t old_point, start, nbytes;
89 z_stream *stream;
90 };
91
92 static void
93 unwind_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
108 DEFUN ("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
129 DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
130 Szlib_decompress_region,
131 2, 2, 0,
132 doc: /* Decompress a gzip- or zlib-compressed region.
133 Replace the text in the region by the decompressed data.
134 On failure, return nil and leave the data in place.
135 This 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 dynwind_begin ();
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 dynwind_end ();
154 return Qnil;
155 }
156 #endif
157
158 /* This is a unibyte buffer, so character positions and bytes are
159 the same. */
160 istart = XINT (start);
161 iend = XINT (end);
162 move_gap_both (iend, iend);
163
164 stream.zalloc = Z_NULL;
165 stream.zfree = Z_NULL;
166 stream.opaque = Z_NULL;
167 stream.avail_in = 0;
168 stream.next_in = Z_NULL;
169
170 /* The magic number 32 apparently means "autodetect both the gzip and
171 zlib formats" according to zlib.h. */
172 if (fn_inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK) {
173 dynwind_end ();
174 return Qnil;
175 }
176
177 unwind_data.start = iend;
178 unwind_data.stream = &stream;
179 unwind_data.old_point = PT;
180 unwind_data.nbytes = 0;
181 record_unwind_protect_ptr (unwind_decompress, &unwind_data);
182
183 /* Insert the decompressed data at the end of the compressed data. */
184 SET_PT (iend);
185
186 pos_byte = istart;
187
188 /* Keep calling 'inflate' until it reports an error or end-of-input. */
189 do
190 {
191 /* Maximum number of bytes that one 'inflate' call should read and write.
192 Do not make avail_out too large, as that might unduly delay C-g.
193 zlib requires that avail_in and avail_out not exceed UINT_MAX. */
194 ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
195 int avail_out = 16 * 1024;
196 int decompressed;
197
198 if (GAP_SIZE < avail_out)
199 make_gap (avail_out - GAP_SIZE);
200 stream.next_in = BYTE_POS_ADDR (pos_byte);
201 stream.avail_in = avail_in;
202 stream.next_out = GPT_ADDR;
203 stream.avail_out = avail_out;
204 inflate_status = fn_inflate (&stream, Z_NO_FLUSH);
205 pos_byte += avail_in - stream.avail_in;
206 decompressed = avail_out - stream.avail_out;
207 insert_from_gap (decompressed, decompressed, 0);
208 unwind_data.nbytes += decompressed;
209 QUIT;
210 }
211 while (inflate_status == Z_OK);
212
213 if (inflate_status != Z_STREAM_END){
214
215 dynwind_end ();
216 return Qnil;
217 }
218
219 unwind_data.start = 0;
220
221 /* Delete the compressed data. */
222 del_range (istart, iend);
223
224 dynwind_end ();
225 return Qt;
226 }
227
228 \f
229 /***********************************************************************
230 Initialization
231 ***********************************************************************/
232 void
233 syms_of_decompress (void)
234 {
235 #include "decompress.x"
236
237 DEFSYM (Qzlib_dll, "zlib");
238 }
239
240 #endif /* HAVE_ZLIB */