/* zip.c -- IO on .zip files using zlib
   Version 1.01, May 8th, 2004

   Slightly modified by StatMat to expose internal data
   28th March, 2005

   Copyright (C) 1998-2004 Gilles Vollant

   Read zip.h for more info
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "zlib.h"
#include "zip.h"

#ifdef STDC
#  include <stddef.h>
#  include <string.h>
#  include <stdlib.h>
#endif
#ifdef NO_ERRNO_H
extern int errno;
#else
#   include <errno.h>
#endif


#ifndef local
#  define local static
#endif
/* compile with -Dlocal if your debugger can't find static symbols */

#ifndef VERSIONMADEBY
# define VERSIONMADEBY   (0x0) /* platform depedent */
#endif

#ifndef Z_MAXFILENAMEINZIP
#define Z_MAXFILENAMEINZIP (256)
#endif

#ifndef ALLOC
# define ALLOC(size) (malloc(size))
#endif
#ifndef TRYFREE
# define TRYFREE(p) {if (p) free(p);}
#endif

/*
#define SIZECENTRALDIRITEM (0x2e)
#define SIZEZIPLOCALHEADER (0x1e)
*/

/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */

#ifndef SEEK_CUR
#define SEEK_CUR    1
#endif

#ifndef SEEK_END
#define SEEK_END    2
#endif

#ifndef SEEK_SET
#define SEEK_SET    0
#endif

#ifndef DEF_MEM_LEVEL
#if MAX_MEM_LEVEL >= 8
#  define DEF_MEM_LEVEL 8
#else
#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
#endif
#endif
const char zip_copyright[] =
  " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";

#ifndef NOCRYPT
#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED
#include "crypt.h"
#endif

local linkedlist_datablock_internal *
allocate_new_datablock ()
{
  linkedlist_datablock_internal *ldi;
  ldi = (linkedlist_datablock_internal *)
    ALLOC (sizeof (linkedlist_datablock_internal));
  if (ldi != NULL)
  {
    ldi->next_datablock = NULL;
    ldi->filled_in_this_block = 0;
    ldi->avail_in_this_block = SIZEDATA_INDATABLOCK;
  }
  return ldi;
}

local void
free_datablock (ldi)
     linkedlist_datablock_internal *ldi;
{
  while (ldi != NULL)
  {
    linkedlist_datablock_internal *ldinext = ldi->next_datablock;
    TRYFREE (ldi);
    ldi = ldinext;
  }
}

local void
init_linkedlist (ll)
     linkedlist_data *ll;
{
  ll->first_block = ll->last_block = NULL;
}

local void
free_linkedlist (ll)
     linkedlist_data *ll;
{
  free_datablock (ll->first_block);
  ll->first_block = ll->last_block = NULL;
}


local int
add_data_in_datablock (ll, buf, len)
     linkedlist_data *ll;
     const void *buf;
     uLong len;
{
  linkedlist_datablock_internal *ldi;
  const unsigned char *from_copy;

  if (ll == NULL)
    return ZIP_INTERNALERROR;

  if (ll->last_block == NULL)
  {
    ll->first_block = ll->last_block = allocate_new_datablock ();
    if (ll->first_block == NULL)
      return ZIP_INTERNALERROR;
  }

  ldi = ll->last_block;
  from_copy = (unsigned char *) buf;

  while (len > 0)
  {
    uInt copy_this;
    uInt i;
    unsigned char *to_copy;

    if (ldi->avail_in_this_block == 0)
    {
      ldi->next_datablock = allocate_new_datablock ();
      if (ldi->next_datablock == NULL)
        return ZIP_INTERNALERROR;
      ldi = ldi->next_datablock;
      ll->last_block = ldi;
    }

    if (ldi->avail_in_this_block < len)
      copy_this = (uInt) ldi->avail_in_this_block;
    else
      copy_this = (uInt) len;

    to_copy = &(ldi->data[ldi->filled_in_this_block]);

    for (i = 0; i < copy_this; i++)
      *(to_copy + i) = *(from_copy + i);

    ldi->filled_in_this_block += copy_this;
    ldi->avail_in_this_block -= copy_this;
    from_copy += copy_this;
    len -= copy_this;
  }
  return ZIP_OK;
}



/****************************************************************************/

#ifndef NO_ADDFILEINEXISTINGZIP
/* ===========================================================================
   Inputs a long in LSB order to the given file
   nbByte == 1, 2 or 4 (byte, short or long)
*/

local int ziplocal_putValue OF ((const zlib_filefunc_def * pzlib_filefunc_def,
                                 voidpf filestream, uLong x, int nbByte));
