aboutsummaryrefslogtreecommitdiffstats
path: root/src/decompress.c
diff options
context:
space:
mode:
authorPaul Eggert2013-08-11 16:09:26 -0700
committerPaul Eggert2013-08-11 16:09:26 -0700
commitd0e615c324db994c8ac0ea2f0725dfc7b6f73f53 (patch)
tree03b58c64ab3306c78dbaafcaed0e93e5e103949c /src/decompress.c
parent8d28d0ac2af6f38abb588efb9f673d0e74768e8a (diff)
downloademacs-d0e615c324db994c8ac0ea2f0725dfc7b6f73f53.tar.gz
emacs-d0e615c324db994c8ac0ea2f0725dfc7b6f73f53.zip
* decompress.c: Fix bugs with large buffers and weird inputs.
Tune a bit. Reindent as per usual Emacs style. (BUFFER_SIZE): Remove. (Fdecompress_gzipped_region): Do not mishandle input buffers with more than UINT_MAX bytes. Decompress into the gap instead of into an auto buffer, as this should avoid copying. Return nil if 'inflate' returns Z_NEED_DICT, as we have no dictionary. Do not set immediate_quit; we shouldn't trust zlib code that much.
Diffstat (limited to 'src/decompress.c')
-rw-r--r--src/decompress.c100
1 files changed, 53 insertions, 47 deletions
diff --git a/src/decompress.c b/src/decompress.c
index a6323a843e9..866f4f51516 100644
--- a/src/decompress.c
+++ b/src/decompress.c
@@ -27,48 +27,48 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
27#include "buffer.h" 27#include "buffer.h"
28 28
29 29
30#define BUFFER_SIZE 16384 30struct decompress_unwind_data
31 31{
32struct decompress_unwind_data {
33 ptrdiff_t old_point, start; 32 ptrdiff_t old_point, start;
34 z_stream *stream; 33 z_stream *stream;
35}; 34};
36 35
37static void 36static void
38unwind_decompress (void *ddata) { 37unwind_decompress (void *ddata)
38{
39 struct decompress_unwind_data *data = ddata; 39 struct decompress_unwind_data *data = ddata;
40 inflateEnd (data->stream); 40 inflateEnd (data->stream);
41 /* Delete any uncompressed data already inserted and restore 41
42 point. */ 42 /* Delete any uncompressed data already inserted and restore point. */
43 if (data->start) { 43 if (data->start)
44 del_range (data->start, PT); 44 {
45 SET_PT (data->old_point); 45 del_range (data->start, PT);
46 } 46 SET_PT (data->old_point);
47 }
47} 48}
48 49
49DEFUN ("decompress-gzipped-region", Fdecompress_gzipped_region, 50DEFUN ("decompress-gzipped-region", Fdecompress_gzipped_region,
50 Sdecompress_gzipped_region, 51 Sdecompress_gzipped_region,
51 2, 2, 0, 52 2, 2, 0,
52 doc: /* Decompress a gzip-compressed region. 53 doc: /* Decompress a gzip-compressed region.
53The text in the region will be replaced by the decompressed data. 54Replace the text in the region by the decompressed data.
54On failure, nil is returned and the data is left in place. 55On failure, return nil and leave the data in place.
55This function can only be called in unibyte buffers.*/) 56This function can be called only in unibyte buffers. */)
56 (Lisp_Object start, Lisp_Object end) 57 (Lisp_Object start, Lisp_Object end)
57{ 58{
58 ptrdiff_t istart, iend, point = PT; 59 ptrdiff_t istart, iend, pos_byte;
59 z_stream stream; 60 z_stream stream;
60 int decompressed; 61 int inflate_status;
61 char out[16384];
62 struct decompress_unwind_data unwind_data; 62 struct decompress_unwind_data unwind_data;
63 ptrdiff_t count = SPECPDL_INDEX (); 63 ptrdiff_t count = SPECPDL_INDEX ();
64 64
65 validate_region (&start, &end); 65 validate_region (&start, &end);
66 66
67 if (! NILP (BVAR (current_buffer, enable_multibyte_characters))) 67 if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
68 error ("This function can only be called in unibyte buffers"); 68 error ("This function can be called only in unibyte buffers");
69 69
70 /* This is a unibyte buffer, so character positions and bytes are 70 /* This is a unibyte buffer, so character positions and bytes are
71 the same. */ 71 the same. */
72 istart = XINT (start); 72 istart = XINT (start);
73 iend = XINT (end); 73 iend = XINT (end);
74 move_gap_both (iend, iend); 74 move_gap_both (iend, iend);
@@ -79,49 +79,55 @@ This function can only be called in unibyte buffers.*/)
79 stream.avail_in = 0; 79 stream.avail_in = 0;
80 stream.next_in = Z_NULL; 80 stream.next_in = Z_NULL;
81 81
82 /* This magic number apparently means "this is gzip". */ 82 /* This magic number apparently means "this is gzip". */
83 if (inflateInit2 (&stream, 16 + MAX_WBITS) != Z_OK) 83 if (inflateInit2 (&stream, 16 + MAX_WBITS) != Z_OK)
84 return Qnil; 84 return Qnil;
85 85
86 /* We're inserting the decompressed data at the end of the
87 compressed data. */
88 SET_PT (iend);
89
90 stream.avail_in = iend - istart;
91 stream.next_in = (char *) BYTE_POS_ADDR (istart);
92
93 unwind_data.start = iend; 86 unwind_data.start = iend;
94 unwind_data.stream = &stream; 87 unwind_data.stream = &stream;
95 unwind_data.old_point = point; 88 unwind_data.old_point = PT;
89
96 record_unwind_protect_ptr (unwind_decompress, &unwind_data); 90 record_unwind_protect_ptr (unwind_decompress, &unwind_data);
97 91
98 immediate_quit = 1; 92 /* Insert the decompressed data at the end of the compressed data. */
99 93 SET_PT (iend);
100 /* Run inflate() on input until the output buffer isn't full. */
101 do {
102 int result;
103 stream.avail_out = BUFFER_SIZE;
104 stream.next_out = out;
105 result = inflate (&stream, Z_NO_FLUSH);
106 if (result < 0) {
107 unbind_to (count, Qnil);
108 return Qnil;
109 }
110 94
111 decompressed = BUFFER_SIZE - stream.avail_out; 95 pos_byte = istart;
112 insert_1_both (out, decompressed, decompressed, 0, 0, 0); 96
113 QUIT; 97 /* Keep calling 'inflate' until it reports an error or end-of-input. */
114 } while (stream.avail_out == 0); 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);
115 121
116 immediate_quit = 0; 122 if (inflate_status != Z_STREAM_END)
123 return unbind_to (count, Qnil);
117 124
118 unwind_data.start = 0; 125 unwind_data.start = 0;
119 unbind_to (count, Qnil);
120 126
121 /* Delete the compressed data. */ 127 /* Delete the compressed data. */
122 del_range (istart, iend); 128 del_range (istart, iend);
123 129
124 return Qt; 130 return unbind_to (count, Qt);
125} 131}
126 132
127 133