blob: 056ce9d51028e1edd7cedd79c82231d94abaa88b [file] [log] [blame]
/*
* 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 VM
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "hyport.h"
#include "zipsup.h"
#include "hypool.h"
#define UDATA_TOP_BIT (((UDATA)1)<<(sizeof(UDATA)*8-1))
#define ISCLASS_BIT UDATA_TOP_BIT
#define NOT_FOUND ((UDATA) (~0))
#define OFFSET_MASK (~ISCLASS_BIT)
#define IMPLICIT_ENTRY (~ISCLASS_BIT)
/* This should be a multiple of the page size, minus a few UDATAs in case
the OS allocator needs header space (so we don't waste a page).
If the OS provides a fine-grain allocator (e.g. Windows) then it doesn't really
matter if we don't fit in one page, but the KISS principle applies.. */
#define ACTUAL_CHUNK_SIZE (4096 - 4*sizeof(UDATA) )
typedef struct HaZipChunkHeader
{
struct HaZipChunkHeader *next;
U_8 *beginFree; /* UDATA-aligned, points to first free byte */
U_8 *endFree; /* unaligned, points to the byte after the last free byte */
#if defined(ATOMIC_LONG_ACCESS)
UDATA padding; /* align to 64 */
#endif
} HaZipChunkHeader;
typedef struct HyZipFileEntry
{
char *name;
UDATA nameLength;
UDATA zipFileOffset;
} HyZipFileEntry;
/* a file record can hold a variable number of file entries. */
typedef struct HyZipFileRecord
{
struct HyZipFileRecord *next;
UDATA entryCount;
HyZipFileEntry entry[1];
} HyZipFileRecord;
typedef struct HaZipDirEntry
{
struct HaZipDirEntry *next;
struct HyZipFileRecord *fileList;
struct HaZipDirEntry *dirList;
char *name;
UDATA zipFileOffset;
#if defined(ATOMIC_LONG_ACCESS)
UDATA padding; /* align to 64 */
#endif
} HaZipDirEntry;
/* trick: a HyZipCache * is a pointer to a HyZipCacheEntry which is the first entry
in the first chunk of the cache. This saves us one hymem_allocate_memory
(or probably two if the zipName isn't huge) */
typedef struct HyZipCacheEntry
{
HyZipCache info; /* publicly visible part */
HaZipChunkHeader *currentChunk;
HaZipDirEntry *chunkActiveDir;
HaZipDirEntry root;
} HyZipCacheEntry;
typedef struct HyZipCacheTraversal
{
HyZipCache *zipCache;
HyPortLibrary *portLib;
HaZipDirEntry *dirEntry;
HyZipFileRecord *fileRecord;
UDATA fileRecordPos;
} HyZipCacheTraversal;
void zipCache_freeChunk
PROTOTYPE ((HyPortLibrary * portLib, HaZipChunkHeader * chunk));
HaZipDirEntry *zipCache_searchDirListCaseInsensitive
PROTOTYPE ((HaZipDirEntry * dirEntry, const char *namePtr, UDATA nameSize,
BOOLEAN isClass));
HaZipChunkHeader *zipCache_allocateChunk
PROTOTYPE ((HyPortLibrary * portLib));
HyZipFileEntry *zipCache_addToFileList
PROTOTYPE ((HyZipCacheEntry * zce, HaZipDirEntry * dirEntry,
const char *namePtr, IDATA nameSize, BOOLEAN isClass,
UDATA elementOffset));
UDATA *zipCache_reserveEntry
PROTOTYPE ((HyZipCacheEntry *zce, HaZipChunkHeader * chunk, UDATA entryBytes, UDATA stringBytes));
HyZipFileEntry *zipCache_searchFileList
PROTOTYPE ((HaZipDirEntry * dirEntry, const char *namePtr, UDATA nameSize,
BOOLEAN isClass));
HaZipDirEntry *zipCache_addToDirList
PROTOTYPE ((HyZipCacheEntry * zce, HaZipDirEntry * dirEntry,
const char *namePtr, int nameSize, BOOLEAN isClass));
HaZipDirEntry *zipCache_searchDirList
PROTOTYPE ((HaZipDirEntry * dirEntry, const char *namePtr, UDATA nameSize,
BOOLEAN isClass));
IDATA helper_memicmp
PROTOTYPE ((const void *src1, const void *src2, UDATA length));
/**
* Creates a new, empty zip cache for the provided zip file.
*
* @param[in] portLib the port library
* @param[in] zipName the zip file name
* @param[in] zipNameLength
*
* @return the new zip cache if one was successfully created, NULL otherwise
*
*/
HyZipCache *
zipCache_new (HyPortLibrary * portLib, char *zipName, IDATA zipNameLength, IDATA zipFileSize, I_64 zipTimeStamp, IDATA startCentralDir)
{
HaZipChunkHeader *chunk;
HyZipCacheEntry *zce;
PORT_ACCESS_FROM_PORT (portLib);
chunk = zipCache_allocateChunk (portLib);
if (!chunk)
return NULL;
zce = (HyZipCacheEntry *) zipCache_reserveEntry (NULL, chunk, sizeof (HyZipCacheEntry), 0);
if (!zce)
{
/* ACTUAL_CHUNK_SIZE is so small it can't hold one HyZipCacheEntry?? */
zipCache_freeChunk (portLib, chunk);
return NULL;
}
zce->info.portLib = portLib;
zce->currentChunk = chunk;
/* Try to put the name string in this chunk. If it won't fit, we'll allocate it separately */
if (zipCache_reserveEntry (zce, chunk, 0, zipNameLength + 1))
{
zce->info.zipFileName = chunk->endFree;
}
else
{
zce->info.zipFileName = hymem_allocate_memory (zipNameLength + 1);
if (!zce->info.zipFileName)
{
zipCache_freeChunk (portLib, chunk);
return NULL;
}
}
memcpy (zce->info.zipFileName, zipName, zipNameLength);
zce->info.zipFileName[zipNameLength] = '\0';
zce->info.zipFileSize = zipFileSize;
zce->info.startCentralDir = startCentralDir;
zce->info.zipTimeStamp = zipTimeStamp;
/* zce->info.cachePool is already NULL */
/* zce->info.cachePoolEntry is already NULL */
zce->root.zipFileOffset = 1;
return (HyZipCache *) zce;
}
/**
* Add an association between a file or directory named elementName and offset elementOffset to the zip cache provided
*
* @param[in] zipCache the zip cache being added to
* @param[in] elementName the name of the file or directory element
* @param[in] elementOffset the corresponding offset of the element
*
* @return TRUE if the association was made, FALSE otherwise
*
*/
BOOLEAN
zipCache_addElement (HyZipCache * zipCache, char *elementName,
UDATA elementOffset)
{
HyZipCacheEntry *zce = (HyZipCacheEntry *) zipCache;
HaZipDirEntry *dirEntry;
HyZipFileEntry *fileEntry;
char *curName;
IDATA curSize;
IDATA prefixSize;
BOOLEAN isClass;
if (!zipCache || !elementName || !elementName[0]
|| (elementOffset & ISCLASS_BIT)
|| ((elementOffset & OFFSET_MASK) == IMPLICIT_ENTRY))
return FALSE;
dirEntry = &zce->root;
curName = elementName;
for (;;)
{
HaZipDirEntry *d;
/* scan forwards in curName until '/' or NUL */
for (curSize = 0; curName[curSize] && (curName[curSize] != '/');
curSize++)
/* nothing */ ;
prefixSize = curSize + 1;
isClass = FALSE;
if ((curSize >= 6) && !memcmp (&curName[curSize - 6], ".class", 6))
{
isClass = TRUE;
curSize -= 6;
}
if (!*curName)
{
/* We ran out of string, which means the elementName was */
/* a directory name---in fact, it was the subdir we parsed */
/* last time through the loop. */
if ((dirEntry->zipFileOffset & OFFSET_MASK) != IMPLICIT_ENTRY)
{
/* Can't add the same directory more than once! */
return TRUE;
}
dirEntry->zipFileOffset =
elementOffset | (isClass ? ISCLASS_BIT : 0);
return TRUE;
}
if (curName[curSize] != '/')
{
/* The prefix we're looking at doesn't end with a '/', which means */
/* it is really the suffix of the elementName, and it's a filename. */
fileEntry =
zipCache_searchFileList (dirEntry, curName, curSize, isClass);
if (fileEntry)
{
/* We've seen this file before...update the entry to the new offset. */
fileEntry->zipFileOffset =
elementOffset | (isClass ? ISCLASS_BIT : 0);
}
else
{
if (!zipCache_addToFileList
(zce, dirEntry, curName, curSize, isClass, elementOffset))
return FALSE;
}
return TRUE;
}
/* If we got here, we're looking at a prefix which ends with '/' */
/* Treat that prefix as a subdirectory. If it doesn't exist, create it implicitly */
if (!(d = zipCache_searchDirList (dirEntry, curName, curSize, isClass)))
{
if (!
(d =
zipCache_addToDirList (zce, dirEntry, curName, curSize,
isClass)))
{
return FALSE;
}
}
dirEntry = d;
curName += prefixSize;
}
}
/**
* Returns the offset associated with a file or directory element named elementName
* in a zipCache.
*
* @param[in] zipCache the zip cache we are searching
* @param[in] elementName the name of the element of which we want the offset
* @param[in] searchDirList when TRUE, search the dir list even if elementName does not end in '/'
*
* @return the zipCache if a match is found
* @return -1 if no element of that name has been explicitly added to the cache.
*
*/
UDATA
zipCache_findElement (HyZipCache * zipCache, const char *elementName,
BOOLEAN searchDirList)
{
HyZipCacheEntry *zce = (HyZipCacheEntry *) zipCache;
HaZipDirEntry *dirEntry;
HyZipFileEntry *fileEntry;
const char *curName;
IDATA curSize;
IDATA prefixSize;
BOOLEAN isClass;
if (!zipCache || !elementName || !elementName[0])
return NOT_FOUND;
dirEntry = &zce->root;
curName = elementName;
for (;;)
{
/* scan forwards in curName until '/' or NUL */
for (curSize = 0; curName[curSize] && (curName[curSize] != '/');
curSize++)
/* nothing */ ;
prefixSize = curName[curSize] ? curSize + 1 : curSize;
isClass = FALSE;
if ((curSize >= 6) && !memcmp (&curName[curSize - 6], ".class", 6))
{
isClass = TRUE;
curSize -= 6;
}
if (!*curName)
{
/* We ran out of string, which means the elementName was */
/* a directory name---in fact, it was the subdir we parsed */
/* last time through the loop. */
/* directory may have been implicitly but not explicitly added */
if ((dirEntry->zipFileOffset & OFFSET_MASK) == IMPLICIT_ENTRY)
return NOT_FOUND; /* if it was never added, it doesn't "really" exist! */
return dirEntry->zipFileOffset & OFFSET_MASK;
}
if (curName[curSize] != '/')
{
/* The prefix we're looking at doesn't end with a '/', which means */
/* it is really the suffix of the elementName, and it's a filename. */
fileEntry =
zipCache_searchFileList (dirEntry, curName, curSize, isClass);
if (fileEntry)
{
return fileEntry->zipFileOffset & OFFSET_MASK;
}
if (!searchDirList)
{
return NOT_FOUND;
}
}
/* If we got here, we're looking at a prefix which ends with '/', or searchDirList is TRUE */
/* Treat that prefix as a subdirectory. It will exist if elementName was added before. */
dirEntry = zipCache_searchDirList (dirEntry, curName, curSize, isClass);
if (!dirEntry)
return NOT_FOUND;
curName += prefixSize;
}
}
/**
* Deletes a zip cache and frees its resources
*
* @param[in] zipCache the zip cache to be freed
*
* @return none
*
* @see zipCache_new
*
*/
void
zipCache_kill (HyZipCache * zipCache)
{
HaZipChunkHeader *chunk, *chunk2;
HyZipCacheEntry *zce = (HyZipCacheEntry *) zipCache;
HyPortLibrary *portLib = zce->info.portLib;
PORT_ACCESS_FROM_PORT (portLib);
chunk =
(HaZipChunkHeader *) (((U_8 *) zipCache) - sizeof (HaZipChunkHeader));
if (((UDATA) ((U_8 *) zce->info.zipFileName - (U_8 *) chunk)) >=
ACTUAL_CHUNK_SIZE)
{
/* zce->info.zipFileName points outside the first chunk, therefore it was allocated
separately rather than being reserved from the chunk */
hymem_free_memory (zce->info.zipFileName);
}
chunk = zce->currentChunk;
while (chunk)
{
chunk2 = chunk->next;
zipCache_freeChunk (portLib, chunk);
chunk = chunk2;
}
}
/* Allocate a new HaZipDirEntry and insert into dirEntry's dirList. */
HaZipDirEntry *
zipCache_addToDirList (HyZipCacheEntry * zce, HaZipDirEntry * dirEntry,
const char *namePtr, int nameSize, BOOLEAN isClass)
{
HaZipDirEntry *entry;
HaZipChunkHeader *chunk = zce->currentChunk;
zce->chunkActiveDir = NULL;
entry = (HaZipDirEntry *) zipCache_reserveEntry (zce, chunk, sizeof (*entry), nameSize + 1);
if (!entry)
{
if (!(chunk = zipCache_allocateChunk (zce->info.portLib)))
return NULL;
chunk->next = zce->currentChunk;
zce->currentChunk = chunk;
entry =
(HaZipDirEntry *) zipCache_reserveEntry (zce, chunk, sizeof (*entry), nameSize + 1);
if (!entry)
{
/* ACTUAL_CHUNK_SIZE is so small it can't hold one HaZipDirEntry?? */
return NULL;
}
}
entry->next = dirEntry->dirList;
dirEntry->dirList = entry;
entry->zipFileOffset = IMPLICIT_ENTRY | (isClass ? ISCLASS_BIT : 0);
entry->name = (char *) chunk->endFree;
memcpy (entry->name, namePtr, nameSize);
/* chunk->endFree[nameSize] is already zero (NUL) */
return entry;
}
/* Allocate a new zipFileEntry and insert it into dirEntry's fileList. */
/* If possible, the new file entry will be appended to the active zipFileRecord. */
/* Otherwise, a new zipFileRecord will be allocated to hold the new zipFileEntry. */
HyZipFileEntry *
zipCache_addToFileList (HyZipCacheEntry * zce, HaZipDirEntry * dirEntry,
const char *namePtr, IDATA nameSize, BOOLEAN isClass,
UDATA elementOffset)
{
HyZipFileEntry *entry;
HyZipFileRecord *record;
HaZipChunkHeader *chunk = zce->currentChunk;
if (zce->chunkActiveDir == dirEntry)
{
if ((entry = (HyZipFileEntry *) zipCache_reserveEntry (zce, chunk, sizeof (*entry), nameSize + 1)))
{
/* add to end of existing entry */
zce->chunkActiveDir->fileList->entryCount++;
goto haveEntry;
}
}
record = (HyZipFileRecord *) zipCache_reserveEntry (zce, chunk, sizeof (*record), nameSize + 1);
if (!record)
{
if (!(chunk = zipCache_allocateChunk (zce->info.portLib)))
return NULL;
chunk->next = zce->currentChunk;
zce->currentChunk = chunk;
zce->chunkActiveDir = NULL;
record = (HyZipFileRecord *) zipCache_reserveEntry (zce, chunk, sizeof (*record), nameSize + 1);
if (!record)
{
/* ACTUAL_CHUNK_SIZE is so small it can't hold one zipFileRecord?? */
return NULL;
}
}
record->next = dirEntry->fileList;
dirEntry->fileList = record;
zce->chunkActiveDir = dirEntry;
record->entryCount = 1;
entry = record->entry;
haveEntry:
entry->name = (char *) chunk->endFree;
memcpy (entry->name, namePtr, nameSize);
/* chunk->endFree[nameSize] is already zero (NUL) */
entry->nameLength = nameSize;
entry->zipFileOffset = elementOffset | (isClass ? ISCLASS_BIT : 0);
return entry;
}
/* Allocate a new chunk and initialize its zipChunkHeader. */
HaZipChunkHeader *
zipCache_allocateChunk (HyPortLibrary * portLib)
{
HaZipChunkHeader *chunk;
PORT_ACCESS_FROM_PORT (portLib);
chunk = (HaZipChunkHeader *) hymem_allocate_memory (ACTUAL_CHUNK_SIZE);
if (!chunk)
return NULL;
memset (chunk, 0, ACTUAL_CHUNK_SIZE);
chunk->beginFree = ((U_8 *) chunk) + sizeof (HaZipChunkHeader);
chunk->endFree = ((U_8 *) chunk) + ACTUAL_CHUNK_SIZE;
return chunk;
}
/* Frees a chunk which is no longer used. */
/* portLib must be the original portLib which was passed to zipCache_allocateChunk. */
void
zipCache_freeChunk (HyPortLibrary * portLib, HaZipChunkHeader * chunk)
{
PORT_ACCESS_FROM_PORT (portLib);
hymem_free_memory (chunk);
}
/* Tries to reserve storage in a chunk for entryBytes of header data, and */
/* stringBytes of string data. If there is not enough storage, NULL is */
/* returned and no storage is reserved. If there is enough storage, a */
/* pointer is returned to the allocated entryBytes, and chunk->bottom points */
/* to the allocated stringBytes. */
UDATA *
zipCache_reserveEntry (HyZipCacheEntry * zce, HaZipChunkHeader * chunk, UDATA entryBytes,
UDATA stringBytes)
{
UDATA *entry;
if (!chunk)
return NULL;
if ((chunk->endFree - chunk->beginFree) <
(IDATA) (entryBytes + stringBytes))
return NULL;
entry = (UDATA *) (chunk->beginFree);
chunk->beginFree += entryBytes;
chunk->endFree -= stringBytes;
return entry;
}
/* Searches the dirList in dirEntry for a directory entry named */
/* namePtr[0..nameSize-1] with the specified isClass value. */
HaZipDirEntry *
zipCache_searchDirList (HaZipDirEntry * dirEntry, const char *namePtr,
UDATA nameSize, BOOLEAN isClass)
{
HaZipDirEntry *entry;
if (!dirEntry || !namePtr)
return NULL;
entry = dirEntry->dirList;
while (entry)
{
if (!memcmp (entry->name, namePtr, nameSize) && !entry->name[nameSize])
{
if (isClass && (entry->zipFileOffset & ISCLASS_BIT))
return entry;
if (!isClass && !(entry->zipFileOffset & ISCLASS_BIT))
return entry;
}
entry = entry->next;
}
return NULL;
}
/* Searches the fileList in dirEntry for a file entry named */
/* namePtr[0..nameSize-1] with the specified isClass value. */
HyZipFileEntry *
zipCache_searchFileList (HaZipDirEntry * dirEntry, const char *namePtr,
UDATA nameSize, BOOLEAN isClass)
{
HyZipFileRecord *record;
HyZipFileEntry *entry;
IDATA i;
if (!dirEntry || !namePtr)
return NULL;
record = dirEntry->fileList;
while (record)
{
for (i = record->entryCount; i--;)
{
entry = &record->entry[i];
if (entry->nameLength == nameSize)
{
if (!memcmp (entry->name, namePtr, nameSize))
{
if (isClass && (entry->zipFileOffset & ISCLASS_BIT))
return &record->entry[i];
if (!isClass && !(entry->zipFileOffset & ISCLASS_BIT))
return &record->entry[i];
}
}
}
record = record->next;
}
return NULL;
}
/**
* Searches for a directory named elementName in zipCache and if found provides
* a handle to it that can be used to enumerate through all of the directory's files.
*
* @note The search is CASE-INSENSITIVE (contrast with @ref zipCache_findElement, which is case-sensitive).
* @note The search is NOT recursive.
*
* @param[in] zipCache the zip cache that is being searched
* @param[in] directoryName the directory we want to enumerate
* @param[out] handle enumerate all the files in directory directoryName on this handle
*
* @return 0 on success and sets handle
* @return -1 if the directory is not found
* @return -2 if there is not enough memory to complete this call
*
* @see zipCache_findElement
*/
IDATA
zipCache_enumNew (HyZipCache * zipCache, char *directoryName, void **handle)
{
HyZipCacheEntry *zce = (HyZipCacheEntry *) zipCache;
HaZipDirEntry *dirEntry;
char *curName;
IDATA curSize;
IDATA prefixSize;
BOOLEAN isClass;
if (!zipCache || !directoryName || !directoryName[0] || !handle)
{
return -3;
}
else
{
PORT_ACCESS_FROM_PORT (zce->info.portLib);
dirEntry = &zce->root;
curName = directoryName;
for (;;)
{
/* scan forwards in curName until '/' or NUL */
for (curSize = 0; curName[curSize] && (curName[curSize] != '/');
curSize++)
/* nothing */ ;
prefixSize = curSize + 1;
isClass = FALSE;
/* Note: CASE-INSENSITIVE HERE */
if ((curSize >= 6)
&& !helper_memicmp (&curName[curSize - 6], ".class", 6))
{
isClass = TRUE;
curSize -= 6;
}
if (!*curName)
{
/* We ran out of string, which means directoryName was */
/* the subdir we parsed last time through the loop. Begin the traversal here. */
HyZipCacheTraversal *traversal =
hymem_allocate_memory (sizeof (*traversal));
if (!traversal)
{
return -2;
}
traversal->zipCache = zipCache;
traversal->portLib = zce->info.portLib;
traversal->dirEntry = dirEntry;
traversal->fileRecord = dirEntry->fileList;
traversal->fileRecordPos = 0;
/* ensure an automatically-managed cache doesn't go away while enumerating */
if (zce->info.cachePool)
{
zipCachePool_addRef (zce->info.cachePool, zipCache);
}
*handle = traversal;
return 0;
}
if (curName[curSize] != '/')
{
/* The prefix we're looking at doesn't end with a '/', which means */
/* it is really the suffix of the directoryName, and it's a filename. */
return -1; /* We're not interested in filenames */
}
/* If we got here, we're looking at a prefix which ends with '/' */
/* Treat that prefix as a subdirectory. It will exist if directoryName has been
added or if any file or directory inside directoryName has been added. */
dirEntry =
zipCache_searchDirListCaseInsensitive (dirEntry, curName, curSize,
isClass);
if (!dirEntry)
{
return -1;
}
curName += prefixSize;
}
}
}
/**
* Gets the name and offset of the next element in the directory being enumerated.
*
* If nameBufSize is insufficient to hold the entire name, returns the required size for nameBuf.
* @note Does NOT skip the element if nameBufSize buffer is of insufficient size to hold the entire name.
*
* @param[in] handle returned from @ref zipCache_enumNew. Used to enumerate the elements corresponding to the directory name returned by @ref zipCache_enumGetDirName
* @param[out] nameBuf holder for element in the directory being enumerated
* @param[in] nameBufSize
* @param[out] offset the offset of the next element
*
* @return 0 on success
* @return -1 if all the directories have been returned already
* @return the required size of nameBuf if nameBufSize is insufficient to hold the entire name (does not skip the element)
*
* @see zipCache_enumNew
*
*/
IDATA
zipCache_enumElement (void *handle, char *nameBuf, UDATA nameBufSize,
UDATA * offset)
{
HyZipCacheTraversal *traversal = (HyZipCacheTraversal *) handle;
HyZipFileEntry *fileEntry;
UDATA nameLen;
if (!traversal || !nameBuf || !nameBufSize)
return -3;
if (!traversal->fileRecord)
return -1; /* No more elements */
fileEntry = &traversal->fileRecord->entry[traversal->fileRecordPos];
nameLen = fileEntry->nameLength + 1;
if (fileEntry->zipFileOffset & ISCLASS_BIT)
nameLen += 6;
if (nameBufSize < nameLen)
{
/* Buffer is too small. Return size the caller must allocate to try again. */
return nameLen;
}
memcpy (nameBuf, fileEntry->name, fileEntry->nameLength);
if (fileEntry->zipFileOffset & ISCLASS_BIT)
memcpy (nameBuf + fileEntry->nameLength, ".class", 6);
nameBuf[nameLen - 1] = 0;
if (offset)
*offset = fileEntry->zipFileOffset & OFFSET_MASK;
/* Advance to the next element */
++traversal->fileRecordPos;
if (traversal->fileRecordPos >= traversal->fileRecord->entryCount)
{
traversal->fileRecord = traversal->fileRecord->next;
traversal->fileRecordPos = 0;
}
return 0;
}
/**
* Gets the name of the directory on which the enumeration is based.
*
* @param[in] handle handle returned from @ref zipCache_enumNew.
* @param[out] nameBuf buffer to hold the directory name
* @param[in] nameBufSize
*
* @return 0 on success
* @return -3 on param failures
* @return the required size for nameBuf if nameBufSize is insufficient to hold the entire name
*
*/
IDATA
zipCache_enumGetDirName (void *handle, char *nameBuf, UDATA nameBufSize)
{
HyZipCacheTraversal *traversal = (HyZipCacheTraversal *) handle;
HaZipDirEntry *dirEntry;
UDATA nameLen;
if (!traversal || !nameBuf || !nameBufSize)
return -3;
dirEntry = traversal->dirEntry;
nameLen = strlen (dirEntry->name) + 1 + 1; /* for '/' and null */
if (nameBufSize < nameLen)
{
/* Buffer is too small. Return size the caller must allocate to try again. */
return nameLen;
}
strcpy (nameBuf, dirEntry->name);
strcat (nameBuf, "/");
return 0;
}
/**
* Frees any resources allocated by @ref zipCache_enumNew.
*
* @param[in] handle enumerate on this handle
*
* @return none
*
* @see zipCache_enumNew
*/
void
zipCache_enumKill (void *handle)
{
HyZipCacheTraversal *traversal = (HyZipCacheTraversal *) handle;
if (!traversal)
{
return;
}
else
{
PORT_ACCESS_FROM_PORT (traversal->portLib);
if (traversal->zipCache)
{
zipCachePool_release (traversal->zipCache->cachePool,
traversal->zipCache);
}
hymem_free_memory (traversal);
}
}
/* Searches the dirList in dirEntry for a directory entry named */
/* namePtr[0..nameSize-1] with the specified isClass value. */
HaZipDirEntry *
zipCache_searchDirListCaseInsensitive (HaZipDirEntry * dirEntry,
const char *namePtr, UDATA nameSize,
BOOLEAN isClass)
{
HaZipDirEntry *entry;
if (!dirEntry || !namePtr)
return NULL;
entry = dirEntry->dirList;
while (entry)
{
if (!helper_memicmp (entry->name, namePtr, nameSize)
&& !entry->name[nameSize])
{
if (isClass && (entry->zipFileOffset & ISCLASS_BIT))
return entry;
if (!isClass && !(entry->zipFileOffset & ISCLASS_BIT))
return entry;
}
entry = entry->next;
}
return NULL;
}
/* Returns zero if the two strings are equal over the first length characters. Otherwise,
returns 1 or -1 ala stricmp. */
IDATA
helper_memicmp (const void *src1, const void *src2, UDATA length)
{
char const *s1 = (char const *) src1;
char const *s2 = (char const *) src2;
UDATA i;
for (i = 0; i < length; i++)
{
if (toupper (s1[i]) > toupper (s2[i]))
return 1;
if (toupper (s1[i]) < toupper (s2[i]))
return -1;
}
return 0;
}
/**
* Return the startCentralDir of the cache.
*
* @param[in] zipCache the zip cache
*
* @return the startCentralDir of the cache
*/
IDATA zipCache_getStartCentralDir(HyZipCache *zipCache) {
return zipCache->startCentralDir;
}
/**
* Whack the cache timestamp to keep other people from starting to use it. Once all the current
* users of the cache have stopped using it, it will go away.
*
* @param[in] zipCache the zip cache
*/
void zipCache_invalidateCache(HyZipCache * zipCache) {
zipCache->zipTimeStamp = -2;
}