local int
ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte)
     const zlib_filefunc_def *pzlib_filefunc_def;
     voidpf filestream;
     uLong x;
     int nbByte;
{
  unsigned char buf[4];
  int n;
  for (n = 0; n < nbByte; n++)
  {
    buf[n] = (unsigned char) (x & 0xff);
    x >>= 8;
  }
  if (x != 0)
  { /* data overflow - hack for ZIP64 (X Roche) */
    for (n = 0; n < nbByte; n++)
    {
      buf[n] = 0xff;
    }
  }

  if (ZWRITE (*pzlib_filefunc_def, filestream, buf, nbByte) != (uLong) nbByte)
    return ZIP_ERRNO;
  else
    return ZIP_OK;
}

local void ziplocal_putValue_inmemory OF ((void *dest, uLong x, int nbByte));
local void
ziplocal_putValue_inmemory (dest, x, nbByte)
     void *dest;
     uLong x;
     int nbByte;
{
  unsigned char *buf = (unsigned char *) dest;
  int n;
  for (n = 0; n < nbByte; n++)
  {
    buf[n] = (unsigned char) (x & 0xff);
    x >>= 8;
  }

  if (x != 0)
  { /* data overflow - hack for ZIP64 */
    for (n = 0; n < nbByte; n++)
    {
      buf[n] = 0xff;
    }
  }
}

/****************************************************************************/


local uLong
ziplocal_TmzDateToDosDate (ptm, dosDate)
     const tm_zip *ptm;
     uLong dosDate;
{
  uLong year = (uLong) ptm->tm_year;
  if (year > 1980)
    year -= 1980;
  else if (year > 80)
    year -= 80;
  return
    (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon + 1)) +
              (512 * year)) << 16) | ((ptm->tm_sec / 2) + (32 * ptm->tm_min) +
                                      (2048 * (uLong) ptm->tm_hour));
}


/****************************************************************************/

local int ziplocal_getByte OF ((const zlib_filefunc_def * pzlib_filefunc_def,
                                voidpf filestream, int *pi));

local int
ziplocal_getByte (pzlib_filefunc_def, filestream, pi)
     const zlib_filefunc_def *pzlib_filefunc_def;
     voidpf filestream;
     int *pi;
{
  unsigned char c;
  int err = (int) ZREAD (*pzlib_filefunc_def, filestream, &c, 1);
  if (err == 1)
  {
    *pi = (int) c;
    return ZIP_OK;
  }
  else
  {
    if (ZERROR (*pzlib_filefunc_def, filestream))
      return ZIP_ERRNO;
    else
      return ZIP_EOF;
  }
}


/* ===========================================================================
   Reads a long in LSB order from the given gz_stream. Sets
*/
local int ziplocal_getShort OF ((const zlib_filefunc_def * pzlib_filefunc_def,
                                 voidpf filestream, uLong * pX));

local int
ziplocal_getShort (pzlib_filefunc_def, filestream, pX)
     const zlib_filefunc_def *pzlib_filefunc_def;
     voidpf filestream;
     uLong *pX;
{
  uLong x;
  int i;
  int err;

  err = ziplocal_getByte (pzlib_filefunc_def, filestream, &i);
  x = (uLong) i;

  if (err == ZIP_OK)
    err = ziplocal_getByte (pzlib_filefunc_def, filestream, &i);
  x += ((uLong) i) << 8;

  if (err == ZIP_OK)
    *pX = x;
  else
    *pX = 0;
  return err;
}

local int ziplocal_getLong OF ((const zlib_filefunc_def * pzlib_filefunc_def,
                                voidpf filestream, uLong * pX));

local int
ziplocal_getLong (pzlib_filefunc_def, filestream, pX)
     const zlib_filefunc_def *pzlib_filefunc_def;
     voidpf filestream;
     uLong *pX;
{
  uLong x;
  int i;
  int err;

  err = ziplocal_getByte (pzlib_filefunc_def, filestream, &i);
  x = (uLong) i;

  if (err == ZIP_OK)
    err = ziplocal_getByte (pzlib_filefunc_def, filestream, &i);
  x += ((uLong) i) << 8;

  if (err == ZIP_OK)
    err = ziplocal_getByte (pzlib_filefunc_def, filestream, &i);
  x += ((uLong) i) << 16;

  if (err == ZIP_OK)
    err = ziplocal_getByte (pzlib_filefunc_def, filestream, &i);
  x += ((uLong) i) << 24;

  if (err == ZIP_OK)
    *pX = x;
  else
    *pX = 0;
  return err;
}

#ifndef BUFREADCOMMENT
#define BUFREADCOMMENT (0x400)
#endif
/*
  Locate the Central directory of a zipfile (at the end, just before
    the global comment)
*/
local uLong ziplocal_SearchCentralDir
OF ((const zlib_filefunc_def * pzlib_filefunc_def, voidpf filestream));

