root/src/decompress.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. init_zlib_functions
  2. accumulate_and_process_md5
  3. final_process_md5
  4. md5_gz_stream
  5. unwind_decompress
  6. DEFUN
  7. syms_of_decompress

     1 /* Interface to zlib.
     2    Copyright (C) 2013-2023 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 (at
     9 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 <https://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 "buffer.h"
    27 #include "composite.h"
    28 #include "md5.h"
    29 
    30 #include <verify.h>
    31 
    32 #ifdef WINDOWSNT
    33 # include <windows.h>
    34 # include "w32common.h"
    35 # include "w32.h"
    36 
    37 DEF_DLL_FN (int, inflateInit2_,
    38             (z_streamp strm, int windowBits, const char *version,
    39              int stream_size));
    40 DEF_DLL_FN (int, inflate, (z_streamp strm, int flush));
    41 DEF_DLL_FN (int, inflateEnd, (z_streamp strm));
    42 
    43 static bool zlib_initialized;
    44 
    45 static bool
    46 init_zlib_functions (void)
    47 {
    48   HMODULE library = w32_delayed_load (Qzlib);
    49 
    50   if (!library)
    51     return false;
    52 
    53   LOAD_DLL_FN (library, inflateInit2_);
    54   LOAD_DLL_FN (library, inflate);
    55   LOAD_DLL_FN (library, inflateEnd);
    56   return true;
    57 }
    58 
    59 # undef inflate
    60 # undef inflateEnd
    61 # undef inflateInit2_
    62 
    63 # define inflate fn_inflate
    64 # define inflateEnd fn_inflateEnd
    65 # define inflateInit2_ fn_inflateInit2_
    66 
    67 #endif  /* WINDOWSNT */
    68 
    69 
    70 #ifdef HAVE_NATIVE_COMP
    71 
    72 # define MD5_BLOCKSIZE 32768 /* From md5.c  */
    73 
    74 static char acc_buff[2 * MD5_BLOCKSIZE];
    75 static size_t acc_size;
    76 
    77 static void
    78 accumulate_and_process_md5 (void *data, size_t len, struct md5_ctx *ctxt)
    79 {
    80   eassert (len <= MD5_BLOCKSIZE);
    81   /* We may optimize this saving some of these memcpy/move using
    82      directly the outer buffers but so far don't bother.  */
    83   memcpy (acc_buff + acc_size, data, len);
    84   acc_size += len;
    85   if (acc_size >= MD5_BLOCKSIZE)
    86     {
    87       acc_size -= MD5_BLOCKSIZE;
    88       md5_process_block (acc_buff, MD5_BLOCKSIZE, ctxt);
    89       memmove (acc_buff, acc_buff + MD5_BLOCKSIZE, acc_size);
    90     }
    91 }
    92 
    93 static void
    94 final_process_md5 (struct md5_ctx *ctxt)
    95 {
    96   if (acc_size)
    97     {
    98       md5_process_bytes (acc_buff, acc_size, ctxt);
    99       acc_size = 0;
   100     }
   101 }
   102 
   103 int
   104 md5_gz_stream (FILE *source, void *resblock)
   105 {
   106   z_stream stream;
   107   unsigned char in[MD5_BLOCKSIZE];
   108   unsigned char out[MD5_BLOCKSIZE];
   109 
   110 # ifdef WINDOWSNT
   111   if (!zlib_initialized)
   112     zlib_initialized = init_zlib_functions ();
   113   if (!zlib_initialized)
   114     {
   115       message1 ("zlib library not found");
   116       return -1;
   117     }
   118 # endif
   119 
   120   eassert (!acc_size);
   121 
   122   struct md5_ctx ctx;
   123   md5_init_ctx (&ctx);
   124 
   125   /* allocate inflate state */
   126   stream.zalloc = Z_NULL;
   127   stream.zfree = Z_NULL;
   128   stream.opaque = Z_NULL;
   129   stream.avail_in = 0;
   130   stream.next_in = Z_NULL;
   131   int res = inflateInit2 (&stream, MAX_WBITS + 32);
   132   if (res != Z_OK)
   133     return -1;
   134 
   135   do {
   136     stream.avail_in = fread (in, 1, MD5_BLOCKSIZE, source);
   137     if (ferror (source)) {
   138       inflateEnd (&stream);
   139       return -1;
   140     }
   141     if (stream.avail_in == 0)
   142       break;
   143     stream.next_in = in;
   144 
   145     do {
   146       stream.avail_out = MD5_BLOCKSIZE;
   147       stream.next_out = out;
   148       res = inflate (&stream, Z_NO_FLUSH);
   149 
   150       if (res != Z_OK && res != Z_STREAM_END)
   151         return -1;
   152 
   153       accumulate_and_process_md5 (out, MD5_BLOCKSIZE - stream.avail_out, &ctx);
   154     } while (stream.avail_in && !stream.avail_out);
   155 
   156   } while (res != Z_STREAM_END);
   157 
   158   final_process_md5 (&ctx);
   159   inflateEnd (&stream);
   160 
   161   if (res != Z_STREAM_END)
   162     return -1;
   163 
   164   md5_finish_ctx (&ctx, resblock);
   165 
   166   return 0;
   167 }
   168 # undef MD5_BLOCKSIZE
   169 #endif
   170 
   171 
   172 
   173 struct decompress_unwind_data
   174 {
   175   ptrdiff_t old_point, orig, start, nbytes;
   176   z_stream *stream;
   177 };
   178 
   179 static void
   180 unwind_decompress (void *ddata)
   181 {
   182   struct decompress_unwind_data *data = ddata;
   183   inflateEnd (data->stream);
   184 
   185   /* Delete any uncompressed data already inserted on error, but
   186      without calling the change hooks.  */
   187   if (data->start)
   188     {
   189       del_range_2 (data->start, data->start, /* byte, char offsets the same */
   190                    data->start + data->nbytes, data->start + data->nbytes,
   191                    0);
   192       update_compositions (data->start, data->start, CHECK_HEAD);
   193       /* "Balance" the before-change-functions call, which would
   194          otherwise be left "hanging".  */
   195       signal_after_change (data->orig, data->start - data->orig,
   196                            data->start - data->orig);
   197     }
   198   /* Put point where it was, or if the buffer has shrunk because the
   199      compressed data is bigger than the uncompressed, at
   200      point-max.  */
   201   SET_PT (min (data->old_point, ZV));
   202 }
   203 
   204 DEFUN ("zlib-available-p", Fzlib_available_p, Szlib_available_p, 0, 0, 0,
   205        doc: /* Return t if zlib decompression is available in this instance of Emacs.  */)
   206      (void)
   207 {
   208 #ifdef WINDOWSNT
   209   Lisp_Object found = Fassq (Qzlib, Vlibrary_cache);
   210   if (CONSP (found))
   211     return XCDR (found);
   212   else
   213     {
   214       Lisp_Object status;
   215       zlib_initialized = init_zlib_functions ();
   216       status = zlib_initialized ? Qt : Qnil;
   217       Vlibrary_cache = Fcons (Fcons (Qzlib, status), Vlibrary_cache);
   218       return status;
   219     }
   220 #else
   221   return Qt;
   222 #endif
   223 }
   224 
   225 DEFUN ("zlib-decompress-region", Fzlib_decompress_region,
   226        Szlib_decompress_region,
   227        2, 3, 0,
   228        doc: /* Decompress a gzip- or zlib-compressed region.
   229 Replace the text in the region by the decompressed data.
   230 
   231 If optional parameter ALLOW-PARTIAL is nil or omitted, then on
   232 failure, return nil and leave the data in place.  Otherwise, return
   233 the number of bytes that were not decompressed and replace the region
   234 text by whatever data was successfully decompressed (similar to gzip).
   235 If decompression is completely successful return t.
   236 
   237 This function can be called only in unibyte buffers.  */)
   238   (Lisp_Object start, Lisp_Object end, Lisp_Object allow_partial)
   239 {
   240   ptrdiff_t istart, iend, pos_byte;
   241   z_stream stream;
   242   int inflate_status;
   243   struct decompress_unwind_data unwind_data;
   244   specpdl_ref count = SPECPDL_INDEX ();
   245 
   246   validate_region (&start, &end);
   247 
   248   if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
   249     error ("This function can be called only in unibyte buffers");
   250 
   251 #ifdef WINDOWSNT
   252   if (!zlib_initialized)
   253     zlib_initialized = init_zlib_functions ();
   254   if (!zlib_initialized)
   255     {
   256       message1 ("zlib library not found");
   257       return Qnil;
   258     }
   259 #endif
   260 
   261   /* This is a unibyte buffer, so character positions and bytes are
   262      the same.  */
   263   istart = XFIXNUM (start);
   264   iend = XFIXNUM (end);
   265 
   266   /* Do the following before manipulating the gap.  */
   267   modify_text (istart, iend);
   268 
   269   move_gap_both (iend, iend);
   270 
   271   stream.zalloc = Z_NULL;
   272   stream.zfree = Z_NULL;
   273   stream.opaque = Z_NULL;
   274   stream.avail_in = 0;
   275   stream.next_in = Z_NULL;
   276 
   277   /* The magic number 32 apparently means "autodetect both the gzip and
   278      zlib formats" according to zlib.h.  */
   279   if (inflateInit2 (&stream, MAX_WBITS + 32) != Z_OK)
   280     return Qnil;
   281 
   282   unwind_data.orig = istart;
   283   unwind_data.start = iend;
   284   unwind_data.stream = &stream;
   285   unwind_data.old_point = PT;
   286   unwind_data.nbytes = 0;
   287   record_unwind_protect_ptr (unwind_decompress, &unwind_data);
   288 
   289   /* Insert the decompressed data at the end of the compressed data.  */
   290   SET_PT (iend);
   291 
   292   pos_byte = istart;
   293 
   294   /* Keep calling 'inflate' until it reports an error or end-of-input.  */
   295   do
   296     {
   297       /* Maximum number of bytes that one 'inflate' call should read and write.
   298          Do not make avail_out too large, as that might unduly delay C-g.
   299          zlib requires that avail_in and avail_out not exceed UINT_MAX.  */
   300       ptrdiff_t avail_in = min (iend - pos_byte, UINT_MAX);
   301       int avail_out = 16 * 1024;
   302       int decompressed;
   303 
   304       if (GAP_SIZE < avail_out)
   305         make_gap (avail_out - GAP_SIZE);
   306       stream.next_in = BYTE_POS_ADDR (pos_byte);
   307       stream.avail_in = avail_in;
   308       stream.next_out = GPT_ADDR;
   309       stream.avail_out = avail_out;
   310       inflate_status = inflate (&stream, Z_NO_FLUSH);
   311       pos_byte += avail_in - stream.avail_in;
   312       decompressed = avail_out - stream.avail_out;
   313       insert_from_gap (decompressed, decompressed, 0);
   314       unwind_data.nbytes += decompressed;
   315       maybe_quit ();
   316     }
   317   while (inflate_status == Z_OK);
   318 
   319   Lisp_Object ret = Qt;
   320   if (inflate_status != Z_STREAM_END)
   321     {
   322       if (!NILP (allow_partial))
   323         ret = make_int (iend - pos_byte);
   324       else
   325         return unbind_to (count, Qnil);
   326     }
   327 
   328   unwind_data.start = 0;
   329 
   330   /* Delete the compressed data.  */
   331   del_range_2 (istart, istart, /* byte and char offsets are the same */
   332                iend, iend, 0);
   333 
   334   signal_after_change (istart, iend - istart, unwind_data.nbytes);
   335   update_compositions (istart, istart, CHECK_HEAD);
   336 
   337   return unbind_to (count, ret);
   338 }
   339 
   340 
   341 /***********************************************************************
   342                             Initialization
   343  ***********************************************************************/
   344 void
   345 syms_of_decompress (void)
   346 {
   347   defsubr (&Szlib_decompress_region);
   348   defsubr (&Szlib_available_p);
   349 }
   350 
   351 #endif /* HAVE_ZLIB */

/* [<][>][^][v][top][bottom][index][help] */