decompress.c 6.12 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/* Interface to zlib.
   Copyright (C) 2013 Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */

#include <config.h>

#ifdef HAVE_ZLIB

#include <zlib.h>

#include "lisp.h"
#include "character.h"
#include "buffer.h"

29 30
#include <verify.h>

31 32 33 34 35 36 37 38 39 40 41 42
static Lisp_Object Qzlib_dll;

#ifdef WINDOWSNT
#include <windows.h>
#include "w32.h"

/* Macro for defining functions that will be loaded from the zlib DLL.  */
#define DEF_ZLIB_FN(rettype,func,args) static rettype (FAR CDECL *fn_##func)args

/* Macro for loading zlib functions from the library.  */
#define LOAD_ZLIB_FN(lib,func) {					\
    fn_##func = (void *) GetProcAddress (lib, #func);			\
43
    if (!fn_##func) return false;					\
44 45 46 47 48 49 50 51 52 53 54
  }

DEF_ZLIB_FN (int, inflateInit2_,
	     (z_streamp strm, int  windowBits, const char *version, int stream_size));

DEF_ZLIB_FN (int, inflate,
	     (z_streamp strm, int flush));

DEF_ZLIB_FN (int, inflateEnd,
	     (z_streamp strm));

55 56
static bool zlib_initialized;

57 58 59 60 61 62 63 64
static bool
init_zlib_functions (void)
{
  HMODULE library = w32_delayed_load (Qzlib_dll);

  if (!library)
    {
      message1 ("zlib library not found");
65
      return false;
66 67 68 69 70
    }

  LOAD_ZLIB_FN (library, inflateInit2_);
  LOAD_ZLIB_FN (library, inflate);
  LOAD_ZLIB_FN (library, inflateEnd);
71
  return true;
72 73
}

Paul Eggert's avatar
Paul Eggert committed
74 75 76
#define fn_inflateInit2(strm, windowBits) \
        fn_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))

77 78
#else /* !WINDOWSNT */

Paul Eggert's avatar
Paul Eggert committed
79
#define fn_inflateInit2		inflateInit2
80 81 82 83 84
#define fn_inflate		inflate
#define fn_inflateEnd		inflateEnd

#endif	/* WINDOWSNT */

85

86 87
struct decompress_unwind_data
{
88 89 90 91 92
  ptrdiff_t old_point, start;
  z_stream *stream;
};

static void
93 94
unwind_decompress (void *ddata)
{
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 101 102 103 104 105
    del_range (data->start, PT);

  /* Put point where it was, or if the buffer has shrunk because the
     compressed data is bigger than the uncompressed, at
     point-max.  */
  SET_PT (min (data->old_point, ZV));
106 107
}

108 109 110 111 112 113 114 115 116 117 118
DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0,
       doc: /* Return t if zlib decompression is available in this instance of Emacs.  */)
     (void)
{
#ifdef WINDOWSNT
  Lisp_Object found = Fassq (Qzlib_dll, Vlibrary_cache);
  if (CONSP (found))
    return XCDR (found);
  else
    {
      Lisp_Object status;
119 120
      zlib_initialized = init_zlib_functions ();
      status = zlib_initialized ? Qt : Qnil;
121 122 123 124 125 126 127 128
      Vlibrary_cache = Fcons (Fcons (Qzlib_dll, status), Vlibrary_cache);
      return status;
    }
#else
  return Qt;
#endif
}

129 130
DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
       Szlib_decompress_region,
131
       2, 2, 0,
132
       doc: /* Decompress a gzip- or zlib-compressed region.
133 134 135
Replace the text in the region by the decompressed data.
On failure, return nil and leave the data in place.
This function can be called only in unibyte buffers.  */)
136 137
  (Lisp_Object start, Lisp_Object end)
{
138
  ptrdiff_t istart, iend, pos_byte;
139
  z_stream stream;
140
  int inflate_status;
141 142 143 144 145 146
  struct decompress_unwind_data unwind_data;
  ptrdiff_t count = SPECPDL_INDEX ();

  validate_region (&start, &end);

  if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
147
    error ("This function can be called only in unibyte buffers");
148

149 150 151
#ifdef WINDOWSNT
  if (!zlib_initialized)
    zlib_initialized = init_zlib_functions ();
152 153
  if (!zlib_initialized)
    return Qnil;
154 155
#endif

156
  /* This is a unibyte buffer, so character positions and bytes are
157
     the same.  */
158 159
  istart = XINT (start);
  iend = XINT (end);
160
  move_gap_both (iend, iend);
161 162 163 164 165 166 167

  stream.zalloc = Z_NULL;
  stream.zfree = Z_NULL;
  stream.opaque = Z_NULL;
  stream.avail_in = 0;
  stream.next_in = Z_NULL;

Paul Eggert's avatar
Paul Eggert committed
168
  /* The magic number 32 apparently means "autodetect both the gzip and
169 170
     zlib formats" according to zlib.h.  */
  if (fn_inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK)
171 172 173 174
    return Qnil;

  unwind_data.start = iend;
  unwind_data.stream = &stream;
175 176
  unwind_data.old_point = PT;

177 178
  record_unwind_protect_ptr (unwind_decompress, &unwind_data);

179 180
  /* Insert the decompressed data at the end of the compressed data.  */
  SET_PT (iend);
181

182 183 184 185 186 187
  pos_byte = istart;

  /* Keep calling 'inflate' until it reports an error or end-of-input.  */
  do
    {
      /* 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 192
      int avail_out = 16 * 1024;
      int decompressed;
193 194 195 196 197 198 199

      if (GAP_SIZE < avail_out)
	make_gap (avail_out - GAP_SIZE);
      stream.next_in = BYTE_POS_ADDR (pos_byte);
      stream.avail_in = avail_in;
      stream.next_out = GPT_ADDR;
      stream.avail_out = avail_out;
200
      inflate_status = fn_inflate (&stream, Z_NO_FLUSH);
201 202 203 204 205 206
      pos_byte += avail_in - stream.avail_in;
      decompressed = avail_out - stream.avail_out;
      insert_from_gap (decompressed, decompressed, 0);
      QUIT;
    }
  while (inflate_status == Z_OK);
207

208 209
  if (inflate_status != Z_STREAM_END)
    return unbind_to (count, Qnil);
210 211 212

  unwind_data.start = 0;

213
  /* Delete the compressed data.  */
214 215
  del_range (istart, iend);

216
  return unbind_to (count, Qt);
217 218 219 220 221 222 223 224 225
}


/***********************************************************************
			    Initialization
 ***********************************************************************/
void
syms_of_decompress (void)
{
226
  DEFSYM (Qzlib_dll, "zlib");
227
  defsubr (&Szlib_decompress_region);
228
  defsubr (&Szlib_available_p);
229 230 231
}

#endif /* HAVE_ZLIB */