* decompress.c: Fix bugs with large buffers and weird inputs.
[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
29\f
d0e615c3
PE
30struct decompress_unwind_data
31{
313546eb
LMI
32 ptrdiff_t old_point, start;
33 z_stream *stream;
34};
35
36static void
d0e615c3
PE
37unwind_decompress (void *ddata)
38{
313546eb
LMI
39 struct decompress_unwind_data *data = ddata;
40 inflateEnd (data->stream);
d0e615c3
PE
41
42 /* Delete any uncompressed data already inserted and restore point. */
43 if (data->start)
44 {
45 del_range (data->start, PT);
46 SET_PT (data->old_point);
47 }
313546eb
LMI
48}
49
50DEFUN ("decompress-gzipped-region", Fdecompress_gzipped_region,
51 Sdecompress_gzipped_region,
52 2, 2, 0,
53 doc: /* Decompress a gzip-compressed region.
d0e615c3
PE
54Replace the text in the region by the decompressed data.
55On failure, return nil and leave the data in place.
56This function can be called only in unibyte buffers. */)
313546eb
LMI
57 (Lisp_Object start, Lisp_Object end)
58{
d0e615c3 59 ptrdiff_t istart, iend, pos_byte;
313546eb 60 z_stream stream;
d0e615c3 61 int inflate_status;
313546eb
LMI
62 struct decompress_unwind_data unwind_data;
63 ptrdiff_t count = SPECPDL_INDEX ();
64
65 validate_region (&start, &end);
313546eb
LMI
66
67 if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
d0e615c3 68 error ("This function can be called only in unibyte buffers");
313546eb
LMI
69
70 /* This is a unibyte buffer, so character positions and bytes are
d0e615c3 71 the same. */
313546eb
LMI
72 istart = XINT (start);
73 iend = XINT (end);
99a32242 74 move_gap_both (iend, iend);
313546eb
LMI
75
76 stream.zalloc = Z_NULL;
77 stream.zfree = Z_NULL;
78 stream.opaque = Z_NULL;
79 stream.avail_in = 0;
80 stream.next_in = Z_NULL;
81
d0e615c3 82 /* This magic number apparently means "this is gzip". */
313546eb
LMI
83 if (inflateInit2 (&stream, 16 + MAX_WBITS) != Z_OK)
84 return Qnil;
85
313546eb
LMI
86 unwind_data.start = iend;
87 unwind_data.stream = &stream;
d0e615c3
PE
88 unwind_data.old_point = PT;
89
313546eb
LMI
90 record_unwind_protect_ptr (unwind_decompress, &unwind_data);
91
d0e615c3
PE
92 /* Insert the decompressed data at the end of the compressed data. */
93 SET_PT (iend);
313546eb 94
d0e615c3
PE
95 pos_byte = istart;
96
97 /* Keep calling 'inflate' until it reports an error or end-of-input. */
98 do
99 {
100 /* Maximum number of bytes that one 'inflate' call should read and write.
101 zlib requires that these values not exceed UINT_MAX.
102 Do not make avail_out too large, as that might unduly delay C-g. */
103 ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
104 ptrdiff_t avail_out = min (1 << 14, UINT_MAX);
105
106 ptrdiff_t decompressed;
107
108 if (GAP_SIZE < avail_out)
109 make_gap (avail_out - GAP_SIZE);
110 stream.next_in = BYTE_POS_ADDR (pos_byte);
111 stream.avail_in = avail_in;
112 stream.next_out = GPT_ADDR;
113 stream.avail_out = avail_out;
114 inflate_status = inflate (&stream, Z_NO_FLUSH);
115 pos_byte += avail_in - stream.avail_in;
116 decompressed = avail_out - stream.avail_out;
117 insert_from_gap (decompressed, decompressed, 0);
118 QUIT;
119 }
120 while (inflate_status == Z_OK);
313546eb 121
d0e615c3
PE
122 if (inflate_status != Z_STREAM_END)
123 return unbind_to (count, Qnil);
313546eb
LMI
124
125 unwind_data.start = 0;
313546eb 126
d0e615c3 127 /* Delete the compressed data. */
313546eb
LMI
128 del_range (istart, iend);
129
d0e615c3 130 return unbind_to (count, Qt);
313546eb
LMI
131}
132
133\f
134/***********************************************************************
135 Initialization
136 ***********************************************************************/
137void
138syms_of_decompress (void)
139{
140 defsubr (&Sdecompress_gzipped_region);
141}
142
143#endif /* HAVE_ZLIB */