/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file
 * @ingroup ZipSupport
 * @brief Zip Support for Java VM
*/

#include <string.h>
#include <sys/stat.h>

#include "hy2sie.h"
#include "zipsup.h"

#include "zlib.h"

// zlib is statically linked for Android:
#define checkZipLibrary(dummy) 0
#ifdef checkZipLibrary
#define inflateInit2Func(a, b, c, d) inflateInit2_ (a, b, c, d)
#define inflateFunc(a, b) inflate (a, b)
#define inflateEndFunc(a) inflateEnd (a)
#else
/* Globals for the zip library */
UDATA zipDLLDescriptor = 0;
int (*inflateInit2Func) (void *, int, const char *, int);
int (*inflateFunc) (void *, int);
int (*inflateEndFunc) (void *);
#endif

#define ZIP_NEXT_U8(value, index) (value = *(index++))
#define ZIP_NEXT_U16(value, index) ((value = (index[1] << 8) | index[0]), index += 2, value)
#define ZIP_NEXT_U32(value, index) ((value = ((U_32)index[3] << 24) | ((U_32)index[2] << 16) | ((U_32)index[1] << 8) | (U_32)index[0]), index += 4, value)

#define WORK_BUFFER_SIZE 64000

#define SCAN_CHUNK_SIZE 1024

struct workBuffer
{
  HyPortLibrary *portLib;
  UDATA *bufferStart;
  UDATA *bufferEnd;
  UDATA *currentAlloc;
  UDATA cntr;
};

#define CDEV_CURRENT_FUNCTION _prototypes_private
I_32 zip_populateCache
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile));

static I_32 inflateData
PROTOTYPE ((struct workBuffer * workBuf, U_8 * inputBuffer,
            U_32 inputBufferSize, U_8 * outputBuffer, U_32 outputBufferSize));

I_32 checkZipLibrary PROTOTYPE ((HyPortLibrary * portLib));

I_32 scanForDataDescriptor
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
            HyZipEntry * zipEntry));
void zdatafree PROTOTYPE ((void *opaque, void *address));
static I_32 readZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
            HyZipEntry * zipEntry, const char *filename,
            IDATA * enumerationPointer, IDATA * entryStart,
            BOOLEAN findDirectory));
I_32 scanForCentralEnd
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
            HyZipCentralEnd * endEntry));
void *zdataalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size));

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION _prototypes_public
I_32 zip_getZipEntryData
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
            U_8 * buffer, U_32 bufferSize));
I_32 zip_getZipEntryFromOffset
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
            IDATA offset));
I_32 zip_establishCache
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile));
void zip_resetZipFile
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
            IDATA * nextEntryPointer));
I_32 zip_getNextZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile,
            HyZipEntry * zipEntry, IDATA * nextEntryPointer));
I_32 zip_getZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
            const char *filename, BOOLEAN findDirectory));
I_32 zip_getZipEntryExtraField
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
            U_8 * buffer, U_32 bufferSize));
void zip_initZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipEntry * entry));
I_32 zip_openZipFile
PROTOTYPE ((HyPortLibrary * portLib, char *filename, HyZipFile * zipFile,
            HyZipCachePool * cachePool));
void zip_freeZipEntry
PROTOTYPE ((HyPortLibrary * portLib, HyZipEntry * entry));
I_32 VMCALL zip_closeZipFile
PROTOTYPE ((HyPortLibrary * portLib, struct HyZipFile * zipFile));
I_32 zip_getZipEntryComment
PROTOTYPE ((HyPortLibrary * portLib, HyZipFile * zipFile, HyZipEntry * entry,
            U_8 * buffer, U_32 bufferSize));

#undef CDEV_CURRENT_FUNCTION

//#include "hythread.h"
//#define ENTER() hythread_monitor_enter(hythread_global_monitor())
//#define EXIT() hythread_monitor_exit(hythread_global_monitor())

#include "hymutex.h"
MUTEX zip_globalMutex;
static int initialized = 0;

#define ENTER() \
  if (!initialized) { MUTEX_INIT(zip_globalMutex); initialized = 1; } \
  MUTEX_ENTER(zip_globalMutex);

#define EXIT() MUTEX_EXIT(zip_globalMutex);


HyZipCachePool *
zipsup_GetZipCachePool(JNIEnv * env)
{
  static HyZipCachePool *pool = 0;

  if (pool == 0) {
    pool = zipCachePool_new(env);
  }
  return pool;
}


#ifndef checkZipLibrary
#define CDEV_CURRENT_FUNCTION checkZipLibrary

/*
	Ensure that the zip library is loaded.
	Return 0 on success, -1 on failure.
*/
I_32
checkZipLibrary (HyPortLibrary * portLib)
{
  PORT_ACCESS_FROM_PORT (portLib);

  /* if the library has already been loaded return success/failure */
  if (zipDLLDescriptor > 1)
    return 0;
  if (zipDLLDescriptor == 1)
    return -1;

  /* open up the zip library by name */

  if (hysl_open_shared_library (HY_ZIP_DLL_NAME, &zipDLLDescriptor, TRUE))
    goto openFailed;

  /* look up the functions */
  if (hysl_lookup_name
      (zipDLLDescriptor, "inflateInit2_", (void *) &inflateInit2Func,
       "ILILI"))
    goto loadFailed;
  if (hysl_lookup_name
      (zipDLLDescriptor, "inflate", (void *) &inflateFunc, "IPI"))
    goto loadFailed;
  if (hysl_lookup_name
      (zipDLLDescriptor, "inflateEnd", (void *) &inflateEndFunc, "IP"))
    goto loadFailed;

  /* good to go */
  return 0;

loadFailed:
  hysl_close_shared_library (zipDLLDescriptor);

  /* mark the descriptor as a failed load. only report the error once */
  zipDLLDescriptor = 1;

  /* Unable to open %s (Missing export) */
  hynls_printf (PORTLIB, HYNLS_WARNING, HYNLS_ZIP_MISSING_EXPORT,
                HY_ZIP_DLL_NAME);

  return -1;

openFailed:
  /* mark the descriptor as a failed load. only report the error once */
  zipDLLDescriptor = 1;

  /* Unable to open %s (%s) */
  hynls_printf (PORTLIB, HYNLS_WARNING, HYNLS_ZIP_UNABLE_TO_OPEN_ZIP_DLL,
                HY_ZIP_DLL_NAME, hyerror_last_error_message ());
  return -1;
}

#undef CDEV_CURRENT_FUNCTION
#endif

#define CDEV_CURRENT_FUNCTION inflateData

