diff options
| author | Xue Fuqiao | 2013-09-04 08:39:34 +0800 |
|---|---|---|
| committer | Xue Fuqiao | 2013-09-04 08:39:34 +0800 |
| commit | adf2fc4a01efe77d73cd52bc9173914ed56ff531 (patch) | |
| tree | a5a280a5554a7bffeaf94fccae29fa3ac1a5d066 /src/decompress.c | |
| parent | 63191d9f2043d2e67657e85a7b3842805dd1dad6 (diff) | |
| parent | 38726039b77db432989fed106c88e9f1aa463281 (diff) | |
| download | emacs-adf2fc4a01efe77d73cd52bc9173914ed56ff531.tar.gz emacs-adf2fc4a01efe77d73cd52bc9173914ed56ff531.zip | |
Merge from mainline.
Diffstat (limited to 'src/decompress.c')
| -rw-r--r-- | src/decompress.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/src/decompress.c b/src/decompress.c new file mode 100644 index 00000000000..dc9f4b72d93 --- /dev/null +++ b/src/decompress.c | |||
| @@ -0,0 +1,232 @@ | |||
| 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 | |||
| 86 | struct decompress_unwind_data | ||
| 87 | { | ||
| 88 | ptrdiff_t old_point, start, nbytes; | ||
| 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 on error. */ | ||
| 99 | if (data->start) | ||
| 100 | del_range (data->start, data->start + data->nbytes); | ||
| 101 | |||
| 102 | /* Put point where it was, or if the buffer has shrunk because the | ||
| 103 | compressed data is bigger than the uncompressed, at | ||
| 104 | point-max. */ | ||
| 105 | SET_PT (min (data->old_point, ZV)); | ||
| 106 | } | ||
| 107 | |||
| 108 | DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0, | ||
| 109 | doc: /* Return t if zlib decompression is available in this instance of Emacs. */) | ||
| 110 | (void) | ||
| 111 | { | ||
| 112 | #ifdef WINDOWSNT | ||
| 113 | Lisp_Object found = Fassq (Qzlib_dll, Vlibrary_cache); | ||
| 114 | if (CONSP (found)) | ||
| 115 | return XCDR (found); | ||
| 116 | else | ||
| 117 | { | ||
| 118 | Lisp_Object status; | ||
| 119 | zlib_initialized = init_zlib_functions (); | ||
| 120 | status = zlib_initialized ? Qt : Qnil; | ||
| 121 | Vlibrary_cache = Fcons (Fcons (Qzlib_dll, status), Vlibrary_cache); | ||
| 122 | return status; | ||
| 123 | } | ||
| 124 | #else | ||
| 125 | return Qt; | ||
| 126 | #endif | ||
| 127 | } | ||
| 128 | |||
| 129 | DEFUN ("zlib-decompress-region", Fzlib_decompress_region, | ||
| 130 | Szlib_decompress_region, | ||
| 131 | 2, 2, 0, | ||
| 132 | doc: /* Decompress a gzip- or zlib-compressed region. | ||
| 133 | Replace the text in the region by the decompressed data. | ||
| 134 | On failure, return nil and leave the data in place. | ||
| 135 | This function can be called only in unibyte buffers. */) | ||
| 136 | (Lisp_Object start, Lisp_Object end) | ||
| 137 | { | ||
| 138 | ptrdiff_t istart, iend, pos_byte; | ||
| 139 | z_stream stream; | ||
| 140 | int inflate_status; | ||
| 141 | struct decompress_unwind_data unwind_data; | ||
| 142 | ptrdiff_t count = SPECPDL_INDEX (); | ||
| 143 | |||
| 144 | validate_region (&start, &end); | ||
| 145 | |||
| 146 | if (! NILP (BVAR (current_buffer, enable_multibyte_characters))) | ||
| 147 | error ("This function can be called only in unibyte buffers"); | ||
| 148 | |||
| 149 | #ifdef WINDOWSNT | ||
| 150 | if (!zlib_initialized) | ||
| 151 | zlib_initialized = init_zlib_functions (); | ||
| 152 | if (!zlib_initialized) | ||
| 153 | return Qnil; | ||
| 154 | #endif | ||
| 155 | |||
| 156 | /* This is a unibyte buffer, so character positions and bytes are | ||
| 157 | the same. */ | ||
| 158 | istart = XINT (start); | ||
| 159 | iend = XINT (end); | ||
| 160 | move_gap_both (iend, iend); | ||
| 161 | |||
| 162 | stream.zalloc = Z_NULL; | ||
| 163 | stream.zfree = Z_NULL; | ||
| 164 | stream.opaque = Z_NULL; | ||
| 165 | stream.avail_in = 0; | ||
| 166 | stream.next_in = Z_NULL; | ||
| 167 | |||
| 168 | /* The magic number 32 apparently means "autodetect both the gzip and | ||
| 169 | zlib formats" according to zlib.h. */ | ||
| 170 | if (fn_inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK) | ||
| 171 | return Qnil; | ||
| 172 | |||
| 173 | unwind_data.start = iend; | ||
| 174 | unwind_data.stream = &stream; | ||
| 175 | unwind_data.old_point = PT; | ||
| 176 | unwind_data.nbytes = 0; | ||
| 177 | record_unwind_protect_ptr (unwind_decompress, &unwind_data); | ||
| 178 | |||
| 179 | /* Insert the decompressed data at the end of the compressed data. */ | ||
| 180 | SET_PT (iend); | ||
| 181 | |||
| 182 | pos_byte = istart; | ||
| 183 | |||
| 184 | /* Keep calling 'inflate' until it reports an error or end-of-input. */ | ||
| 185 | do | ||
| 186 | { | ||
| 187 | /* Maximum number of bytes that one 'inflate' call should read and write. | ||
| 188 | Do not make avail_out too large, as that might unduly delay C-g. | ||
| 189 | zlib requires that avail_in and avail_out not exceed UINT_MAX. */ | ||
| 190 | ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX); | ||
| 191 | int avail_out = 16 * 1024; | ||
| 192 | int decompressed; | ||
| 193 | |||
| 194 | if (GAP_SIZE < avail_out) | ||
| 195 | make_gap (avail_out - GAP_SIZE); | ||
| 196 | stream.next_in = BYTE_POS_ADDR (pos_byte); | ||
| 197 | stream.avail_in = avail_in; | ||
| 198 | stream.next_out = GPT_ADDR; | ||
| 199 | stream.avail_out = avail_out; | ||
| 200 | inflate_status = fn_inflate (&stream, Z_NO_FLUSH); | ||
| 201 | pos_byte += avail_in - stream.avail_in; | ||
| 202 | decompressed = avail_out - stream.avail_out; | ||
| 203 | insert_from_gap (decompressed, decompressed, 0); | ||
| 204 | unwind_data.nbytes += decompressed; | ||
| 205 | QUIT; | ||
| 206 | } | ||
| 207 | while (inflate_status == Z_OK); | ||
| 208 | |||
| 209 | if (inflate_status != Z_STREAM_END) | ||
| 210 | return unbind_to (count, Qnil); | ||
| 211 | |||
| 212 | unwind_data.start = 0; | ||
| 213 | |||
| 214 | /* Delete the compressed data. */ | ||
| 215 | del_range (istart, iend); | ||
| 216 | |||
| 217 | return unbind_to (count, Qt); | ||
| 218 | } | ||
| 219 | |||
| 220 | |||
| 221 | /*********************************************************************** | ||
| 222 | Initialization | ||
| 223 | ***********************************************************************/ | ||
| 224 | void | ||
| 225 | syms_of_decompress (void) | ||
| 226 | { | ||
| 227 | DEFSYM (Qzlib_dll, "zlib"); | ||
| 228 | defsubr (&Szlib_decompress_region); | ||
| 229 | defsubr (&Szlib_available_p); | ||
| 230 | } | ||
| 231 | |||
| 232 | #endif /* HAVE_ZLIB */ | ||