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