/*
	Returns 0 on success or one of the following:
			ZIP_ERR_UNSUPPORTED_FILE_TYPE
			ZIP_ERR_FILE_CORRUPT
			ZIP_ERR_OUT_OF_MEMORY
			ZIP_ERR_INTERNAL_ERROR
*/
static I_32
inflateData (struct workBuffer *workBuf, U_8 * inputBuffer,
             U_32 inputBufferSize, U_8 * outputBuffer, U_32 outputBufferSize)
{
  PORT_ACCESS_FROM_PORT (workBuf->portLib);

  z_stream stream;
  I_32 err;

  stream.next_in = inputBuffer;
  stream.avail_in = inputBufferSize;
  stream.next_out = outputBuffer;
  stream.avail_out = outputBufferSize;

  stream.opaque = workBuf;
  stream.zalloc = zdataalloc;
  stream.zfree = zdatafree;

  /* Initialize stream. Pass "-15" as max number of window bits, negated
     to indicate that no zlib header is present in the data. */
  err = inflateInit2Func (&stream, -15, ZLIB_VERSION, sizeof (z_stream));
  if (err != Z_OK)
    return -1;

  /* Inflate the data. */
  err = inflateFunc (&stream, Z_SYNC_FLUSH);

  /* Clean up the stream. */
  inflateEndFunc (&stream);

  /* Check the return code. Did we complete the inflate? */
  if ((err == Z_STREAM_END) || (err == Z_OK))
    {
      if (stream.total_out == outputBufferSize)
        {
          return 0;
        }
    }

  switch (err)
    {
    case Z_OK:                 /* an error if file is incomplete */
    case Z_STREAM_END:         /* an error if file is incomplete */
    case Z_ERRNO:              /* a random error */
    case Z_STREAM_ERROR:       /* stream inconsistent */
    case Z_DATA_ERROR:         /* corrupted zip */
      return ZIP_ERR_FILE_CORRUPT;

    case Z_VERSION_ERROR:      /* wrong zlib version */
    case Z_NEED_DICT:          /* needs a preset dictionary that we can't provide */
      return ZIP_ERR_UNSUPPORTED_FILE_TYPE;

    case Z_MEM_ERROR:          /* out of memory */
      return ZIP_ERR_OUT_OF_MEMORY;

    case Z_BUF_ERROR:          /* no progress / out of output buffer */
    default:                   /* jic */
      return ZIP_ERR_INTERNAL_ERROR;
    }
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION scanForCentralEnd
/*
	Scan backward from end of file for a central end header. Read from zipFile and update the HyZipCentralEnd provided.

	Returns 0 on success or one of the following:
			ZIP_ERR_FILE_READ_ERROR
			ZIP_ERR_FILE_CORRUPT
*/
I_32
scanForCentralEnd (HyPortLibrary * portLib, HyZipFile * zipFile,
                   HyZipCentralEnd * endEntry)
{
  U_8 *current;
  U_8 buffer[SCAN_CHUNK_SIZE];
  I_32 i, size, state;
  U_32 dataSize = 0;
  I_64 seekResult;
  I_32 fileSize;
  I_32 bytesAlreadyRead = 0;

  PORT_ACCESS_FROM_PORT (portLib);

  /* Haven't seen anything yet. */
  state = 0;

  seekResult = hyfile_seek (zipFile->fd, 0, HySeekEnd);
  if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
    {
      zipFile->pointer = -1;
      return ZIP_ERR_FILE_READ_ERROR;
    }
  fileSize = (I_32) seekResult;
  zipFile->pointer = fileSize;

  while (TRUE)
    {
      /* Fill the buffer. */
      if (bytesAlreadyRead == fileSize)
        {
          zipFile->pointer = -1;
          return ZIP_ERR_FILE_CORRUPT;
        }

      size = SCAN_CHUNK_SIZE;
      if (size > fileSize - bytesAlreadyRead)
        size = fileSize - bytesAlreadyRead;
      bytesAlreadyRead += size;
      seekResult =
        hyfile_seek (zipFile->fd, fileSize - bytesAlreadyRead, HySeekSet);
      if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
        {
          zipFile->pointer = -1;
          return ZIP_ERR_FILE_READ_ERROR;
        }
      zipFile->pointer = (I_32) seekResult;

      if (hyfile_read (zipFile->fd, buffer, size) != size)
        {
          zipFile->pointer = -1;
          return ZIP_ERR_FILE_READ_ERROR;
        }
      zipFile->pointer += size;

      /* Scan the buffer (backwards) for CentralEnd signature = PK^E^F. */
      for (i = size; i--; dataSize++)
        {
          switch (state)
            {
            case 0:
              /* Nothing yet. */
              if (buffer[i] == 6)
                state = 1;
              break;

            case 1:
              /* Seen ^F */
              if (buffer[i] == 5)
                state = 2;
              else
                state = 0;
              break;

            case 2:
              /* Seen ^E^F */
              if (buffer[i] == 'K')
                state = 3;
              else
                state = 0;
              break;

            case 3:
              /* Seen K^E^F */
              if (buffer[i] == 'P' && dataSize >= 21)
                {
                  /* Found it.  Read the data from the end-of-central-dir record. */
                  current = buffer + i + 4;
                  ZIP_NEXT_U16 (endEntry->diskNumber, current);
                  ZIP_NEXT_U16 (endEntry->dirStartDisk, current);
                  ZIP_NEXT_U16 (endEntry->thisDiskEntries, current);
                  ZIP_NEXT_U16 (endEntry->totalEntries, current);
                  ZIP_NEXT_U32 (endEntry->dirSize, current);
                  ZIP_NEXT_U32 (endEntry->dirOffset, current);
                  ZIP_NEXT_U16 (endEntry->commentLength, current);

                  /* Quick test to ensure that the header isn't invalid. 
                     Current dataSize is the number of bytes of data scanned, up to the ^H in the stream. */
                  if (dataSize >= (U_32) (21 + endEntry->commentLength))
                    return 0;

                  /* Header looked invalid. Pretend we didn't see it and keep scanning.. */
                }
              state = 0;
              break;
            }
        }
    }
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION scanForDataDescriptor
/*
	Scan ahead for a data descriptor. Read from zipFile and update the HyZipLocalHeader provided.

	Returns 0 on success or one of the following:
			ZIP_ERR_FILE_READ_ERROR
			ZIP_ERR_FILE_CORRUPT
*/
I_32
scanForDataDescriptor (HyPortLibrary * portLib, HyZipFile * zipFile,
                       HyZipEntry * zipEntry)
{
  U_8 *current;
  U_8 buffer[SCAN_CHUNK_SIZE], descriptor[16];
  I_32 i, size, state;
  U_32 dataSize, blockPointer;
  I_64 seekResult;

  PORT_ACCESS_FROM_PORT (portLib);

  /* Skip ahead and read the data descriptor. The compressed size should be 0. */
  if (zipFile->pointer !=
      (IDATA) (zipEntry->dataPointer + zipEntry->compressedSize))
    {
      seekResult =
        hyfile_seek (zipFile->fd,
                     zipEntry->dataPointer + zipEntry->compressedSize,
                     HySeekSet);
      if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
        {
          zipFile->pointer = -1;
          return ZIP_ERR_FILE_READ_ERROR;
        }
      zipFile->pointer = (I_32) seekResult;
    }

  /* Haven't seen anything yet. */
  blockPointer = dataSize = zipEntry->compressedSize;
  state = 0;

  /* Scan until we find PK^G^H (otherwise it's an error). */
  while (1)
    {
      /* Fill the buffer. */
      size = hyfile_read (zipFile->fd, buffer, SCAN_CHUNK_SIZE);
      if (size == 0)
        {
          return ZIP_ERR_FILE_CORRUPT;
        }
      else if (size < 0)
        {
          zipFile->pointer = -1;
          return ZIP_ERR_FILE_READ_ERROR;
        }
      zipFile->pointer += size;
      blockPointer += size;

      /* Scan the buffer. */
      for (i = 0; i < size; i++, dataSize++)
        {
          switch (state)
            {
            case 0:
              /* Nothing yet. */
              if (buffer[i] == 'P')
                {
                  state = 1;
                }
              break;

            case 1:
              /* Seen P */
              if (buffer[i] == 'K')
                {
                  state = 2;
                }
              else
                state = 0;
              break;

            case 2:
              /* Seen PK */
              if (buffer[i] == 7)
                {
                  state = 3;
                }
              else
                {
                  state = 0;
                }
              break;

            case 3:
              /* Seen PK^G */
              if (buffer[i] == 8)
                {
                  /* Found it! Read the descriptor */
                  if (i + 12 < size)
                    {
                      current = &buffer[i + 1];
                    }
                  else
                    {
                      seekResult =
                        hyfile_seek (zipFile->fd,
                                     zipEntry->dataPointer + dataSize + 1,
                                     HySeekSet);
                      if ((seekResult < 0)
                          || (seekResult > HYCONST64 (0x7FFFFFFF)))
                        {
                          zipFile->pointer = -1;
                          return ZIP_ERR_FILE_READ_ERROR;
                        }
                      zipFile->pointer = (I_32) seekResult;
                      if (hyfile_read (zipFile->fd, descriptor, 12) != 12)
                        {
                          zipFile->pointer = -1;
                          return ZIP_ERR_FILE_READ_ERROR;
                        }
                      zipFile->pointer += 12;
                      current = descriptor;
                    }

                  /* Read the data from the descriptor. */
                  ZIP_NEXT_U32 (zipEntry->crc32, current);
                  ZIP_NEXT_U32 (zipEntry->compressedSize, current);
                  ZIP_NEXT_U32 (zipEntry->uncompressedSize, current);

                  /* Quick test to ensure that the header isn't invalid. 
                     Current dataSize is the number of bytes of data scanned, up to the ^H in the stream. */
                  if (dataSize - 3 == zipEntry->compressedSize)
                    {
                      return 0;
                    }

                  /* Header looked invalid. Reset the pointer and continue scanning. */
                  seekResult =
                    hyfile_seek (zipFile->fd,
                                 zipEntry->dataPointer + blockPointer,
                                 HySeekSet);
                  if ((seekResult < 0)
                      || (seekResult > HYCONST64 (0x7FFFFFFF)))
                    {
                      zipFile->pointer = -1;
                      return ZIP_ERR_FILE_READ_ERROR;
                    }
                  zipFile->pointer = (I_32) seekResult;
                }
              else
                state = 0;
              break;
            }
        }
    }
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_populateCache
/*
	Fill in the cache of a given zip file.  This should only be called once during zip_openZipFile!

	Returns 0 on success or one of the following:
			ZIP_ERR_FILE_READ_ERROR
			ZIP_ERR_FILE_OPEN_ERROR
			ZIP_ERR_UNKNOWN_FILE_TYPE
			ZIP_ERR_UNSUPPORTED_FILE_TYPE
			ZIP_ERR_OUT_OF_MEMORY
			ZIP_ERR_INTERNAL_ERROR
*/
I_32
zip_populateCache (HyPortLibrary * portLib, HyZipFile * zipFile)
{
  PORT_ACCESS_FROM_PORT (portLib);

  I_32 result = 0;
  IDATA bufferSize = 65536;
  IDATA unreadSize = 0;
  IDATA bufferedSize = 0;
  IDATA bytesToRead = 0;
  IDATA filenameCopied;
  HyZipEntry entry;
  HyZipCentralEnd endEntry;
  U_8 *buffer = NULL;
  U_8 *filename = NULL;
  IDATA filenameSize = 256;     /* Should be sufficient for most filenames */
  U_8 *current;
  U_32 sig;
  U_32 localHeaderOffset;
  IDATA startCentralDir;
  I_64 seekResult;

  if (!zipFile->cache)
    return ZIP_ERR_INTERNAL_ERROR;

  /* Find and read the end-of-central-dir record. */
  result = scanForCentralEnd (portLib, zipFile, &endEntry);
  if (result != 0)
    return result;

  unreadSize = endEntry.dirSize + 4 /* slop */ ;
  zipFile->cache->startCentralDir = startCentralDir =
    (IDATA) ((UDATA) endEntry.dirOffset);

  if (zipFile->pointer != startCentralDir)
    {
      seekResult = hyfile_seek (zipFile->fd, startCentralDir, HySeekSet);
      if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
        {
          zipFile->pointer = -1;
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }

      zipFile->pointer = (I_32) seekResult;
      if (zipFile->pointer != startCentralDir)
        {
          result = ZIP_ERR_FILE_READ_ERROR;
          zipFile->pointer = -1;
          goto finished;
        }
    }

  /* No point in allocating more than we'll actually need.. */
  if (bufferSize > unreadSize)
    bufferSize = unreadSize;

  filename = hymem_allocate_memory (filenameSize);
  if (!filename)
    {
      result = ZIP_ERR_OUT_OF_MEMORY;
      goto finished;
    }

  /* Allocate some space to hold central directory goo as we eat through it */
  buffer = hymem_allocate_memory (bufferSize);
  if (!buffer && (bufferSize > 4096))
    {
      /* Not enough memory, fall back to a smaller buffer! */
      bufferSize = 4096;
      buffer = hymem_allocate_memory (bufferSize);
    }
  if (!buffer)
    {
      result = ZIP_ERR_OUT_OF_MEMORY;
      goto finished;
    }

  while (unreadSize)
    {

      /* Read as much as needed into buffer. */
      bytesToRead = bufferSize - bufferedSize;
      if (bytesToRead > unreadSize)
        bytesToRead = unreadSize;
      result = hyfile_read (zipFile->fd, buffer + bufferedSize, bytesToRead);
      if (result < 0)
        {
          result = ZIP_ERR_FILE_READ_ERROR;
          zipFile->pointer = -1;
          goto finished;
        }
      zipFile->pointer += result;
      unreadSize -= result;
      bufferedSize += result;
      current = buffer;

      /* consume entries until we run out. */
      while (current + 46 < buffer + bufferedSize)
        {
          IDATA entryPointer;

          entryPointer =
            zipFile->pointer + (current - (buffer + bufferedSize));

          sig = 0;
          ZIP_NEXT_U32 (sig, current);
          if (sig == ZIP_CentralEnd)
            {
              /* We're done here. */
              result = 0;
              goto finished;
            }
          if (sig != ZIP_CentralHeader)
            {
              /* Umm...What the Hell? */
              result = ZIP_ERR_FILE_CORRUPT;
              goto finished;
            }

          /* Read ZIP_CentralHeader entry */
          ZIP_NEXT_U16 (entry.versionCreated, current);
          ZIP_NEXT_U16 (entry.versionNeeded, current);
          ZIP_NEXT_U16 (entry.flags, current);
          ZIP_NEXT_U16 (entry.compressionMethod, current);
          ZIP_NEXT_U16 (entry.lastModTime, current);
          ZIP_NEXT_U16 (entry.lastModDate, current);
          ZIP_NEXT_U32 (entry.crc32, current);
          ZIP_NEXT_U32 (entry.compressedSize, current);
          ZIP_NEXT_U32 (entry.uncompressedSize, current);
          ZIP_NEXT_U16 (entry.filenameLength, current);
          ZIP_NEXT_U16 (entry.extraFieldLength, current);
          ZIP_NEXT_U16 (entry.fileCommentLength, current);
          current += sizeof (U_16);     /* skip disk number field */
          ZIP_NEXT_U16 (entry.internalAttributes, current);
          current += sizeof (U_32);     /* skip external attributes field */
          ZIP_NEXT_U32 (localHeaderOffset, current);

          /* Increase filename buffer size if necessary. */
          if (filenameSize < entry.filenameLength + 1)
            {
              hymem_free_memory (filename);
              filenameSize = entry.filenameLength + 1;
              filename = hymem_allocate_memory (filenameSize);
              if (!filename)
                {
                  result = ZIP_ERR_OUT_OF_MEMORY;
                  goto finished;
                }
            }

          filenameCopied = 0;
          while (filenameCopied < entry.filenameLength)
            {
              IDATA size;
              /* Copy as much of the filename as we can see in the buffer (probably the whole thing). */

              size = entry.filenameLength - filenameCopied;
              if (size > bufferedSize - (current - buffer))
                {
                  size = bufferedSize - (current - buffer);
                }
              memcpy (filename + filenameCopied, current, size);
              filenameCopied += size;
              current += size;
              if (filenameCopied >= entry.filenameLength)
                break;          /* done */

              /* Otherwise, we ran out of source string.  Load another chunk.. */
              bufferedSize = 0;
              if (!unreadSize)
                {
                  /* Central header is supposedly done?  Bak */
                  result = ZIP_ERR_FILE_CORRUPT;
                  goto finished;
                }
              bytesToRead = bufferSize - bufferedSize;
              if (bytesToRead > unreadSize)
                bytesToRead = unreadSize;
              result =
                hyfile_read (zipFile->fd, buffer + bufferedSize, bytesToRead);
              if (result < 0)
                {
                  result = ZIP_ERR_FILE_READ_ERROR;
                  zipFile->pointer = -1;
                  goto finished;
                }
              zipFile->pointer += result;
              unreadSize -= result;
              bufferedSize += result;
              current = buffer;
            }
          filename[entry.filenameLength] = '\0';        /* null-terminate */

          if (((entry.compressionMethod == ZIP_CM_Deflated)
               && (entry.flags & 0x8)) || (entry.fileCommentLength != 0))
            {
              /* Either local header doesn't know the compressedSize, or this entry has a file
                 comment.  In either case, cache the central header instead of the local header
                 so we can find the information we need later. */

              result =
                zipCache_addElement (zipFile->cache, (char *) filename,
                                     entryPointer);

            }
          else
            {
              result =
                zipCache_addElement (zipFile->cache, (char *) filename,
                                     localHeaderOffset);
            }

          if (!result)
            {
              result = ZIP_ERR_OUT_OF_MEMORY;
              goto finished;
            }

          /* Skip the data and comment. */
          bytesToRead = entry.extraFieldLength + entry.fileCommentLength;
          if (bufferedSize - (current - buffer) >= bytesToRead)
            {
              current += bytesToRead;
            }
          else
            {
              /* The rest of the buffer is uninteresting.  Skip ahead to where the good stuff is */
              bytesToRead -= (bufferedSize - (current - buffer));
              current = buffer + bufferedSize;
              unreadSize -= bytesToRead;

              seekResult = hyfile_seek (zipFile->fd, bytesToRead, HySeekCur);
              if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
                {
                  zipFile->pointer = -1;
                  result = ZIP_ERR_FILE_READ_ERROR;
                  goto finished;
                }
              zipFile->pointer = (I_32) seekResult;
            }
        }
      bufferedSize -= (current - buffer);
      memmove (buffer, current, bufferedSize);
    }

  result = 0;

finished:
  if (filename)
    hymem_free_memory (filename);
  if (buffer)
    hymem_free_memory (buffer);
  return result;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION readZipEntry
/*
	Read the next zip entry for the zipFile into the zipEntry provided.  If filename is non-NULL, it is expected to match
	the filename read for the entry.  If (cachePointer != -1) the filename of the entry will be looked up in the cache (assuming
	there is one) to help detect use of an invalid cache.  If enumerationPointer is non-NULL, sequential access is assumed and
	either a local zip entry or a data descriptor will be accepted, but a central zip entry will cause ZIP_ERR_NO_MORE_ENTRIES
	to be returned.  If enumerationPointer is NULL, random access is assumed and either a local zip entry or a central zip
	entry will be accepted.

	Returns 0 on success or one of the following:
			ZIP_ERR_FILE_READ_ERROR
			ZIP_ERR_FILE_CORRUPT
			ZIP_ERR_OUT_OF_MEMORY
			ZIP_ERR_NO_MORE_ENTRIES
*/
static I_32
readZipEntry (HyPortLibrary * portLib, HyZipFile * zipFile,
              HyZipEntry * zipEntry, const char *filename,
              IDATA * enumerationPointer, IDATA * entryStart,
              BOOLEAN findDirectory)
{
  PORT_ACCESS_FROM_PORT (portLib);

  I_32 result;
  U_8 buffer[46 + 128];
  U_8 *current;
  U_32 sig;
  IDATA readLength;
  I_64 seekResult;
  U_8 *readBuffer;
  IDATA currentEntryPointer, localEntryPointer;
  IDATA headerSize;
  IDATA filenameLength = filename ? strlen (filename) : 0;

retry:
  if (entryStart)
    *entryStart = zipFile->pointer;
  readBuffer = NULL;
  /* Guess how many bytes we'll need to read.  If we guess correctly we will do fewer I/O operations */
  headerSize = 30;              /* local zip header size */
  if (zipFile->cache && (zipFile->pointer >= zipFile->cache->startCentralDir))
    {
      headerSize = 46;          /* central zip header size */
    }
  readLength = headerSize + (filename ? filenameLength : 128);
  if (findDirectory)
    {
      /* Extra byte for possible trailing '/' */
      readLength++;
    }

  /* Allocate some memory if necessary */
  if (readLength <= sizeof (buffer))
    {
      current = buffer;
    }
  else
    {
      current = readBuffer = hymem_allocate_memory (readLength);
      if (!readBuffer)
        return ZIP_ERR_OUT_OF_MEMORY;
    }

  currentEntryPointer = localEntryPointer = zipFile->pointer;

  result = hyfile_read (zipFile->fd, current, readLength);
  if ((result < 22)
      || (filename
          && !(result == readLength
               || (findDirectory && result == (readLength - 1)))))
    {
      /* We clearly didn't get enough bytes */
      result = ZIP_ERR_FILE_READ_ERROR;
      goto finished;
    }
  zipFile->pointer += result;
  readLength = result;          /* If it's not enough, we'll catch that later */
  ZIP_NEXT_U32 (sig, current);

  if (enumerationPointer)
    {
      if ((sig == ZIP_CentralEnd))
        {
          result = ZIP_ERR_NO_MORE_ENTRIES;
          goto finished;
        }
    }
  if ((enumerationPointer || (!zipFile->cache))
      && (sig == ZIP_DataDescriptor))
    {
      /* We failed to predict a data descriptor here.  This should be an error (i.e. only happens in malformed zips?) 
         but, but we will silently skip over it */
      seekResult =
        hyfile_seek (zipFile->fd, currentEntryPointer + 16, HySeekSet);
      if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
        {
          zipFile->pointer = -1;
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }
      zipFile->pointer = (I_32) seekResult;

      if (zipFile->pointer == currentEntryPointer + 16)
        {
          if (readBuffer)
            {
              hymem_free_memory (readBuffer);
            }
          goto retry;
        }
      result = ZIP_ERR_FILE_READ_ERROR;
      goto finished;
    }

  if ((sig != ZIP_CentralHeader) && (sig != ZIP_LocalHeader))
    {
      /* Unexpected. */
      result = ZIP_ERR_FILE_CORRUPT;
      goto finished;
    }
  headerSize = ((sig == ZIP_CentralHeader) ? 46 : 30);
  if (readLength < headerSize)
    {
      /* We didn't get the whole header (and none of the filename).. */
      /* NOTE: this could happen in normal use if the assumed filename length above is <16.  Since it's 128, we don't 
         handle the impossible case where we would have to read more header.  It could also happen if the caller
         supplied a filename of length <16 but that only happens when we have a cache (so we'll know the header size) 
       */
      result = ZIP_ERR_FILE_READ_ERROR;
    }
  readLength -= headerSize;

  if (sig == ZIP_CentralHeader)
    {
      current += 2;             /* skip versionCreated field */
    }
  ZIP_NEXT_U16 (zipEntry->versionNeeded, current);
  ZIP_NEXT_U16 (zipEntry->flags, current);
  ZIP_NEXT_U16 (zipEntry->compressionMethod, current);
  ZIP_NEXT_U16 (zipEntry->lastModTime, current);
  ZIP_NEXT_U16 (zipEntry->lastModDate, current);
  ZIP_NEXT_U32 (zipEntry->crc32, current);
  ZIP_NEXT_U32 (zipEntry->compressedSize, current);
  ZIP_NEXT_U32 (zipEntry->uncompressedSize, current);
  ZIP_NEXT_U16 (zipEntry->filenameLength, current);
  ZIP_NEXT_U16 (zipEntry->extraFieldLength, current);
  zipEntry->fileCommentLength = 0;

  if (sig == ZIP_CentralHeader)
    {
      ZIP_NEXT_U16 (zipEntry->fileCommentLength, current);
      current += 8;             /* skip disk number start + internal attrs + external attrs */
      ZIP_NEXT_U32 (localEntryPointer, current);
    }

  if (filename)
    {
      if (zipFile->cache)
        {
          if (!
              (readLength == zipEntry->filenameLength
               || (findDirectory
                   && (readLength - 1) == zipEntry->filenameLength)))
            {
              /* We knew exactly how much we were supposed to read, and this wasn't it */
              result = ZIP_ERR_FILE_CORRUPT;
              goto finished;
            }
        }
    }

  /* Allocate space for filename */
  if (zipEntry->filenameLength >= ZIP_INTERNAL_MAX)
    {
      zipEntry->filename =
        hymem_allocate_memory (zipEntry->filenameLength + 1);
      if (!zipEntry->filename)
        {
          result = ZIP_ERR_OUT_OF_MEMORY;
          goto finished;
        }
    }
  else
    {
      zipEntry->filename = zipEntry->internalFilename;
    }
  if (readLength > zipEntry->filenameLength)
    {
      readLength = zipEntry->filenameLength;
    }
  memcpy (zipEntry->filename, current, readLength);

  /* Read the rest of the filename if necessary.  Allocate space in HyZipEntry for it! */
  if (readLength < zipEntry->filenameLength)
    {
      result =
        hyfile_read (zipFile->fd, zipEntry->filename + readLength,
                     zipEntry->filenameLength - readLength);
      if (result != (zipEntry->filenameLength - readLength))
        {
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }
      zipFile->pointer += result;
    }
  zipEntry->filename[zipEntry->filenameLength] = '\0';

  /* If we know what filename is supposed to be, compare it and make sure it matches */
  /* Note: CASE-SENSITIVE COMPARE because filenames in zips are case sensitive (even on platforms with
     case-insensitive file systems) */
  if (filename)
    {
      if (!
          ((findDirectory && zipEntry->filenameLength == (filenameLength + 1)
            && zipEntry->filename[filenameLength] == '/'
            && !strncmp ((char *) zipEntry->filename, (const char *) filename,
                         filenameLength))
           || !strcmp ((const char *) zipEntry->filename,
                       (const char *) filename)))
        {
          /* We seem to have read something totally invalid.. */
          result = ZIP_ERR_FILE_CORRUPT;
          goto finished;
        }
    }

  zipEntry->filenamePointer = currentEntryPointer + headerSize;
  zipEntry->extraFieldPointer =
    localEntryPointer + 30 + zipEntry->filenameLength;
  zipEntry->dataPointer =
    zipEntry->extraFieldPointer + zipEntry->extraFieldLength;
  zipEntry->extraField = NULL;
  zipEntry->fileCommentPointer = 0;
  zipEntry->fileComment = NULL;
  zipEntry->data = NULL;

  if (sig == ZIP_CentralHeader)
    {
      U_8 buf[2];
      U_8 *buf2 = buf;
      U_16 lost;
      /* Also, we know where the comment is */
      zipEntry->fileCommentPointer = currentEntryPointer + headerSize +
        zipEntry->filenameLength + zipEntry->extraFieldLength;
      if (hyfile_seek (zipFile->fd, localEntryPointer + 28, HySeekSet) ==
          localEntryPointer + 28)
        {
          if (hyfile_read (zipFile->fd, buf, 2) == 2)
            {
              ZIP_NEXT_U16 (lost, buf2);
              zipEntry->dataPointer = zipEntry->extraFieldPointer + lost;
              zipFile->pointer = localEntryPointer + 30;
            }
        }
    }

  if ((sig == ZIP_LocalHeader)
      && (zipEntry->compressionMethod == ZIP_CM_Deflated)
      && (zipEntry->flags & 0x8))
    {
      /* What we just read doesn't tell us how big the compressed data is.  We have to do a heuristic search for a
         valid data descriptor at the end of the compressed text */
      result = scanForDataDescriptor (portLib, zipFile, zipEntry);
      if (result < 0)
        goto finished;
    }

  /* Entry read successfully */

  if (enumerationPointer)
    {
      /* Work out where the next entry is supposed to be */
      *enumerationPointer =
        zipEntry->fileCommentPointer + zipEntry->fileCommentLength;
    }

  if (readBuffer)
    hymem_free_memory (readBuffer);
  return 0;

finished:
  if (readBuffer)
    {
      hymem_free_memory (readBuffer);
    }
  if ((zipEntry->filename)
      && (zipEntry->filename != zipEntry->internalFilename))
    {
      hymem_free_memory (zipEntry->filename);
    }
  zipEntry->filename = NULL;
  if (result == ZIP_ERR_FILE_READ_ERROR)
    {
      zipFile->pointer = -1;
    }
  return result;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_closeZipFile
/**
 * Attempt to close the zipfile. 
 *
 * @param[in] portLib the port library
 * @param[in] zipFile The zip file to be closed
 *
 * @return 0 on success
 * @return 	ZIP_ERR_FILE_CLOSE_ERROR if there is an error closing the file
 * @return 	ZIP_ERR_INTERNAL_ERROR if there is an internal error
 *
*/
I_32 VMCALL
zip_closeZipFile (HyPortLibrary * portLib, struct HyZipFile * zipFile)
{
  PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
  THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
  IDATA fd;

  ENTER ();

  fd = zipFile->fd;
  zipFile->fd = -1;

  if (zipFile->cache && zipFile->cachePool)
    {
      zipCachePool_release (zipFile->cachePool, zipFile->cache);
      zipFile->cache = NULL;
    }
  if ((zipFile->filename) && (zipFile->filename != zipFile->internalFilename))
    {
      hymem_free_memory (zipFile->filename);
    }
  zipFile->filename = NULL;

  if (fd == -1)
    {
      EXIT ();
      return ZIP_ERR_INTERNAL_ERROR;
    }
  if (hyfile_close (fd))
    {
      EXIT ();
      return ZIP_ERR_FILE_CLOSE_ERROR;
    }
  EXIT ();
  return 0;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_establishCache
/** 
 * Called to set up a cache when a zip file is opened with a cachePool but without a cache, or when the
 *	current cache is found to be invalid in some way.
 * 
 * @param[in] portLib the port library
 * @param[in] zipFile the zip file for which we want to establish a cache
 * 
 * The current cache is marked as invalid such that new instances of zip files 
 * won't try to use it and an attempt is made to establish a new cache.
 *
 * @return 0 on success
 * @return 	ZIP_ERR_FILE_READ_ERROR if there is an error reading from zipFile 
 * @return	ZIP_ERR_FILE_OPEN_ERROR if is there is an error opening the file 
 * @return	ZIP_ERR_UNKNOWN_FILE_TYPE if the file type is unknown
 * @return	ZIP_ERR_UNSUPPORTED_FILE_TYPE if the file type is unsupported
 * @return	ZIP_ERR_OUT_OF_MEMORY  if there is not enough memory to complete this call
 * @return	ZIP_ERR_INTERNAL_ERROR if there was an internal error
*/

I_32
zip_establishCache (HyPortLibrary * portLib, HyZipFile * zipFile)
{
  PORT_ACCESS_FROM_PORT (portLib);
  I_32 result;
  I_64 timeStamp, actualFileSize;
  IDATA fileSize, filenameLength;

  if (zipFile->cache)
    {
      if (zipFile->cachePool)
        {
          /* Whack cache timestamp to keep other people from starting to use it (we will create a new one for them
             to start to use instead).  Once all the current users of the cache have stopped using it, it will go away */
          zipFile->cache->zipTimeStamp = -2;
          zipCachePool_release (zipFile->cachePool, zipFile->cache);
        }
      zipFile->cache = NULL;
    }
  if (!zipFile->cachePool)
    {
      return ZIP_ERR_INTERNAL_ERROR;
    }

  /* Check the cachePool for a suitable cache. */
  filenameLength = strlen ((const char *) zipFile->filename);

//  timeStamp = hyfile_lastmod ((const char *) zipFile->filename);
//  actualFileSize = hyfile_length ((const char *) zipFile->filename);
  {
    struct stat st;
    tzset ();
    stat ((mcSignednessBull)zipFile->filename, &st);
    timeStamp = (U_64)st.st_mtime * 1000;
    actualFileSize = (I_64) st.st_size;
  }

  if ((actualFileSize < 0) || (actualFileSize > HYCONST64 (0x7FFFFFFF)))
    {
      return ZIP_ERR_INTERNAL_ERROR;
    }
  fileSize = (IDATA) actualFileSize;

  zipFile->cache =
    zipCachePool_findCache (zipFile->cachePool,
                            (const char *) zipFile->filename, filenameLength,
                            fileSize, timeStamp);
  if (!zipFile->cache)
    {
      /* Build a new cache.  Because caller asked for a cache, fail if we can't provide one */
      zipFile->cache =
        zipCache_new (portLib, (char *) zipFile->filename, filenameLength);
      if (!zipFile->cache)
        return ZIP_ERR_OUT_OF_MEMORY;

      zipFile->cache->zipFileSize = fileSize;
      zipFile->cache->zipTimeStamp = timeStamp;

      result = zip_populateCache (portLib, zipFile);
      if (result != 0)
        {
          zipCache_kill (zipFile->cache);
          zipFile->cache = NULL;
          return result;
        }
      if (!zipCachePool_addCache (zipFile->cachePool, zipFile->cache))
        {
          zipCache_kill (zipFile->cache);
          zipFile->cache = NULL;
          return ZIP_ERR_OUT_OF_MEMORY;
        }
    }
  return 0;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_initZipEntry
/**
 * Initialize a zip entry.
 * 
 * Should be called before the entry is passed to any other zip support functions 
 *
 * @param[in] portLib the port library
 * @param[in] entry the zip entry to init
 *
 * @return none
*/

void
zip_initZipEntry (HyPortLibrary * portLib, HyZipEntry * entry)
{
  memset (entry, 0, sizeof (*entry));
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_freeZipEntry
/**
 * Free any memory associated with a zip entry.
 * 
 * @param[in] portLib the port library
 * @param[in] entry the zip entry we are freeing 
 * 
 * @return none 
 * 
 * @note This does not free the entry itself.
*/

void
zip_freeZipEntry (HyPortLibrary * portLib, HyZipEntry * entry)
{
  PORT_ACCESS_FROM_PORT (portLib);

  if ((entry->filename) && (entry->filename != entry->internalFilename))
    {
      hymem_free_memory (entry->filename);
    }
  entry->filename = NULL;
  if (entry->extraField)
    {
      hymem_free_memory (entry->extraField);
      entry->extraField = NULL;
    }
  if (entry->data)
    {
      hymem_free_memory (entry->data);
      entry->data = NULL;
    }
  if (entry->fileComment)
    {
      hymem_free_memory (entry->fileComment);
      entry->fileComment = NULL;
    }
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_getNextZipEntry
/**
 *	Read the next zip entry at nextEntryPointer into zipEntry.
 *	
 * Any memory held onto by zipEntry may be lost, and therefore
 *	MUST be freed with @ref zip_freeZipEntry first.
 *
 * @param[in] portLib the port library
 * @param[in] zipFile The zip file being read
 * @param[out] zipEntry compressed data is placed here
 * @param[in] nextEntryPointer
 * 
 * @return 0 on success
 * @return 	ZIP_ERR_FILE_READ_ERROR if there is an error reading zipFile
 * @return ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
 * @return 	ZIP_ERR_NO_MORE_ENTRIES if there are no more entries in zipFile
 * @return 	ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
 *
 * @see zip_freeZipEntry
 *
*/
I_32
zip_getNextZipEntry (HyPortLibrary * portLib, HyZipFile * zipFile,
                     HyZipEntry * zipEntry, IDATA * nextEntryPointer)
{
  PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
  THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
  IDATA result;
  BOOLEAN retryAllowed = TRUE;
  IDATA pointer;
  IDATA entryStart;
  I_64 seekResult;

  ENTER ();

retry:
  pointer = *nextEntryPointer;

  /* Seek to the entry's position in the file. */
  if (pointer != zipFile->pointer)
    {
      seekResult = hyfile_seek (zipFile->fd, pointer, HySeekSet);
      if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
        {
          zipFile->pointer = -1;
          EXIT ();
          return ZIP_ERR_FILE_READ_ERROR;
        }
      zipFile->pointer = (I_32) seekResult;

      if (pointer != zipFile->pointer)
        {
          zipFile->pointer = -1;
          EXIT ();
          return ZIP_ERR_FILE_READ_ERROR;
        }
    }

  /* Read the entry */
  entryStart = *nextEntryPointer;
  result =
    readZipEntry (portLib, zipFile, zipEntry, NULL, &pointer, &entryStart,
                  FALSE);
  if (result != 0)
    {
      if (!retryAllowed || (result == ZIP_ERR_NO_MORE_ENTRIES))
        {
          EXIT ();
          return result;
        }
      zip_establishCache (portLib, zipFile);
      retryAllowed = FALSE;
      goto retry;
    }

  if (zipFile->cache)
    {
      /* Validity check: look up filename in the cache... */
      result =
        (IDATA) zipCache_findElement (zipFile->cache,
                                      (const char *) zipEntry->filename,
                                      FALSE);
      if (result != entryStart)
        {
          if (result >= zipFile->cache->startCentralDir)
            {
              /* ! Cache contents are not valid.  Invalidate it and make a new one */
              if (!retryAllowed)
                {
                  EXIT ();
                  return ZIP_ERR_FILE_CORRUPT;  /* should never happen! */
                }
              result = zip_establishCache (portLib, zipFile);
              if (result)
                {
                  /* (silently start operating without a cache if we couldn't make a new one) */
                }
              else
                {
                  retryAllowed = FALSE;
                  goto retry;
                }
            }
          else
            {
              /* We know that the central header for this entry contains info that the
                 local header is missing (comment length and/or uncompressed size) */
              zipEntry->fileCommentPointer = -1;
            }
        }
    }

  *nextEntryPointer = pointer;
  EXIT ();
  return 0;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_getZipEntry
/**
 *	Attempt to find and read the zip entry corresponding to filename.
 *	If found, read the entry into the parameter entry. 
 *
 * If an uncached entry is found, the filename field will be filled in. This
 * memory will have to be freed with @ref zip_freeZipEntry.
 * 
 * @param[in] portLib the port library
 * @param[in] zipFile the file being read from
 * @param[out] entry the zip entry found in zipFile is read to here
 * @param[in] filename the name of the file that corresponds to entry
 * @param[in] findDirectory when true, match a directory even if filename does not end in '/'.
 *			Note findDirectory is only supported (for the JCL) when there is a cache
 *
 * @return 0 on success or one of the following:
 * @return	ZIP_ERR_FILE_READ_ERROR if there is an error reading from zipFile
 * @return	ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
 * @return	ZIP_ERR_ENTRY_NOT_FOUND if a zip entry with name filename was not found
 * @return 	ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
 *
 * @see zip_freeZipEntry
*/

I_32
zip_getZipEntry (HyPortLibrary * portLib, HyZipFile * zipFile,
                 HyZipEntry * entry, const char *filename,
                 BOOLEAN findDirectory)
{
  PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
  THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
  IDATA result, position;
  BOOLEAN retryAllowed = TRUE;
  I_64 seekResult;

  ENTER ();

retry:
  if (zipFile->cache)
    {
      /* Look up filename in the cache. */
      position =
        (IDATA) zipCache_findElement (zipFile->cache, filename,
                                      findDirectory);
      if (position == -1)
        {
          /* Note: we assume the cache is still valid here */
          EXIT ();
          return ZIP_ERR_ENTRY_NOT_FOUND;
        }

      /* Seek to the entry's position in the file. */
      if (zipFile->pointer != position)
        {
          seekResult = hyfile_seek (zipFile->fd, position, HySeekSet);
          if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
            {
              zipFile->pointer = -1;
              EXIT ();
              return ZIP_ERR_FILE_READ_ERROR;
            }
          zipFile->pointer = (I_32) seekResult;

          if (zipFile->pointer != position)
            {
              zipFile->pointer = -1;
              EXIT ();
              return ZIP_ERR_FILE_READ_ERROR;
            }
        }

      /* Read the entry */
      result =
        readZipEntry (portLib, zipFile, entry, filename, NULL, NULL,
                      findDirectory);
      if (result != 0)
        {
          if (!retryAllowed)
            {
              EXIT ();
              return result;
            }
          result = zip_establishCache (portLib, zipFile);       /* invalidate existing cache */
          if (result)
            {
              EXIT ();
              return result;
            }
          retryAllowed = FALSE;
          goto retry;
        }
      EXIT ();
      return 0;
    }
  else
    {
      /* Uh oh -- random access without a cache (SLOW!) */
      position = 0;
      zip_resetZipFile (PORTLIB, zipFile, &position);
      while (TRUE)
        {

          if (zipFile->pointer != position)
            {
              seekResult = hyfile_seek (zipFile->fd, position, HySeekSet);
              if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
                {
                  zipFile->pointer = -1;
                  EXIT ();
                  return ZIP_ERR_FILE_READ_ERROR;
                }
              zipFile->pointer = (I_32) seekResult;

              if (zipFile->pointer != position)
                {
                  zipFile->pointer = -1;
                  EXIT ();
                  return ZIP_ERR_FILE_READ_ERROR;
                }
            }

          result =
            readZipEntry (portLib, zipFile, entry, NULL, &position, NULL,
                          FALSE);
          if (result || !strcmp ((const char *) entry->filename, filename))
            {
              EXIT ();
              return result;
            }

          /* No match.  Reset for next entry */
          zip_freeZipEntry (portLib, entry);
          zip_initZipEntry (portLib, entry);
        }
    }
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_getZipEntryData
/** 
 *	Attempt to read and uncompress the data for the zip entry entry.
 * 
 *	If buffer is non-NULL it is used, but not explicitly held onto by the entry.
 *	If buffer is NULL, memory is allocated and held onto by the entry, and thus
 *	should later be freed with @ref zip_freeZipEntry.
 *
 * @param[in] portLib the port library
 * @param[in] zipFile the zip file being read from.
 * @param[in,out] entry the zip entry
 * @param[in] buffer may or may not be NULL
 * @param[in] bufferSize

 * @return 0 on success
 * @return	ZIP_ERR_FILE_READ_ERROR if there is an error reading from zipEntry
 * @return	ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
 * @return	ZIP_ERR_ENTRY_NOT_FOUND if entry is not found
 * @return 	ZIP_ERR_OUT_OF_MEMORY  if there is not enough memory to complete this call
 * @return 	ZIP_ERR_BUFFER_TOO_SMALL if buffer is too small to hold the comment for zipFile
 *
 * @see zip_freeZipEntry
 *
*/
I_32
zip_getZipEntryData (HyPortLibrary * portLib, HyZipFile * zipFile,
                     HyZipEntry * entry, U_8 * buffer, U_32 bufferSize)
{
  PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
  THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */

  I_32 result;
  U_8 *dataBuffer;
  struct workBuffer wb;
  I_64 seekResult;

  ENTER ();

  wb.portLib = portLib;
  wb.bufferStart = wb.bufferEnd = wb.currentAlloc = 0;

  if (buffer)
    {
      if (bufferSize < entry->uncompressedSize)
        {
          EXIT ();
          return ZIP_ERR_BUFFER_TOO_SMALL;
        }
      dataBuffer = buffer;
    }
  else
    {
      /* Note that this is the first zalloc. This memory must be available to the calling method and is freed explicitly in zip_freeZipEntry. */
      /* Note that other allocs freed in zip_freeZipEntry are not alloc'd using zalloc */
      dataBuffer = zdataalloc (&wb, 1, entry->uncompressedSize);
      if (!dataBuffer)
        {
          EXIT ();
          return ZIP_ERR_OUT_OF_MEMORY;
        }
      entry->data = dataBuffer;
    }

  if (entry->compressionMethod == ZIP_CM_Stored)
    {
      /* No compression - just read the data in. */
      if (zipFile->pointer != entry->dataPointer)
        {
          seekResult =
            hyfile_seek (zipFile->fd, entry->dataPointer, HySeekSet);
          if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
            {
              zipFile->pointer = -1;
              result = ZIP_ERR_FILE_READ_ERROR;
              goto finished;
            }
          zipFile->pointer = (I_32) seekResult;

          if (zipFile->pointer != entry->dataPointer)
            {
              result = ZIP_ERR_FILE_READ_ERROR;
              goto finished;
            }
        }
      result = hyfile_read (zipFile->fd, dataBuffer, entry->compressedSize);
      if (result != (I_32) entry->compressedSize)
        {
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }
      zipFile->pointer += result;
      EXIT ();
      return 0;
    }

  if (entry->compressionMethod == ZIP_CM_Deflated)
    {
      U_8 *readBuffer;

      /* Ensure that the library is loaded. */
     if (checkZipLibrary (portLib))
        {
          result = ZIP_ERR_UNSUPPORTED_FILE_TYPE;
          goto finished;
        }

      /* Read the file contents. */
      readBuffer = zdataalloc (&wb, 1, entry->compressedSize);
      if (!readBuffer)
        {
          result = ZIP_ERR_OUT_OF_MEMORY;
          goto finished;
        }
      if (zipFile->pointer != entry->dataPointer)
        {
          seekResult =
            hyfile_seek (zipFile->fd, entry->dataPointer, HySeekSet);
          if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
            {
              zipFile->pointer = -1;
              zdatafree (&wb, readBuffer);
              result = ZIP_ERR_FILE_READ_ERROR;
              goto finished;
            }
          zipFile->pointer = (I_32) seekResult;

          if (zipFile->pointer != entry->dataPointer)
            {
              zdatafree (&wb, readBuffer);
              result = ZIP_ERR_FILE_READ_ERROR;
              goto finished;
            }
        }
      if (hyfile_read (zipFile->fd, readBuffer, entry->compressedSize) !=
          (I_32) entry->compressedSize)
        {
          zdatafree (&wb, readBuffer);
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }
      zipFile->pointer += (I_32) entry->compressedSize;

      /* Deflate the data. */
      result =
        inflateData (&wb, readBuffer, entry->compressedSize, dataBuffer,
                     entry->uncompressedSize);
      zdatafree (&wb, readBuffer);
      if (result)
        goto finished;
      EXIT ();
      return 0;
    }

  /* Whatever this is, we can't decompress it */
  result = ZIP_ERR_UNSUPPORTED_FILE_TYPE;

finished:
  if (!buffer)
    {
      entry->data = NULL;
      zdatafree (&wb, dataBuffer);
    }
  if (result == ZIP_ERR_FILE_READ_ERROR)
    {
      zipFile->pointer = -1;
    }
  EXIT ();
  return result;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_getZipEntryExtraField
/** 
 *	Read the extra field of entry from the zip file filename. 
 *
 * buffer is used if non-NULL, but is not held onto by entry. 
 *
 * If buffer is NULL, memory is allocated and held onto by entry, and MUST be freed later with
 *	@ref zip_freeZipEntry.
 *
 * @param[in] portLib the port library
 * @param[in] zipFile the zip file being read from.
 * @param[in,out] entry the zip entry concerned
 * @param[in] buffer may or may not be NULL
 * @param[in] bufferSize
 *
 * @return 0 on success or one of the following:
 * @return ZIP_ERR_FILE_READ_ERROR if there is an error reading from zipFile
 * @return 	ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
 * @return 	ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
 * @return 	ZIP_ERR_BUFFER_TOO_SMALL if the buffer was non-Null but not large enough to hold the contents of entry
 *
 * @see zip_freeZipEntry
*/
I_32
zip_getZipEntryExtraField (HyPortLibrary * portLib, HyZipFile * zipFile,
                           HyZipEntry * entry, U_8 * buffer, U_32 bufferSize)
{
  PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
  THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */

  I_32 result;
  U_8 *extraFieldBuffer;
  I_64 seekResult;

  ENTER ();

  if (entry->extraFieldLength == 0)
    {
      EXIT ();
      return 0;
    }

  if (buffer)
    {
      if (bufferSize < entry->extraFieldLength)
        {
          EXIT ();
          return ZIP_ERR_BUFFER_TOO_SMALL;
        }
      extraFieldBuffer = buffer;
    }
  else
    {
      extraFieldBuffer = hymem_allocate_memory (entry->extraFieldLength);
      if (!extraFieldBuffer)
        {
          EXIT ();
          return ZIP_ERR_OUT_OF_MEMORY;
        }
      entry->extraField = extraFieldBuffer;
    }

  if (zipFile->pointer != entry->extraFieldPointer)
    {
      seekResult =
        hyfile_seek (zipFile->fd, entry->extraFieldPointer, HySeekSet);
      if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
        {
          zipFile->pointer = -1;
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }
      zipFile->pointer = (I_32) seekResult;
      if (zipFile->pointer != entry->extraFieldPointer)
        {
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }
    }
  result =
    hyfile_read (zipFile->fd, extraFieldBuffer, entry->extraFieldLength);
  if (result != (I_32) entry->extraFieldLength)
    {
      result = ZIP_ERR_FILE_READ_ERROR;
      goto finished;
    }
  zipFile->pointer += result;
  EXIT ();
  return 0;

finished:
  if (!buffer)
    {
      entry->extraField = NULL;
      hymem_free_memory (extraFieldBuffer);
    }
  if (result == ZIP_ERR_FILE_READ_ERROR)
    zipFile->pointer = -1;
  EXIT ();
  return result;

}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_getZipEntryFilename

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_getZipEntryComment
/**
 *	Read the file comment for entry. 
 *
 * If buffer is non-NULL, it is used, but not held onto by entry. 
 *
 * If buffer is NULL, memory is allocated and
 *	held onto by entry, and thus should later be freed with @ref zip_freeZipEntry.
 *
 * @param[in] portLib the port library
 * @param[in] zipFile the zip file concerned
 * @param[in] entry the entry who's comment we want
 * @param[in] buffer may or may not be NULL
 * @param[in] bufferSize

 * @return 0 on success or one of the following
 * @return	ZIP_ERR_FILE_READ_ERROR if there is an error reading the file comment from zipEntry
 * @return	ZIP_ERR_FILE_CORRUPT if zipFile is corrupt
 * @return	ZIP_ERR_ENTRY_NOT_FOUND if entry is not found
 * @return 	ZIP_ERR_OUT_OF_MEMORY  if there is not enough memory to complete this call
 * @return 	ZIP_ERR_BUFFER_TOO_SMALL if buffer is too small to hold the comment for zipFile
*/

I_32
zip_getZipEntryComment (HyPortLibrary * portLib, HyZipFile * zipFile,
                        HyZipEntry * entry, U_8 * buffer, U_32 bufferSize)
{
  PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
  THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */

  I_32 result;
  U_8 *fileCommentBuffer;
  I_64 seekResult;

  ENTER ();

  if (entry->fileCommentLength == 0)
    {
      if (entry->fileCommentPointer == -1)
        {
          /* TODO: we don't know where the comment is (or even if there is one)! This only happens if you're running
             without a cache, so too bad for now */
        }
      EXIT ();
      return 0;
    }

  if (buffer)
    {
      if (bufferSize < entry->fileCommentLength)
        {
          EXIT ();
          return ZIP_ERR_BUFFER_TOO_SMALL;
        }
      fileCommentBuffer = buffer;
    }
  else
    {
      fileCommentBuffer = hymem_allocate_memory (entry->fileCommentLength);
      if (!fileCommentBuffer)
        {
          EXIT ();
          return ZIP_ERR_OUT_OF_MEMORY;
        }
      entry->fileComment = fileCommentBuffer;
    }

  if (zipFile->pointer != entry->fileCommentPointer)
    {
      seekResult =
        hyfile_seek (zipFile->fd, entry->fileCommentPointer, HySeekSet);
      if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
        {
          zipFile->pointer = -1;
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }
      zipFile->pointer = (I_32) seekResult;

      if (zipFile->pointer != entry->fileCommentPointer)
        {
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }
    }
  result =
    hyfile_read (zipFile->fd, fileCommentBuffer, entry->fileCommentLength);
  if (result != (I_32) entry->fileCommentLength)
    {
      result = ZIP_ERR_FILE_READ_ERROR;
      goto finished;
    }
  zipFile->pointer += result;
  EXIT ();
  return 0;

finished:
  if (!buffer)
    {
      entry->fileComment = NULL;
      hymem_free_memory (fileCommentBuffer);
    }
  if (result == ZIP_ERR_FILE_READ_ERROR)
    {
      zipFile->pointer = -1;
    }
  EXIT ();
  return result;

}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_openZipFile
/**
 * Attempt to open a zip file. 
 * 
 * If the cache pool is non-NULL, the cachePool will be used to find a suitable cache, and if none can be found it will create one and add it to cachePool.
 * Zip support is responsible for managing the lifetime of the cache.  
 *
 * @note If cachePool is NULL, zipFile will be opened without a cache.
 *
 * @param[in] portLib the port library
 * @param[in] filename the zip file to open
 * @param[out] zipFile the zip file structure to populate
 * @param[in] cachePool the cache pool
 * 
 * @return 0 on success
 * @return 	ZIP_ERR_FILE_OPEN_ERROR if is there is an error opening the file
 * @return	ZIP_ERR_FILE_READ_ERROR if there is an error reading the file
 * @return	ZIP_ERR_FILE_CORRUPT if the file is corrupt
 * @return	ZIP_ERR_UNKNOWN_FILE_TYPE if the file type is not known
 * @return	ZIP_ERR_UNSUPPORTED_FILE_TYPE if the file type is unsupported
 * @return	ZIP_ERR_OUT_OF_MEMORY if we are out of memory
*/
I_32
zip_openZipFile (HyPortLibrary * portLib, char *filename, HyZipFile * zipFile,
                 HyZipCachePool * cachePool)
{
  PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
  THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */

  IDATA fd = -1;
  I_32 result = 0;
  I_64 seekResult;
  U_8 buffer[4];
  I_32 len;

  ENTER ();

  len = strlen (filename);
  zipFile->fd = -1;
  zipFile->type = ZIP_Unknown;
  zipFile->cache = NULL;
  zipFile->cachePool = NULL;
  zipFile->pointer = -1;
  /* Allocate space for filename */
  if (len >= ZIP_INTERNAL_MAX)
    {
      zipFile->filename = hymem_allocate_memory (len + 1);
      if (!zipFile->filename)
        {
          EXIT ();
          return ZIP_ERR_OUT_OF_MEMORY;
        }
    }
  else
    {
      zipFile->filename = zipFile->internalFilename;
    }

  strcpy ((char *) zipFile->filename, filename);

  fd = hyfile_open (filename, HyOpenRead, 0);
  if (fd == -1)
    {
      result = ZIP_ERR_FILE_OPEN_ERROR;
      goto finished;
    }

  if (hyfile_read (fd, buffer, 4) != 4)
    {
      result = ZIP_ERR_FILE_READ_ERROR;
      goto finished;
    }

  if ((buffer[0] == 'P') && (buffer[1] == 'K'))
    {
      /* If not the central header or local file header, its corrupt */
      if (!
          ((buffer[2] == 1 && buffer[3] == 2)
           || (buffer[2] == 3 && buffer[3] == 4)))
        {
          result = ZIP_ERR_FILE_CORRUPT;
          goto finished;
        }
      /* PKZIP file. Back up the pointer to the beginning. */
      seekResult = hyfile_seek (fd, 0, HySeekSet);
      if (seekResult != 0)
        {
          result = ZIP_ERR_FILE_READ_ERROR;
          goto finished;
        }

      zipFile->fd = fd;
      zipFile->type = ZIP_PKZIP;
      zipFile->pointer = 0;
    }

  if ((buffer[0] == 0x1F) && (buffer[1] == 0x8B))
    {
      /* GZIP - currently unsupported. 
         zipFile->fd = fd;
         zipFile->type = ZIP_GZIP;
         zipFile->pointer = 2;
       */
      result = ZIP_ERR_UNSUPPORTED_FILE_TYPE;
      goto finished;
    }

  if (zipFile->type == ZIP_Unknown)
    {
      result = ZIP_ERR_UNKNOWN_FILE_TYPE;
      goto finished;
    }

  result = 0;

  if (cachePool)
    {
      zipFile->cachePool = cachePool;
      result = zip_establishCache (portLib, zipFile);
    }

finished:
  if (result == 0)
    {
      zipFile->fd = fd;
      EXIT ();
      return 0;
    }
  if (fd != -1)
    {
      hyfile_close (fd);
    }
  if ((zipFile->filename) && (zipFile->filename != zipFile->internalFilename))
    {
      hymem_free_memory (zipFile->filename);
    }
  zipFile->filename = NULL;
  EXIT ();
  return result;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_resetZipFile
/**
 * Reset nextEntryPointer to the first entry in the file.
 *
 * @param[in] portLib the port library
 * @param[in] zipFile the zip being read from
 * @param[out] nextEntryPointer will be reset to the first entry in the file
 *
 * @return none
 *
 * 
*/
void
zip_resetZipFile (HyPortLibrary * portLib, HyZipFile * zipFile,
                  IDATA * nextEntryPointer)
{
  *nextEntryPointer = 0;
  if (zipFile)
    {
      if (zipFile->cache)
        *nextEntryPointer = zipFile->cache->startCentralDir;
      else
        {
          I_32 result;
          HyZipCentralEnd endEntry;
          result = scanForCentralEnd (portLib, zipFile, &endEntry);
          if (result != 0)
            return;
          *nextEntryPointer = (IDATA) ((UDATA) endEntry.dirOffset);
        }
    }
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zip_getZipEntryFromOffset
/**
 * Attempt to read a zip entry at offset from the zip file provided.
 *	If found, read into entry.
 *
 * @note If an uncached entry is found, the filename field will be filled in. This
 *	memory MUST be freed with @ref zip_freeZipEntry.
 *
 * @param[in] portLib the port library
 * @param[in] zipFile the zip file being read
 * @param[in] offset the offset into the zipFile of the desired zip entry
 * @param[out] entry the compressed data
 *
 * @return 0 on success
 * @return ZIP_ERR_FILE_READ_ERROR if there is an error reading from @ref zipFile
 * @return 	ZIP_ERR_FILE_CORRUPT if @ref zipFile is corrupt
 * @return 	ZIP_ERR_ENTRY_NOT_FOUND if the entry is not found 
 * @return 	ZIP_ERR_OUT_OF_MEMORY if there is not enough memory to complete this call
 *
 * @see zip_freeZipEntry
*/
I_32
zip_getZipEntryFromOffset (HyPortLibrary * portLib, HyZipFile * zipFile,
                           HyZipEntry * entry, IDATA offset)
{
  PORT_ACCESS_FROM_PORT (portLib);
#if defined(HY_NO_THR)
  THREAD_ACCESS_FROM_PORT(portLib);
#endif /* HY_NO_THR */
  I_32 result;
  I_64 seekResult;

  ENTER ();

  if (zipFile->pointer != offset)
    {
      seekResult = hyfile_seek (zipFile->fd, offset, HySeekSet);
      if ((seekResult < 0) || (seekResult > HYCONST64 (0x7FFFFFFF)))
        {
          zipFile->pointer = -1;
          EXIT ();
          return ZIP_ERR_FILE_READ_ERROR;
        }
      zipFile->pointer = (I_32) seekResult;
      if (zipFile->pointer != offset)
        {
          zipFile->pointer = -1;
          EXIT ();
          return ZIP_ERR_FILE_READ_ERROR;
        }
    }

  result = readZipEntry (portLib, zipFile, entry, NULL, NULL, NULL, FALSE);
  EXIT ();
  return result;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zdataalloc
/*
	cached alloc management for zip_getZipEntryData.
*/
void *
zdataalloc (void *opaque, U_32 items, U_32 size)
{
  UDATA *returnVal = 0;
  U_32 byteSize = items * size;
  U_32 allocSize = WORK_BUFFER_SIZE;
  struct workBuffer *wb = (struct workBuffer *) opaque;

  PORT_ACCESS_FROM_PORT (wb->portLib);

  /* Round to UDATA multiple */
  byteSize = (byteSize + (sizeof (UDATA) - 1)) & ~(sizeof (UDATA) - 1);

  if (wb->bufferStart == 0)
    {
      if (byteSize > WORK_BUFFER_SIZE)
        {
          allocSize = byteSize;
        }
      wb->bufferStart = hymem_allocate_memory (allocSize);
      if (wb->bufferStart)
        {
          wb->bufferEnd = (UDATA *) ((UDATA) wb->bufferStart + allocSize);
          wb->currentAlloc = wb->bufferStart;
          wb->cntr = 0;
        }
    }

  if ((wb->bufferStart == 0)
      || (((UDATA) wb->currentAlloc + byteSize) > (UDATA) wb->bufferEnd))
    {
      returnVal = hymem_allocate_memory (byteSize);
    }
  else
    {
      ++(wb->cntr);
      returnVal = wb->currentAlloc;
      wb->currentAlloc = (UDATA *) ((UDATA) wb->currentAlloc + byteSize);
    }
  return returnVal;
}

#undef CDEV_CURRENT_FUNCTION

#define CDEV_CURRENT_FUNCTION zdatafree
/*
	cached alloc management for zip_getZipEntryData.
*/
void
zdatafree (void *opaque, void *address)
{
  struct workBuffer *wb = (struct workBuffer *) opaque;

  PORT_ACCESS_FROM_PORT (wb->portLib);

  if ((address < (void *) wb->bufferStart)
      || (address >= (void *) wb->bufferEnd))
    {
      hymem_free_memory (address);
    }
  else if (--(wb->cntr) == 0)
    {
      hymem_free_memory (wb->bufferStart);
      wb->bufferStart = wb->bufferEnd = wb->currentAlloc = 0;
    }

}

#undef CDEV_CURRENT_FUNCTION
