Add a test for decompressing gzipped data
[bpt/emacs.git] / src / decompress.c
CommitLineData
313546eb
LMI
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
594a4307
EZ
29static Lisp_Object Qzlib_dll;
30
31#ifdef WINDOWSNT
32#include <windows.h>
33#include "w32.h"
34
35/* Macro for defining functions that will be loaded from the zlib DLL. */
36#define DEF_ZLIB_FN(rettype,func,args) static rettype (FAR CDECL *fn_##func)args
37
38/* Macro for loading zlib functions from the library. */
39#define LOAD_ZLIB_FN(lib,func) { \
40 fn_##func = (void *) GetProcAddress (lib, #func); \
41 if (!fn_##func) return 0; \
42 }
43
44DEF_ZLIB_FN (int, inflateInit2_,
45 (z_streamp strm, int windowBits, const char *version, int stream_size));
46
47DEF_ZLIB_FN (int, inflate,
48 (z_streamp strm, int flush));
49
50DEF_ZLIB_FN (int, inflateEnd,
51 (z_streamp strm));
52
53static bool
54init_zlib_functions (void)
55{
56 HMODULE library = w32_delayed_load (Qzlib_dll);
57
58 if (!library)
59 {
60 message1 ("zlib library not found");
61 return 0;
62 }
63
64 LOAD_ZLIB_FN (library, inflateInit2_);
65 LOAD_ZLIB_FN (library, inflate);
66 LOAD_ZLIB_FN (library, inflateEnd);
67 return 1;
68}
69
1d238bc7
PE
70#define fn_inflateInit2(strm, windowBits) \
71 fn_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
72
594a4307
EZ
73#else /* !WINDOWSNT */
74
1d238bc7 75#define fn_inflateInit2 inflateInit2
594a4307
EZ
76#define fn_inflate inflate
77#define fn_inflateEnd inflateEnd
78
79#endif /* WINDOWSNT */
80
313546eb 81\f
d0e615c3
PE
82struct decompress_unwind_data
83{
313546eb
LMI
84 ptrdiff_t old_point, start;
85 z_stream *stream;
86};
87
88static void
d0e615c3
PE
89unwind_decompress (void *ddata)
90{
313546eb 91 struct decompress_unwind_data *data = ddata;
594a4307 92 fn_inflateEnd (data->stream);
d0e615c3
PE
93
94 /* Delete any uncompressed data already inserted and restore point. */
95 if (data->start)
96 {
97 del_range (data->start, PT);
98 SET_PT (data->old_point);
99 }
313546eb
LMI
100}
101
594a4307
EZ
102DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0,
103 doc: /* Return t if zlib decompression is available in this instance of Emacs. */)
104 (void)
105{
106#ifdef WINDOWSNT
107 Lisp_Object found = Fassq (Qzlib_dll, Vlibrary_cache);
108 if (CONSP (found))
109 return XCDR (found);
110 else
111 {
112 Lisp_Object status;
113 status = init_zlib_functions () ? Qt : Qnil;
114 Vlibrary_cache = Fcons (Fcons (Qzlib_dll, status), Vlibrary_cache);
115 return status;
116 }
117#else
118 return Qt;
119#endif
120}
121
7699d09e
LMI
122DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
123 Szlib_decompress_region,
313546eb 124 2, 2, 0,
7699d09e 125 doc: /* Decompress a gzip- or zlib-compressed region.
d0e615c3
PE
126Replace the text in the region by the decompressed data.
127On failure, return nil and leave the data in place.
128This function can be called only in unibyte buffers. */)
313546eb
LMI
129 (Lisp_Object start, Lisp_Object end)
130{
d0e615c3 131 ptrdiff_t istart, iend, pos_byte;
313546eb 132 z_stream stream;
d0e615c3 133 int inflate_status;
313546eb
LMI
134 struct decompress_unwind_data unwind_data;
135 ptrdiff_t count = SPECPDL_INDEX ();
136
137 validate_region (&start, &end);
313546eb
LMI
138
139 if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
d0e615c3 140 error ("This function can be called only in unibyte buffers");
313546eb
LMI
141
142 /* This is a unibyte buffer, so character positions and bytes are
d0e615c3 143 the same. */
313546eb
LMI
144 istart = XINT (start);
145 iend = XINT (end);
99a32242 146 move_gap_both (iend, iend);
313546eb
LMI
147
148 stream.zalloc = Z_NULL;
149 stream.zfree = Z_NULL;
150 stream.opaque = Z_NULL;
151 stream.avail_in = 0;
152 stream.next_in = Z_NULL;
153
7699d09e
LMI
154 /* The magic number 32 apparently means "autodect both the gzip and
155 zlib formats" according to zlib.h. */
156 if (fn_inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK)
313546eb
LMI
157 return Qnil;
158
313546eb
LMI
159 unwind_data.start = iend;
160 unwind_data.stream = &stream;
d0e615c3
PE
161 unwind_data.old_point = PT;
162
313546eb
LMI
163 record_unwind_protect_ptr (unwind_decompress, &unwind_data);
164
d0e615c3
PE
165 /* Insert the decompressed data at the end of the compressed data. */
166 SET_PT (iend);
313546eb 167
d0e615c3
PE
168 pos_byte = istart;
169
170 /* Keep calling 'inflate' until it reports an error or end-of-input. */
171 do
172 {
173 /* Maximum number of bytes that one 'inflate' call should read and write.
174 zlib requires that these values not exceed UINT_MAX.
175 Do not make avail_out too large, as that might unduly delay C-g. */
176 ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
177 ptrdiff_t avail_out = min (1 << 14, UINT_MAX);
178
179 ptrdiff_t decompressed;
180
181 if (GAP_SIZE < avail_out)
182 make_gap (avail_out - GAP_SIZE);
183 stream.next_in = BYTE_POS_ADDR (pos_byte);
184 stream.avail_in = avail_in;
185 stream.next_out = GPT_ADDR;
186 stream.avail_out = avail_out;
594a4307 187 inflate_status = fn_inflate (&stream, Z_NO_FLUSH);
d0e615c3
PE
188 pos_byte += avail_in - stream.avail_in;
189 decompressed = avail_out - stream.avail_out;
190 insert_from_gap (decompressed, decompressed, 0);
191 QUIT;
192 }
193 while (inflate_status == Z_OK);
313546eb 194
d0e615c3
PE
195 if (inflate_status != Z_STREAM_END)
196 return unbind_to (count, Qnil);
313546eb
LMI
197
198 unwind_data.start = 0;
313546eb 199
d0e615c3 200 /* Delete the compressed data. */
313546eb
LMI
201 del_range (istart, iend);
202
d0e615c3 203 return unbind_to (count, Qt);
313546eb
LMI
204}
205
206\f
207/***********************************************************************
208 Initialization
209 ***********************************************************************/
210void
211syms_of_decompress (void)
212{
594a4307 213 DEFSYM (Qzlib_dll, "zlib");
7699d09e 214 defsubr (&Szlib_decompress_region);
594a4307 215 defsubr (&Szlib_available_p);
313546eb
LMI
216}
217
218#endif /* HAVE_ZLIB */