local uLong
ziplocal_SearchCentralDir (pzlib_filefunc_def, filestream)
     const zlib_filefunc_def *pzlib_filefunc_def;
     voidpf filestream;
{
  unsigned char *buf;
  uLong uSizeFile;
  uLong uBackRead;
  uLong uMaxBack = 0xffff;      /* maximum size of global comment */
  uLong uPosFound = 0;

  if (ZSEEK (*pzlib_filefunc_def, filestream, 0, ZLIB_FILEFUNC_SEEK_END) != 0)
    return 0;


  uSizeFile = ZTELL (*pzlib_filefunc_def, filestream);

  if (uMaxBack > uSizeFile)
    uMaxBack = uSizeFile;

  buf = (unsigned char *) ALLOC (BUFREADCOMMENT + 4);
  if (buf == NULL)
    return 0;

  uBackRead = 4;
  while (uBackRead < uMaxBack)
  {
    uLong uReadSize, uReadPos;
    int i;
    if (uBackRead + BUFREADCOMMENT > uMaxBack)
      uBackRead = uMaxBack;
    else
      uBackRead += BUFREADCOMMENT;
    uReadPos = uSizeFile - uBackRead;

    uReadSize = ((BUFREADCOMMENT + 4) < (uSizeFile - uReadPos)) ?
      (BUFREADCOMMENT + 4) : (uSizeFile - uReadPos);
    if (ZSEEK
        (*pzlib_filefunc_def, filestream, uReadPos,
         ZLIB_FILEFUNC_SEEK_SET) != 0)
      break;

    if (ZREAD (*pzlib_filefunc_def, filestream, buf, uReadSize) != uReadSize)
      break;

    for (i = (int) uReadSize - 3; (i--) > 0;)
      if (((*(buf + i)) == 0x50) && ((*(buf + i + 1)) == 0x4b) &&
          ((*(buf + i + 2)) == 0x05) && ((*(buf + i + 3)) == 0x06))
      {
        uPosFound = uReadPos + i;
        break;
      }

    if (uPosFound != 0)
      break;
  }
  TRYFREE (buf);
  return uPosFound;
}
#endif /* !NO_ADDFILEINEXISTINGZIP */

