Minor zlib configuration tweaks.
[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 static 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
44 DEF_ZLIB_FN (int, inflateInit2_,
45 (z_streamp strm, int windowBits, const char *version, int stream_size));
46
47 DEF_ZLIB_FN (int, inflate,
48 (z_streamp strm, int flush));
49
50 DEF_ZLIB_FN (int, inflateEnd,
51 (z_streamp strm));
52
53 static bool
54 init_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
70 #define fn_inflateInit2(strm, windowBits) \
71 fn_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
72
73 #else /* !WINDOWSNT */
74
75 #define fn_inflateInit2 inflateInit2
76 #define fn_inflate inflate
77 #define fn_inflateEnd inflateEnd
78
79 #endif /* WINDOWSNT */
80
81 \f
82 struct decompress_unwind_data
83 {
84 ptrdiff_t old_point, start;
85 z_stream *stream;
86 };
87
88 static void
89 unwind_decompress (void *ddata)
90 {
91 struct decompress_unwind_data *data = ddata;
92 fn_inflateEnd (data->stream);
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 }
100 }
101
102 DEFUN ("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
122 DEFUN ("zlib-decompress-gzipped-region", Fzlib_decompress_gzipped_region,
123 Szlib_decompress_gzipped_region,
124 2, 2, 0,
125 doc: /* Decompress a gzip-compressed region.
126 Replace the text in the region by the decompressed data.
127 On failure, return nil and leave the data in place.
128 This function can be called only in unibyte buffers. */)
129 (Lisp_Object start, Lisp_Object end)
130 {
131 ptrdiff_t istart, iend, pos_byte;
132 z_stream stream;
133 int inflate_status;
134 struct decompress_unwind_data unwind_data;
135 ptrdiff_t count = SPECPDL_INDEX ();
136
137 validate_region (&start, &end);
138
139 if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
140 error ("This function can be called only in unibyte buffers");
141
142 /* This is a unibyte buffer, so character positions and bytes are
143 the same. */
144 istart = XINT (start);
145 iend = XINT (end);
146 move_gap_both (iend, iend);
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
154 /* This magic number apparently means "this is gzip". */
155 if (fn_inflateInit2 (&stream, 16 + MAX_WBITS) != Z_OK)
156 return Qnil;
157
158 unwind_data.start = iend;
159 unwind_data.stream = &stream;
160 unwind_data.old_point = PT;
161
162 record_unwind_protect_ptr (unwind_decompress, &unwind_data);
163
164 /* Insert the decompressed data at the end of the compressed data. */
165 SET_PT (iend);
166
167 pos_byte = istart;
168
169 /* Keep calling 'inflate' until it reports an error or end-of-input. */
170 do
171 {
172 /* Maximum number of bytes that one 'inflate' call should read and write.
173 zlib requires that these values not exceed UINT_MAX.
174 Do not make avail_out too large, as that might unduly delay C-g. */
175 ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
176 ptrdiff_t avail_out = min (1 << 14, UINT_MAX);
177
178 ptrdiff_t decompressed;
179
180 if (GAP_SIZE < avail_out)
181 make_gap (avail_out - GAP_SIZE);
182 stream.next_in = BYTE_POS_ADDR (pos_byte);
183 stream.avail_in = avail_in;
184 stream.next_out = GPT_ADDR;
185 stream.avail_out = avail_out;
186 inflate_status = fn_inflate (&stream, Z_NO_FLUSH);
187 pos_byte += avail_in - stream.avail_in;
188 decompressed = avail_out - stream.avail_out;
189 insert_from_gap (decompressed, decompressed, 0);
190 QUIT;
191 }
192 while (inflate_status == Z_OK);
193
194 if (inflate_status != Z_STREAM_END)
195 return unbind_to (count, Qnil);
196
197 unwind_data.start = 0;
198
199 /* Delete the compressed data. */
200 del_range (istart, iend);
201
202 return unbind_to (count, Qt);
203 }
204
205 \f
206 /***********************************************************************
207 Initialization
208 ***********************************************************************/
209 void
210 syms_of_decompress (void)
211 {
212 DEFSYM (Qzlib_dll, "zlib");
213 defsubr (&Szlib_decompress_gzipped_region);
214 defsubr (&Szlib_available_p);
215 }
216
217 #endif /* HAVE_ZLIB */