* decompress.c (unwind_decompress): Always restore point.
[bpt/emacs.git] / src / decompress.c
1 /* Interface to zlib.
2 Copyright (C) 2013 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;
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, PT);
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 status = init_zlib_functions () ? Qt : Qnil;
120 Vlibrary_cache = Fcons (Fcons (Qzlib_dll, status), Vlibrary_cache);
121 return status;
122 }
123 #else
124 return Qt;
125 #endif
126 }
127
128 DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
129 Szlib_decompress_region,
130 2, 2, 0,
131 doc: /* Decompress a gzip- or zlib-compressed region.
132 Replace the text in the region by the decompressed data.
133 On failure, return nil and leave the data in place.
134 This function can be called only in unibyte buffers. */)
135 (Lisp_Object start, Lisp_Object end)
136 {
137 ptrdiff_t istart, iend, pos_byte;
138 z_stream stream;
139 int inflate_status;
140 struct decompress_unwind_data unwind_data;
141 ptrdiff_t count = SPECPDL_INDEX ();
142
143 validate_region (&start, &end);
144
145 if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
146 error ("This function can be called only in unibyte buffers");
147
148 #ifdef WINDOWSNT
149 if (!zlib_initialized)
150 zlib_initialized = init_zlib_functions ();
151 if (!zlib_initialized)
152 return Qnil;
153 #endif
154
155 /* This is a unibyte buffer, so character positions and bytes are
156 the same. */
157 istart = XINT (start);
158 iend = XINT (end);
159 move_gap_both (iend, iend);
160
161 stream.zalloc = Z_NULL;
162 stream.zfree = Z_NULL;
163 stream.opaque = Z_NULL;
164 stream.avail_in = 0;
165 stream.next_in = Z_NULL;
166
167 /* The magic number 32 apparently means "autodect both the gzip and
168 zlib formats" according to zlib.h. */
169 if (fn_inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK)
170 return Qnil;
171
172 unwind_data.start = iend;
173 unwind_data.stream = &stream;
174 unwind_data.old_point = PT;
175
176 record_unwind_protect_ptr (unwind_decompress, &unwind_data);
177
178 /* Insert the decompressed data at the end of the compressed data. */
179 SET_PT (iend);
180
181 pos_byte = istart;
182
183 /* Keep calling 'inflate' until it reports an error or end-of-input. */
184 do
185 {
186 /* Maximum number of bytes that one 'inflate' call should read and write.
187 Do not make avail_out too large, as that might unduly delay C-g.
188 zlib requires that avail_in and avail_out not exceed UINT_MAX. */
189 ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
190 int avail_out = 16 * 1024;
191 int decompressed;
192
193 if (GAP_SIZE < avail_out)
194 make_gap (avail_out - GAP_SIZE);
195 stream.next_in = BYTE_POS_ADDR (pos_byte);
196 stream.avail_in = avail_in;
197 stream.next_out = GPT_ADDR;
198 stream.avail_out = avail_out;
199 inflate_status = fn_inflate (&stream, Z_NO_FLUSH);
200 pos_byte += avail_in - stream.avail_in;
201 decompressed = avail_out - stream.avail_out;
202 insert_from_gap (decompressed, decompressed, 0);
203 QUIT;
204 }
205 while (inflate_status == Z_OK);
206
207 if (inflate_status != Z_STREAM_END)
208 return unbind_to (count, Qnil);
209
210 unwind_data.start = 0;
211
212 /* Delete the compressed data. */
213 del_range (istart, iend);
214
215 return unbind_to (count, Qt);
216 }
217
218 \f
219 /***********************************************************************
220 Initialization
221 ***********************************************************************/
222 void
223 syms_of_decompress (void)
224 {
225 DEFSYM (Qzlib_dll, "zlib");
226 defsubr (&Szlib_decompress_region);
227 defsubr (&Szlib_available_p);
228 }
229
230 #endif /* HAVE_ZLIB */