/************************************************************/
extern zipFile ZEXPORT
zipOpen2 (pathname, append, globalcomment, pzlib_filefunc_def)
     const char *pathname;
     int append;
     zipcharpc *globalcomment;
     zlib_filefunc_def *pzlib_filefunc_def;
{
  zip_internal ziinit;
  zip_internal *zi;
  int err = ZIP_OK;


  if (pzlib_filefunc_def == NULL)
    fill_fopen_filefunc (&ziinit.z_filefunc);
  else
    ziinit.z_filefunc = *pzlib_filefunc_def;

  ziinit.filestream = (*(ziinit.z_filefunc.zopen_file))
    (ziinit.z_filefunc.opaque,
     pathname,
     (append == APPEND_STATUS_CREATE) ?
     (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE |
      ZLIB_FILEFUNC_MODE_CREATE) : (ZLIB_FILEFUNC_MODE_READ |
                                    ZLIB_FILEFUNC_MODE_WRITE |
                                    ZLIB_FILEFUNC_MODE_EXISTING));

  if (ziinit.filestream == NULL)
    return NULL;
  ziinit.begin_pos = ZTELL (ziinit.z_filefunc, ziinit.filestream);
  ziinit.in_opened_file_inzip = 0;
  ziinit.ci.stream_initialised = 0;
  ziinit.number_entry = 0;
  ziinit.add_position_when_writting_offset = 0;
  init_linkedlist (&(ziinit.central_dir));


  zi = (zip_internal *) ALLOC (sizeof (zip_internal));
  if (zi == NULL)
  {
    ZCLOSE (ziinit.z_filefunc, ziinit.filestream);
    return NULL;
  }

  /* now we add file in a zipfile */
#    ifndef NO_ADDFILEINEXISTINGZIP
  if (append == APPEND_STATUS_ADDINZIP)
  {
    uLong byte_before_the_zipfile; /* byte before the zipfile, (>0 for sfx) */

    uLong size_central_dir;     /* size of the central directory */
    uLong offset_central_dir;   /* offset of start of central directory */
    uLong central_pos, uL;

    uLong number_disk;          /* number of the current dist, used for
                                   spaning ZIP, unsupported, always 0 */
    uLong number_disk_with_CD;  /* number the the disk with central dir, used
                                   for spaning ZIP, unsupported, always 0 */
    uLong number_entry;
    uLong number_entry_CD;      /* total number of entries in the central dir
                                   (same than number_entry on nospan) */
    uLong size_comment;

    central_pos =
      ziplocal_SearchCentralDir (&ziinit.z_filefunc, ziinit.filestream);
    if (central_pos == 0)
      err = ZIP_ERRNO;

    if (ZSEEK (ziinit.z_filefunc, ziinit.filestream,
               central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0)
      err = ZIP_ERRNO;

    /* the signature, already checked */
    if (ziplocal_getLong (&ziinit.z_filefunc, ziinit.filestream, &uL) !=
        ZIP_OK)
      err = ZIP_ERRNO;

    /* number of this disk */
    if (ziplocal_getShort
        (&ziinit.z_filefunc, ziinit.filestream, &number_disk) != ZIP_OK)
      err = ZIP_ERRNO;

    /* number of the disk with the start of the central directory */
    if (ziplocal_getShort
        (&ziinit.z_filefunc, ziinit.filestream,
         &number_disk_with_CD) != ZIP_OK)
      err = ZIP_ERRNO;

    /* total number of entries in the central dir on this disk */
    if (ziplocal_getShort
        (&ziinit.z_filefunc, ziinit.filestream, &number_entry) != ZIP_OK)
      err = ZIP_ERRNO;

    /* total number of entries in the central dir */
    if (ziplocal_getShort
        (&ziinit.z_filefunc, ziinit.filestream, &number_entry_CD) != ZIP_OK)
      err = ZIP_ERRNO;

    if ((number_entry_CD != number_entry) ||
        (number_disk_with_CD != 0) || (number_disk != 0))
      err = ZIP_BADZIPFILE;

    /* size of the central directory */
    if (ziplocal_getLong
        (&ziinit.z_filefunc, ziinit.filestream, &size_central_dir) != ZIP_OK)
      err = ZIP_ERRNO;

    /* offset of start of central directory with respect to the starting disk 
       number */
    if (ziplocal_getLong
        (&ziinit.z_filefunc, ziinit.filestream,
         &offset_central_dir) != ZIP_OK)
      err = ZIP_ERRNO;

    /* zipfile comment length */
    if (ziplocal_getShort
        (&ziinit.z_filefunc, ziinit.filestream, &size_comment) != ZIP_OK)
      err = ZIP_ERRNO;

    if ((central_pos < offset_central_dir + size_central_dir) &&
        (err == ZIP_OK))
      err = ZIP_BADZIPFILE;

    if (err != ZIP_OK)
    {
      ZCLOSE (ziinit.z_filefunc, ziinit.filestream);
      return NULL;
    }

    byte_before_the_zipfile = central_pos -
      (offset_central_dir + size_central_dir);
    ziinit.add_position_when_writting_offset = byte_before_the_zipfile;

    {
      uLong size_central_dir_to_read = size_central_dir;
      size_t buf_size = SIZEDATA_INDATABLOCK;
      void *buf_read = (void *) ALLOC (buf_size);
      if (ZSEEK (ziinit.z_filefunc, ziinit.filestream,
                 offset_central_dir + byte_before_the_zipfile,
                 ZLIB_FILEFUNC_SEEK_SET) != 0)
        err = ZIP_ERRNO;

      while ((size_central_dir_to_read > 0) && (err == ZIP_OK))
      {
        uLong read_this = SIZEDATA_INDATABLOCK;
        if (read_this > size_central_dir_to_read)
          read_this = size_central_dir_to_read;
        if (ZREAD (ziinit.z_filefunc, ziinit.filestream, buf_read, read_this)
            != read_this)
          err = ZIP_ERRNO;

        if (err == ZIP_OK)
          err = add_data_in_datablock (&ziinit.central_dir, buf_read,
                                       (uLong) read_this);
        size_central_dir_to_read -= read_this;
      }
      TRYFREE (buf_read);
    }
    ziinit.begin_pos = byte_before_the_zipfile;
    ziinit.number_entry = number_entry_CD;

    if (ZSEEK (ziinit.z_filefunc, ziinit.filestream,
               offset_central_dir + byte_before_the_zipfile,
               ZLIB_FILEFUNC_SEEK_SET) != 0)
      err = ZIP_ERRNO;
  }
#    endif /* !NO_ADDFILEINEXISTINGZIP */

  if (err != ZIP_OK)
  {
    TRYFREE (zi);
    return NULL;
  }
  else
  {
    *zi = ziinit;
    return (zipFile) zi;
  }
}

extern zipFile ZEXPORT
zipOpen (pathname, append)
     const char *pathname;
     int append;
{
  return zipOpen2 (pathname, append, NULL, NULL);
}

extern int ZEXPORT
zipOpenNewFileInZip3 (file, filename, zipfi,
                      extrafield_local, size_extrafield_local,
                      extrafield_global, size_extrafield_global,
                      comment, method, level, raw,
                      windowBits, memLevel, strategy,
                      password, crcForCrypting)
     zipFile file;
     const char *filename;
     const zip_fileinfo *zipfi;
     const void *extrafield_local;
     uInt size_extrafield_local;
     const void *extrafield_global;
     uInt size_extrafield_global;
     const char *comment;
     int method;
     int level;
     int raw;
     int windowBits;
     int memLevel;
     int strategy;
     const char *password;
     uLong crcForCrypting;
{
  zip_internal *zi;
  uInt size_filename;
  uInt size_comment;
  uInt i;
  int err = ZIP_OK;

#    ifdef NOCRYPT
  if (password != NULL)
    return ZIP_PARAMERROR;
#    endif

  if (file == NULL)
    return ZIP_PARAMERROR;
  if ((method != 0) && (method != Z_DEFLATED))
    return ZIP_PARAMERROR;

  zi = (zip_internal *) file;

  if (zi->in_opened_file_inzip == 1)
  {
    err = zipCloseFileInZip (file);
    if (err != ZIP_OK)
      return err;
  }


  if (filename == NULL)
    filename = "-";

  if (comment == NULL)
    size_comment = 0;
  else
    size_comment = (uInt) strlen (comment);

  size_filename = (uInt) strlen (filename);

  if (zipfi == NULL)
    zi->ci.dosDate = 0;
  else
  {
    if (zipfi->dosDate != 0)
      zi->ci.dosDate = zipfi->dosDate;
    else
      zi->ci.dosDate =
        ziplocal_TmzDateToDosDate (&zipfi->tmz_date, zipfi->dosDate);
  }

  zi->ci.flag = 0;
  if ((level == 8) || (level == 9))
    zi->ci.flag |= 2;
  if ((level == 2))
    zi->ci.flag |= 4;
  if ((level == 1))
    zi->ci.flag |= 6;
  if (password != NULL)
    zi->ci.flag |= 1;

  zi->ci.crc32 = 0;
  zi->ci.method = method;
  zi->ci.encrypt = 0;
  zi->ci.stream_initialised = 0;
  zi->ci.pos_in_buffered_data = 0;
  zi->ci.raw = raw;
  zi->ci.pos_local_header = ZTELL (zi->z_filefunc, zi->filestream);
  zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename +
    size_extrafield_global + size_comment;
  zi->ci.central_header = (char *) ALLOC ((uInt) zi->ci.size_centralheader);

  ziplocal_putValue_inmemory (zi->ci.central_header,
                              (uLong) CENTRALHEADERMAGIC, 4);
  /* version info */
  ziplocal_putValue_inmemory (zi->ci.central_header + 4,
                              (uLong) VERSIONMADEBY, 2);
  ziplocal_putValue_inmemory (zi->ci.central_header + 6, (uLong) 20, 2);
  ziplocal_putValue_inmemory (zi->ci.central_header + 8, (uLong) zi->ci.flag,
                              2);
  ziplocal_putValue_inmemory (zi->ci.central_header + 10,
                              (uLong) zi->ci.method, 2);
  ziplocal_putValue_inmemory (zi->ci.central_header + 12,
                              (uLong) zi->ci.dosDate, 4);
  ziplocal_putValue_inmemory (zi->ci.central_header + 16, (uLong) 0, 4); /* crc */
  ziplocal_putValue_inmemory (zi->ci.central_header + 20, (uLong) 0, 4); /* compr 
                                                                            size */
  ziplocal_putValue_inmemory (zi->ci.central_header + 24, (uLong) 0, 4); /* uncompr 
                                                                            size */
  ziplocal_putValue_inmemory (zi->ci.central_header + 28,
                              (uLong) size_filename, 2);
  ziplocal_putValue_inmemory (zi->ci.central_header + 30,
                              (uLong) size_extrafield_global, 2);
  ziplocal_putValue_inmemory (zi->ci.central_header + 32,
                              (uLong) size_comment, 2);
  ziplocal_putValue_inmemory (zi->ci.central_header + 34, (uLong) 0, 2); /* disk 
                                                                            nm 
                                                                            start */

  if (zipfi == NULL)
    ziplocal_putValue_inmemory (zi->ci.central_header + 36, (uLong) 0, 2);
  else
    ziplocal_putValue_inmemory (zi->ci.central_header + 36,
                                (uLong) zipfi->internal_fa, 2);

  if (zipfi == NULL)
    ziplocal_putValue_inmemory (zi->ci.central_header + 38, (uLong) 0, 4);
  else
    ziplocal_putValue_inmemory (zi->ci.central_header + 38,
                                (uLong) zipfi->external_fa, 4);

  ziplocal_putValue_inmemory (zi->ci.central_header + 42,
                              (uLong) zi->ci.pos_local_header -
                              zi->add_position_when_writting_offset, 4);

  for (i = 0; i < size_filename; i++)
    *(zi->ci.central_header + SIZECENTRALHEADER + i) = *(filename + i);

  for (i = 0; i < size_extrafield_global; i++)
    *(zi->ci.central_header + SIZECENTRALHEADER + size_filename + i) =
      *(((const char *) extrafield_global) + i);

  for (i = 0; i < size_comment; i++)
    *(zi->ci.central_header + SIZECENTRALHEADER + size_filename +
      size_extrafield_global + i) = *(comment + i);
  if (zi->ci.central_header == NULL)
    return ZIP_INTERNALERROR;

  /* write the local header */
  err =
    ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                       (uLong) LOCALHEADERMAGIC, 4);

  if (err == ZIP_OK)
    err = ziplocal_putValue (&zi->z_filefunc, zi->filestream, (uLong) 20, 2); /* version 
                                                                                 needed 
                                                                                 to 
                                                                                 extract 
                                                                               */
  if (err == ZIP_OK)
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream, (uLong) zi->ci.flag,
                         2);

  if (err == ZIP_OK)
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) zi->ci.method, 2);

  if (err == ZIP_OK)
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) zi->ci.dosDate, 4);

  if (err == ZIP_OK)
    err = ziplocal_putValue (&zi->z_filefunc, zi->filestream, (uLong) 0, 4); /* crc 
                                                                                32, 
                                                                                unknown 
                                                                              */
  if (err == ZIP_OK)
    err = ziplocal_putValue (&zi->z_filefunc, zi->filestream, (uLong) 0, 4); /* compressed 
                                                                                size, 
                                                                                unknown 
                                                                              */
  if (err == ZIP_OK)
    err = ziplocal_putValue (&zi->z_filefunc, zi->filestream, (uLong) 0, 4); /* uncompressed 
                                                                                size, 
                                                                                unknown 
                                                                              */

  if (err == ZIP_OK)
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) size_filename, 2);

  if (err == ZIP_OK)
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) size_extrafield_local, 2);

  if ((err == ZIP_OK) && (size_filename > 0))
    if (ZWRITE (zi->z_filefunc, zi->filestream, filename, size_filename) !=
        size_filename)
      err = ZIP_ERRNO;

  if ((err == ZIP_OK) && (size_extrafield_local > 0))
    if (ZWRITE
        (zi->z_filefunc, zi->filestream, extrafield_local,
         size_extrafield_local) != size_extrafield_local)
      err = ZIP_ERRNO;

  zi->ci.stream.avail_in = (uInt) 0;
  zi->ci.stream.avail_out = (uInt) Z_BUFSIZE;
  zi->ci.stream.next_out = zi->ci.buffered_data;
  zi->ci.stream.total_in = 0;
  zi->ci.stream.total_out = 0;

  if ((err == ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
  {
    zi->ci.stream.zalloc = (alloc_func) 0;
    zi->ci.stream.zfree = (free_func) 0;
    zi->ci.stream.opaque = (voidpf) 0;

    if (windowBits > 0)
      windowBits = -windowBits;

    err = deflateInit2 (&zi->ci.stream, level,
                        Z_DEFLATED, windowBits, memLevel, strategy);

    if (err == Z_OK)
      zi->ci.stream_initialised = 1;
  }
#    ifndef NOCRYPT
  zi->ci.crypt_header_size = 0;
  if ((err == Z_OK) && (password != NULL))
  {
    unsigned char bufHead[RAND_HEAD_LEN];
    unsigned int sizeHead;
    zi->ci.encrypt = 1;
    zi->ci.pcrc_32_tab = get_crc_table ();
    /* init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab); */

    sizeHead =
      crypthead (password, bufHead, RAND_HEAD_LEN, zi->ci.keys,
                 zi->ci.pcrc_32_tab, crcForCrypting);
    zi->ci.crypt_header_size = sizeHead;

    if (ZWRITE (zi->z_filefunc, zi->filestream, bufHead, sizeHead) !=
        sizeHead)
      err = ZIP_ERRNO;
  }
#    endif

  if (err == Z_OK)
    zi->in_opened_file_inzip = 1;
  return err;
}

extern int ZEXPORT
zipOpenNewFileInZip2 (file, filename, zipfi,
                      extrafield_local, size_extrafield_local,
                      extrafield_global, size_extrafield_global,
                      comment, method, level, raw)
     zipFile file;
     const char *filename;
     const zip_fileinfo *zipfi;
     const void *extrafield_local;
     uInt size_extrafield_local;
     const void *extrafield_global;
     uInt size_extrafield_global;
     const char *comment;
     int method;
     int level;
     int raw;
{
  return zipOpenNewFileInZip3 (file, filename, zipfi,
                               extrafield_local, size_extrafield_local,
                               extrafield_global, size_extrafield_global,
                               comment, method, level, raw,
                               -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
                               NULL, 0);
}

extern int ZEXPORT
zipOpenNewFileInZip (file, filename, zipfi,
                     extrafield_local, size_extrafield_local,
                     extrafield_global, size_extrafield_global,
                     comment, method, level)
     zipFile file;
     const char *filename;
     const zip_fileinfo *zipfi;
     const void *extrafield_local;
     uInt size_extrafield_local;
     const void *extrafield_global;
     uInt size_extrafield_global;
     const char *comment;
     int method;
     int level;
{
  return zipOpenNewFileInZip2 (file, filename, zipfi,
                               extrafield_local, size_extrafield_local,
                               extrafield_global, size_extrafield_global,
                               comment, method, level, 0);
}

local int
zipFlushWriteBuffer (zi)
     zip_internal *zi;
{
  int err = ZIP_OK;

  if (zi->ci.encrypt != 0)
  {
#ifndef NOCRYPT
    uInt i;
    int t;
    for (i = 0; i < zi->ci.pos_in_buffered_data; i++)
      zi->ci.buffered_data[i] = zencode (zi->ci.keys, zi->ci.pcrc_32_tab,
                                         zi->ci.buffered_data[i], t);
#endif
  }
  if (ZWRITE
      (zi->z_filefunc, zi->filestream, zi->ci.buffered_data,
       zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data)
    err = ZIP_ERRNO;
  zi->ci.pos_in_buffered_data = 0;
  return err;
}

extern int ZEXPORT
zipWriteInFileInZip (file, buf, len)
     zipFile file;
     const void *buf;
     unsigned len;
{
  zip_internal *zi;
  int err = ZIP_OK;

  if (file == NULL)
    return ZIP_PARAMERROR;
  zi = (zip_internal *) file;

  if (zi->in_opened_file_inzip == 0)
    return ZIP_PARAMERROR;

  zi->ci.stream.next_in = (void *) buf;
  zi->ci.stream.avail_in = len;
  zi->ci.crc32 = crc32 (zi->ci.crc32, buf, len);

  while ((err == ZIP_OK) && (zi->ci.stream.avail_in > 0))
  {
    if (zi->ci.stream.avail_out == 0)
    {
      if (zipFlushWriteBuffer (zi) == ZIP_ERRNO)
        err = ZIP_ERRNO;
      zi->ci.stream.avail_out = (uInt) Z_BUFSIZE;
      zi->ci.stream.next_out = zi->ci.buffered_data;
    }


    if (err != ZIP_OK)
      break;

    if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
    {
      uLong uTotalOutBefore = zi->ci.stream.total_out;
      err = deflate (&zi->ci.stream, Z_NO_FLUSH);
      zi->ci.pos_in_buffered_data +=
        (uInt) (zi->ci.stream.total_out - uTotalOutBefore);

    }
    else
    {
      uInt copy_this, i;
      if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
        copy_this = zi->ci.stream.avail_in;
      else
        copy_this = zi->ci.stream.avail_out;
      for (i = 0; i < copy_this; i++)
        *(((char *) zi->ci.stream.next_out) + i) =
          *(((const char *) zi->ci.stream.next_in) + i);
      {
        zi->ci.stream.avail_in -= copy_this;
        zi->ci.stream.avail_out -= copy_this;
        zi->ci.stream.next_in += copy_this;
        zi->ci.stream.next_out += copy_this;
        zi->ci.stream.total_in += copy_this;
        zi->ci.stream.total_out += copy_this;
        zi->ci.pos_in_buffered_data += copy_this;
      }
    }
  }

  return err;
}

extern int ZEXPORT
zipCloseFileInZipRaw (file, uncompressed_size, crc32)
     zipFile file;
     uLong uncompressed_size;
     uLong crc32;
{
  zip_internal *zi;
  uLong compressed_size;
  int err = ZIP_OK;

  if (file == NULL)
    return ZIP_PARAMERROR;
  zi = (zip_internal *) file;

  if (zi->in_opened_file_inzip == 0)
    return ZIP_PARAMERROR;
  zi->ci.stream.avail_in = 0;

  if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
    while (err == ZIP_OK)
    {
      uLong uTotalOutBefore;
      if (zi->ci.stream.avail_out == 0)
      {
        if (zipFlushWriteBuffer (zi) == ZIP_ERRNO)
          err = ZIP_ERRNO;
        zi->ci.stream.avail_out = (uInt) Z_BUFSIZE;
        zi->ci.stream.next_out = zi->ci.buffered_data;
      }
      uTotalOutBefore = zi->ci.stream.total_out;
      err = deflate (&zi->ci.stream, Z_FINISH);
      zi->ci.pos_in_buffered_data +=
        (uInt) (zi->ci.stream.total_out - uTotalOutBefore);
    }

  if (err == Z_STREAM_END)
    err = ZIP_OK; /* this is normal */

  if ((zi->ci.pos_in_buffered_data > 0) && (err == ZIP_OK))
    if (zipFlushWriteBuffer (zi) == ZIP_ERRNO)
      err = ZIP_ERRNO;

  if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
  {
    err = deflateEnd (&zi->ci.stream);
    zi->ci.stream_initialised = 0;
  }

  if (!zi->ci.raw)
  {
    crc32 = (uLong) zi->ci.crc32;
    uncompressed_size = (uLong) zi->ci.stream.total_in;
  }
  compressed_size = (uLong) zi->ci.stream.total_out;
#    ifndef NOCRYPT
  compressed_size += zi->ci.crypt_header_size;
#    endif

  ziplocal_putValue_inmemory (zi->ci.central_header + 16, crc32, 4); /* crc */
  ziplocal_putValue_inmemory (zi->ci.central_header + 20, compressed_size, 4); /* compr 
                                                                                  size */

  // FIX STATMAT - 09/04/05
  // Had to comment out this setting of ASCII file type. Results
  // differed on different platforms. Windows would sometimes set
  // this and SuSE Linux wouldn't. Rather than investigate
  // further, just simply removed it.

  // if (zi->ci.stream.data_type == Z_ASCII)
  // ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2);

  // FIX STATMAT - 09/04/05

  ziplocal_putValue_inmemory (zi->ci.central_header + 24, uncompressed_size, 4); /* uncompr 
                                                                                    size */

  if (err == ZIP_OK)
    err = add_data_in_datablock (&zi->central_dir, zi->ci.central_header,
                                 (uLong) zi->ci.size_centralheader);
  free (zi->ci.central_header);

  if (err == ZIP_OK)
  {
    long cur_pos_inzip = ZTELL (zi->z_filefunc, zi->filestream);
    if (ZSEEK (zi->z_filefunc, zi->filestream,
               zi->ci.pos_local_header + 14, ZLIB_FILEFUNC_SEEK_SET) != 0)
      err = ZIP_ERRNO;

    if (err == ZIP_OK)
      err = ziplocal_putValue (&zi->z_filefunc, zi->filestream, crc32, 4); /* crc 
                                                                              32, 
                                                                              unknown 
                                                                            */

    if (err == ZIP_OK) /* compressed size, unknown */
      err =
        ziplocal_putValue (&zi->z_filefunc, zi->filestream, compressed_size,
                           4);

    if (err == ZIP_OK) /* uncompressed size, unknown */
      err =
        ziplocal_putValue (&zi->z_filefunc, zi->filestream, uncompressed_size,
                           4);

    if (ZSEEK (zi->z_filefunc, zi->filestream,
               cur_pos_inzip, ZLIB_FILEFUNC_SEEK_SET) != 0)
      err = ZIP_ERRNO;
  }

  zi->number_entry++;
  zi->in_opened_file_inzip = 0;

  return err;
}

extern int ZEXPORT
zipCloseFileInZip (file)
     zipFile file;
{
  return zipCloseFileInZipRaw (file, 0, 0);
}

extern int ZEXPORT
zipClose (file, global_comment)
     zipFile file;
     const char *global_comment;
{
  zip_internal *zi;
  int err = 0;
  uLong size_centraldir = 0;
  uLong centraldir_pos_inzip;
  uInt size_global_comment;
  if (file == NULL)
    return ZIP_PARAMERROR;
  zi = (zip_internal *) file;

  if (zi->in_opened_file_inzip == 1)
  {
    err = zipCloseFileInZip (file);
  }

  if (global_comment == NULL)
    size_global_comment = 0;
  else
    size_global_comment = (uInt) strlen (global_comment);


  centraldir_pos_inzip = ZTELL (zi->z_filefunc, zi->filestream);
  if (err == ZIP_OK)
  {
    linkedlist_datablock_internal *ldi = zi->central_dir.first_block;
    while (ldi != NULL)
    {
      if ((err == ZIP_OK) && (ldi->filled_in_this_block > 0))
        if (ZWRITE (zi->z_filefunc, zi->filestream,
                    ldi->data, ldi->filled_in_this_block)
            != ldi->filled_in_this_block)
          err = ZIP_ERRNO;

      size_centraldir += ldi->filled_in_this_block;
      ldi = ldi->next_datablock;
    }
  }
  free_datablock (zi->central_dir.first_block);

  if (err == ZIP_OK) /* Magic End */
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) ENDHEADERMAGIC, 4);

  if (err == ZIP_OK) /* number of this disk */
    err = ziplocal_putValue (&zi->z_filefunc, zi->filestream, (uLong) 0, 2);

  if (err == ZIP_OK) /* number of the disk with the start of the central
                        directory */
    err = ziplocal_putValue (&zi->z_filefunc, zi->filestream, (uLong) 0, 2);

  if (err == ZIP_OK) /* total number of entries in the central dir on this
                        disk */
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) zi->number_entry, 2);

  if (err == ZIP_OK) /* total number of entries in the central dir */
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) zi->number_entry, 2);

  if (err == ZIP_OK) /* size of the central directory */
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) size_centraldir, 4);

  if (err == ZIP_OK) /* offset of start of central directory with respect to
                        the starting disk number */
    err = ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                             (uLong) (centraldir_pos_inzip -
                                      zi->add_position_when_writting_offset),
                             4);

  if (err == ZIP_OK) /* zipfile comment length */
    err =
      ziplocal_putValue (&zi->z_filefunc, zi->filestream,
                         (uLong) size_global_comment, 2);

  if ((err == ZIP_OK) && (size_global_comment > 0))
    if (ZWRITE (zi->z_filefunc, zi->filestream,
                global_comment, size_global_comment) != size_global_comment)
      err = ZIP_ERRNO;

  if (ZCLOSE (zi->z_filefunc, zi->filestream) != 0)
    if (err == ZIP_OK)
      err = ZIP_ERRNO;

  TRYFREE (zi);

  return err;
}
