You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by dl...@apache.org on 2007/08/13 00:04:45 UTC

svn commit: r565171 - /harmony/enhanced/sandbox/bootjvm/bootJVM/jvm/src/jarutil.c

Author: dlydick
Date: Sun Aug 12 15:04:45 2007
New Revision: 565171

URL: http://svn.apache.org/viewvc?view=rev&rev=565171
Log:
Utility functions for operating on Java archive files.

Added:
    harmony/enhanced/sandbox/bootjvm/bootJVM/jvm/src/jarutil.c   (with props)

Added: harmony/enhanced/sandbox/bootjvm/bootJVM/jvm/src/jarutil.c
URL: http://svn.apache.org/viewvc/harmony/enhanced/sandbox/bootjvm/bootJVM/jvm/src/jarutil.c?view=auto&rev=565171
==============================================================================
--- harmony/enhanced/sandbox/bootjvm/bootJVM/jvm/src/jarutil.c (added)
+++ harmony/enhanced/sandbox/bootjvm/bootJVM/jvm/src/jarutil.c Sun Aug 12 15:04:45 2007
@@ -0,0 +1,1724 @@
+/*!
+ * @file jarutil.c
+ *
+ * @brief Java archive utilities.
+ *
+ * The Java archiver creates, maintains, and extracts Java archive
+ * files.  This file format is a Java support utility for storing
+ * Java class files, properties files, and any arbitrary data used
+ * by Java programs.
+ *
+ * This file is used both by the JVM java archive extraction and
+ * by the JAR utility.  The contents are organized as an API library
+ * with function definitions and return codes.
+ *
+ * This code was written with guidance from the 'contrib/minizip'
+ * sample application of ZLIB version 1.2.3.  For further information,
+ * please see www.info-zip.org .
+ *
+ *
+ * @section Control
+ *
+ * \$URL: 
+ *
+ * \$Id: 
+ *
+ * Copyright 2006 The Apache Software Foundation
+ * or its licensors, as applicable.
+ *
+ * Licensed 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.
+ *
+ * @version $
+ *
+ * @date $
+ *
+ * @author $
+ *
+ * @section Reference
+ *
+ */
+
+#include "arch.h"
+ARCH_SOURCE_COPYRIGHT_APACHE(jarutil, c,
+"$URL$",
+"$Id$");
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#warning Make sure "zlib.h" indicates correct compression library.
+#include "zlib.h"
+
+#include "jvmcfg.h"
+#include "jar.h"
+#include "util.h"
+
+/*!
+ * @name Heap management functions to pass to ZLIB compression library.
+ *
+ * ZLIB requires a heap allocator and deallocator function for its
+ * normal operation.  If none is provided, it will choose a default,
+ * presumably a straight @b malloc(3) and @b free(3) function.  In order
+ * to avoid this default, the current @link #jar_state jar_state@endlink
+ * structure pointer is passed in, which names the proper functions
+ * to use for memory management.  When ZLIB calls are made,
+ * these values are used to redirect the fixed name function
+ * calls into the proper heap management functions as used in the rest
+ * of the archiving code.
+ *
+ * The reason that the usual function
+ * @link #jar_state.heap_get_data jar_state.heap_get_data@endlink
+ * cannot be used dirctly is that the ZLIB function prototypes are
+ * somewhat different than the standard @b malloc(3) and @b free(3)
+ * library functions in that the allocator takes two parameters for
+ * the amount of memory to be reserved.  Beyond this prototype
+ * difference, they both function as expected.
+ *
+ * The definitions here use the ZLIB prototypes and map them into
+ * the heap management style used here.
+ *
+ */
+
+/*@{ */ /* Begin grouped definitions */
+
+/*!
+ * @brief Fixed-name heap allocator function for ZLIB calls.
+ *
+ * This function name is passed to ZLIB for use in its internal
+ * memory management when it needs heap allocation.  Its name is
+ * fixed so it may be used regardless of the heap allocation approach
+ * configured into this code.
+ *
+ * If no function has been chosen, then the @b simple heap management
+ * rules will be invoked.
+ *
+ * @param opaque  Custom memory management hook provided by ZLIB.
+ *                Not used in this implementation.
+ *
+ * @param items   Number of items of length @b size to reserve
+ *
+ * @param size    Number of bytes to reserve for each item
+ *
+ *
+ * @returns Untyped pointer to allocated heap area.
+ *
+ *
+ * @see jarutil_zlib_free()
+ *
+ * @warning This function @e must be used in conjunction with
+ *          jarutil_zlib_free() for correct operation of
+ *          this mechanism.  Results are undefined otherwise
+ *
+ */
+
+static /* alloc_func */ voidpf jarutil_zlib_malloc(voidpf opaque,
+                                                   uInt items,
+                                                   uInt size)
+{
+    ARCH_FUNCTION_NAME(jarutil_zlib_malloc);
+
+    jar_state *pjs = (jar_state *) opaque;
+
+    rvoid *rc = (pjs->heap_get_data)((rint)(items*size), rfalse);
+
+    /* If no memory available, complain */
+    if (rc != (void *) rnull)
+    {
+        return((voidpf) rc);
+    }
+/*NOTREACHED*/
+
+    /*
+     * In truth, this will never occur with this definition
+     * of heap allocation-- an exception will instead
+     * kill the program.
+     */
+    sysErrMsg(arch_function_name, "ZLIB alloc_func out of memory");
+    return((voidpf) Z_NULL);
+
+} /* END of jarutil_zlib_malloc() */
+
+
+/*!
+ * @brief Fixed-name heap deallocator function for ZLIB calls.
+ *
+ * This function name is passed to ZLIB for use in its internal
+ * memory management when it needs heap deallocation.  Its name is
+ * fixed so it may be used regardless of the heap allocation approach
+ * configured into this code.
+ *
+ * If no function has been chosen, then the @b simple heap management
+ * rules will be invoked.
+ *
+ * @param opaque  Custom memory management hook provided by ZLIB.
+ *                Not used in this implementation.
+ *
+ * @param address Untyped pointer to allocated heap area as returned
+ *                previously by jarutil_zlib_malloc().
+ *
+ * @returns nothing
+ *
+ *
+ * @see jarutil_zlib_malloc()
+ *
+ * @warning This function @e must be used in conjunction with
+ *          jarutil_zlib_malloc() for correct operation of
+ *          this mechanism.  Results are undefined otherwise
+ *
+ */
+
+static /* free_func */ rvoid jarutil_zlib_free(voidpf opaque,
+                                               voidpf address)
+{
+    ARCH_FUNCTION_NAME(jarutil_zlib_free);
+
+    jar_state *pjs = (jar_state *) opaque;
+
+    (pjs->heap_free_data)((rvoid *) address);
+    return;
+
+} /* END of jarutil_zlib_free() */
+
+
+/*@} */ /* End of grouped definitions */
+
+
+/*!
+ * @brief Convert ZLIB return code to text string
+ *
+ * @param zlib_code  Integer code from <zlib.h> definitions
+ *
+ *
+ * @returns text string representation of @b zlib_code in the
+ * context of @b inflateInit() and @b inflate().  (There are
+ * other interpretations of these codes for other ZLIB functions.)
+ *
+ */
+static rchar *jarutil_zlib_code(rint zlib_code)
+{
+    switch(zlib_code)
+    {
+        /* Non-negative conditions */
+        case Z_OK:           return("not finished");
+        case Z_NEED_DICT:    return("not finished");
+        case Z_STREAM_END:   return("valid data");
+
+        /* Negative errors */
+        case Z_ERRNO:        return("system call error");
+        case Z_STREAM_ERROR: return("bad parm block");
+        case Z_DATA_ERROR:   return("corrupt input data");
+        case Z_MEM_ERROR:    return("out of memory");
+        case Z_BUF_ERROR:    return("buffer full");
+        default:             return("unknown ZLIB code");
+    }
+/*NOTREACHED*/
+} /* END of jarutil_zlib_code() */
+
+
+/*!
+ * @brief Search for location of global trailer in a Java archive file.
+ *
+ *
+ * @param[in,out] pjs                State of open Java archive file.
+ *                                   At entrance, the @b fd member
+ *                                   contains the file descriptor of
+ *                                   a JAR file that has been opened
+ *                                   and is ready to examine.
+ *                                   At exit, several members are
+ *                                   loaded with information about
+ *                                   the contents of the file.
+ *                                   Of particular interest is the
+ *                                   @link jar_global_trailer
+                                     trailer@endlink structure, which
+ *                                   contains the global trailer
+ *                                   information from the JAR file,
+ *                                   also its @link jar_global_trailer
+                                     trailer_position@endlink
+ *                                   member, which contains the offset
+ *                                   in that file of the beginning of
+ *                                   the global trailer.
+ *
+ * @returns jar_api_enum status of file system activity,
+ *          particularly @b JAR_OKAY when successful.
+ *          Other values indicate errors.
+ *
+ */
+
+jar_api_enum jarutil_locate_global_trailer(jar_state *pjs)
+{
+    ARCH_FUNCTION_NAME(jarutil_locate_global_trailer);
+
+    ruint max_global_seek = JAR_MAX_TRAILER_SIZE;
+
+    ruint read_back_size  = JAR_NUM_TRAILER_SIGNATURE_BYTES;
+
+
+    /* Seek to EOF for reading back to directory, report any errors */
+    pjs->archive_length = portable_lseek(pjs->fd, 0, SEEK_END);
+
+    if (JAR_MEMBER_IMPOSSIBLE_LSEEK == (rint) pjs->archive_length)
+    {
+        sysDbgMsg(DML2, pjs->jar_filename, JAR_MSG_END_SEEK);
+        pjs->jar_code = JAR_LSEEK;
+        pjs->jar_msg  = JAR_MSG_END_SEEK;
+        return(pjs->jar_code);
+    }
+
+    /* If file is shorter than max trailer, adjust maximum trailer */
+    if (max_global_seek > pjs->archive_length)
+    {
+        max_global_seek = pjs->archive_length;
+    }
+
+    /*
+     * All failure in heap allocation is fatal and is handled 
+     * in a non-local manner, so no error processing is needed.
+     */
+    pjs->bfr = (pjs->heap_get_data)(JAR_READ_BLOCK_SIZE +
+                                      JAR_NUM_TRAILER_SIGNATURE_BYTES,
+                                    rfalse);
+
+    /* Most important return value from valid read */
+    pjs->trailer_position = JAR_MEMBER_IMPOSSIBLE_LSEEK;
+
+    while (read_back_size < max_global_seek)
+    {
+        rint read_position;
+        rint read_size;
+        rint idx;
+
+        if (read_back_size + JAR_READ_BLOCK_SIZE > max_global_seek)
+        {
+            read_back_size = max_global_seek;
+        }
+        else
+        {
+            read_back_size += JAR_READ_BLOCK_SIZE;
+        }
+
+        read_position = pjs->archive_length - read_back_size;
+
+        if ((JAR_READ_BLOCK_SIZE + JAR_NUM_TRAILER_SIGNATURE_BYTES) <
+            (pjs->archive_length - read_position))
+        {
+            read_size = JAR_READ_BLOCK_SIZE +
+                          JAR_NUM_TRAILER_SIGNATURE_BYTES;
+        }
+        else
+        {
+            read_size = pjs->archive_length - read_position;
+        }
+
+        if (read_position !=
+            portable_lseek(pjs->fd, read_position, SEEK_SET))
+        {
+            sysDbgMsg(DML2, pjs->jar_filename, JAR_MSG_TRAILER_SEEK);
+
+            /* Clean up heap after error */
+            (pjs->heap_free_data)(pjs->bfr);
+            pjs->bfr = (rchar *) rnull;
+
+            /* Report invalid seek */
+            pjs->jar_code = JAR_LSEEK;
+            pjs->jar_msg  = JAR_MSG_TRAILER_SEEK;
+            return(pjs->jar_code);
+        }
+
+        if (read_size != portable_read(pjs->fd, pjs->bfr, read_size))
+        {
+            sysDbgMsg(DML2, pjs->jar_filename, JAR_MSG_TRAILER_READ);
+
+            /* Clean up heap after error */
+            (pjs->heap_free_data)(pjs->bfr);
+            pjs->bfr = (rchar *) rnull;
+
+
+            /* Report invalid read */
+            pjs->jar_code = JAR_READ;
+            pjs->jar_msg  = JAR_MSG_TRAILER_READ;
+            return(pjs->jar_code);
+        }
+
+        for (idx = read_size - JAR_NUM_TRAILER_SIGNATURE_BYTES;
+             idx >= 0;
+             idx--)
+        {
+            if ((JAR_TRAILER_SIGNATURE_BYTE1 == pjs->bfr[idx + 0]) &&
+                (JAR_TRAILER_SIGNATURE_BYTE2 == pjs->bfr[idx + 1]) &&
+                (JAR_TRAILER_SIGNATURE_BYTE3 == pjs->bfr[idx + 2]) &&
+                (JAR_TRAILER_SIGNATURE_BYTE4 == pjs->bfr[idx + 3]))
+            {
+                /* Error if signature found but not rest of trailer */
+                if (read_size <
+                    (idx + JAR_NUM_TRAILER_SIGNATURE_BYTES
+                         + sizeof(rushort) /*volume number */
+                         + sizeof(rushort) /*vol w/ start of directory*/
+                         + sizeof(rushort) /*dir entries in _this_ vol*/
+                         + sizeof(rushort) /*total directory entries */
+                         + sizeof(ruint)   /*size of directory */
+                         + sizeof(ruint)   /*start of directory (offset)
+                                               on starting disk */
+                         + sizeof(rushort) /*size of global trailer */))
+                {
+                    /* Clean up heap before reporting error */
+                    (pjs->heap_free_data)(pjs->bfr);
+                    pjs->bfr = (rchar *) rnull;
+
+                    sysDbgMsg(DML2,
+                              pjs->jar_filename,
+                              JAR_MSG_TRAILER_TRUNCATED);
+
+                    /* Report wrong position in global trailer */
+                    pjs->jar_code = JAR_BAD_TRAILER;
+                    pjs->jar_msg  = JAR_MSG_TRAILER_TRUNCATED;
+                    return(pjs->jar_code);
+                }
+
+                /* Record global trailer information for return */
+                pjs->trailer_position = read_position + idx;
+
+                /* Skip recording signature itself, its use is over */
+                pjs->trailer.volume_number =
+                   bytegames_getrs2_le((rushort *) &pjs->bfr[idx + 4]);
+                pjs->trailer.volume_with_directory_start =
+                   bytegames_getrs2_le((rushort *) &pjs->bfr[idx + 6]);
+                pjs->trailer.this_volume_directory_entries =
+                   bytegames_getrs2_le((rushort *) &pjs->bfr[idx + 8]);
+                pjs->trailer.total_directory_entries =
+                   bytegames_getrs2_le((rushort *) &pjs->bfr[idx + 10]);
+                pjs->trailer.total_directory_size =
+                   bytegames_getri4_le((ruint *) &pjs->bfr[idx + 12]);
+                pjs->trailer.directory_position =
+                   bytegames_getri4_le((ruint *) &pjs->bfr[idx + 16]);
+                pjs->trailer.trailer_size =
+                   bytegames_getrs2_le((rushort *) &pjs->bfr[idx + 20]);
+
+                /* Clean up heap before finishing */
+                (pjs->heap_free_data)(pjs->bfr);
+                pjs->bfr = (rchar *) rnull;
+
+                /* check for value mismatches and unsupported values */
+                if (
+                    (pjs->trailer.volume_number != 0)               ||
+                    (pjs->trailer.volume_with_directory_start != 0) ||
+                    (pjs->trailer.total_directory_entries != 
+                     pjs->trailer.this_volume_directory_entries)    ||
+
+                    /* Also _must_ have directory _before_ trailer */
+                    (pjs->trailer_position < 
+                     (pjs->trailer.directory_position +
+                      pjs->trailer.total_directory_size)))
+                {
+                    sysDbgMsg(DML2,
+                              pjs->jar_filename,
+                              JAR_MSG_TRAILER_CONFLICT);
+
+                    pjs->jar_code = JAR_BAD_TRAILER;
+                    pjs->jar_msg  = JAR_MSG_TRAILER_CONFLICT;
+                    return(pjs->jar_code);
+                }
+
+                /* Report that a valid global trailer has been read */
+                pjs->jar_code = JAR_OKAY;
+                pjs->jar_msg  = (rchar *) rnull;
+                return(pjs->jar_code);
+            }
+        }
+    }
+
+    /* Clean up heap and report member not found */
+    (pjs->heap_free_data)(pjs->bfr);
+    pjs->bfr = (rchar *) rnull;
+
+    sysDbgMsg(DML2, pjs->jar_filename, JAR_MSG_TRAILER_MISSING);
+
+    pjs->jar_code = JAR_TRAILER_NOT_FOUND;
+    pjs->jar_msg  = JAR_MSG_TRAILER_MISSING;
+    return(pjs->jar_code);
+
+} /* END of jarutil_locate_global_trailer() */
+
+
+/*!
+ * @brief Convert DOS date to generic data.
+ *
+ * Convert MS-DOS date format from directory entry into form
+ * usable by any code.  This is reminiscent of
+ * <b><code>(struct tm)</code></b> in the header file @e <time.h>
+ * in the naming of the members.
+ *
+ *
+ * @param[in]  dos_date  MS-DOS format date stamp.
+ *
+ * @param[out] ptm_zip   Pointer to generic format date structure.
+ *
+ *
+ * @returns @link #rvoid rvoid@endlink
+ *
+ */
+
+rvoid jarutil_dos_date_to_generic(ruint dos_date, tm_zip *ptm_zip)
+{
+    ARCH_FUNCTION_NAME(jarutil_dos_date_to_generic);
+
+    ruint date_half = (dos_date >> 16) & 0xffff;
+    ruint time_half = (dos_date      ) & 0xffff;
+
+    ptm_zip->tm_mday = (ruint)  ((date_half & 0x001f));
+    ptm_zip->tm_mon  = (ruint) (((date_half & 0x01e0) >>  5) - 1);
+    ptm_zip->tm_year = (ruint) (((date_half & 0xfe00) >>  9) + 1980);
+
+    ptm_zip->tm_hour = (ruint)  ((time_half & 0xf800) >> 11);
+    ptm_zip->tm_min  = (ruint)  ((time_half & 0x07e0) >>  5);
+    ptm_zip->tm_sec  = (ruint) (((time_half & 0x001f)      ) * 2);
+
+} /* END of jarutil_dos_date_to_generic() */
+
+
+/*!
+ * @brief Search for the directory of a Java archive file.
+ *
+ * This means searching for the global trailer, then reading it,
+ * which includes reading the position of the directory, among
+ * other fields.
+ *
+ * @param[in,out] pjs  State of open Java archive file.  Of particular
+ *                     interest is the @link #jar_global_trailer
+                       trailer@endlink structure, which contains
+ *                     the global trailer information from the JAR
+ *                     file, also its @link #jar_global_trailer
+                       trailer_position@endlink member, which contains
+ *                     the offset in that file of the beginning of
+ *                     the global trailer.  Likewise, its
+ *                  @link #jar_global_trailer directory_position@endlink
+ *                     member contains the offset in that file of the
+ *                     beginning of the directory.
+ *
+ *
+ * @returns jar_api_enum status of file system activity,
+ *          particularly @b JAR_OKAY when successful.
+ *          Other values indicate errors.
+ *
+ */
+
+jar_api_enum jarutil_locate_zip_directory(jar_state *pjs)
+{
+    ARCH_FUNCTION_NAME(jarutil_locate_zip_directory);
+
+    jar_api_enum rc;
+
+
+    /*
+     * Search for global trailer at end of Java archive.
+     * From here, all member files may be located.
+     */
+    rc = jarutil_locate_global_trailer(pjs);
+    if (JAR_OKAY != rc)
+    {
+        return(rc);
+    }
+
+    /*
+     * Calculate gap between start of global trailer (at EOF) and
+     * end of directory, which should immediately precede it.
+     */
+    pjs->trailer_directory_gap =
+        pjs->trailer_position -
+        pjs->trailer.directory_position -
+        pjs->trailer.total_directory_size;
+
+    pjs->jar_code = JAR_OKAY;
+    pjs->jar_msg  = (rchar *) rnull;
+    return(JAR_OKAY);
+
+} /* END of jarutil_locate_zip_directory() */
+
+
+/*!
+ * @brief Search for next member file in a Java archive file.
+ *
+ *
+ * If run immediately after jarutil_locate_zip_directory() and
+ * seeking to the start of the directory via
+ *
+ * <b><code>portable_lseek(pjs->fd,
+                           pjs->trailer.directory_position,
+                           SEEK_SET)</code></b>
+ *
+ * then the directory entry of the first member file will be
+ * retrieved.  After this first time, the second and following
+ * members will be retrieved.
+ *
+ *
+ * @param[in,out] pjs    State of open Java archive file.  Of
+ *                       particular interest is the
+ *               @link #jar_directory_entry pjs->directory_entry@endlink
+ *                       member, which contains the directory entry of
+ *                       that member file in the archive.  A compressed
+ *                       read will extract it from the archive.
+ *
+ * @param[in] direntidx  Number of entries from beginning of directory.
+ *                       The first entry is at index zero.  This is
+ *                       used only for reporting in error messages.
+ *
+ *
+ * @returns jar_api_enum status of file system activity,
+ *          particularly @b JAR_OKAY when successful.
+ *          Other values indicate errors.
+ *
+ */
+
+jar_api_enum jarutil_read_next_directory_entry(jar_state *pjs,
+                                               rushort    direntidx)
+{
+    ARCH_FUNCTION_NAME(jarutil_read_next_directory_entry);
+
+    ruint lseek_next_entry;
+
+    /*
+     * Read the directory entry up to before the file name.
+     * Use the file name area to read, then byte-swap into
+     * the individual fields.  This can be done for two
+     * reasons:  (1) the data format is fixed and well established,
+     * and (2) the file name field is much large than the requested
+     * data.  Therefore, no temporary buffer storage is needed.
+     */
+#define DIRECTORY_ENTRY_READ_SIZE                               \
+           (JAR_NUM_DIRECTORY_SIGNATURE_BYTES +                 \
+            sizeof(pjs->directory_entry.version) +              \
+            sizeof(pjs->directory_entry.version_needed) +       \
+            sizeof(pjs->directory_entry.flags) +                \
+            sizeof(pjs->directory_entry.compression_method) +   \
+            sizeof(pjs->directory_entry.dos_date) +             \
+            sizeof(pjs->directory_entry.crc32) +                \
+            sizeof(pjs->directory_entry.compressed_size) +      \
+            sizeof(pjs->directory_entry.uncompressed_size) +    \
+            sizeof(pjs->directory_entry.size_member_filename) + \
+            sizeof(pjs->directory_entry.size_member_extra) +    \
+            sizeof(pjs->directory_entry.size_member_comment) +  \
+            sizeof(pjs->directory_entry.disk_num_start) +       \
+            sizeof(pjs->directory_entry.internal_fa) +          \
+            sizeof(pjs->directory_entry.external_fa) +          \
+            sizeof(pjs->directory_entry.member_offset))
+
+#define TRANSCRIBE_DIRENT_FIELD(name, type, lefn, idx)                 \
+    pjs->directory_entry.name =                                        \
+        bytegames_getr##lefn##_le((type *)                             \
+                             &pjs->directory_entry.member_filename[idx])
+
+    /*
+     * Read next directory entry.
+     */
+
+    if (DIRECTORY_ENTRY_READ_SIZE !=
+        portable_read(pjs->fd,
+                      pjs->directory_entry.member_filename,
+                      DIRECTORY_ENTRY_READ_SIZE))
+    {
+        sysDbgMsg(DML2, pjs->jar_filename, JAR_MSG_DIRENT_READ);
+
+        pjs->jar_code = JAR_READ;
+        pjs->jar_msg  = JAR_MSG_DIRENT_READ;
+        return(pjs->jar_code);
+    }
+
+    /* Transcribe and validate directory signature */
+    pjs->directory_entry.magic[0] =
+                                pjs->directory_entry.member_filename[0];
+    pjs->directory_entry.magic[1] =
+                                pjs->directory_entry.member_filename[1];
+    pjs->directory_entry.magic[2] =
+                                pjs->directory_entry.member_filename[2];
+    pjs->directory_entry.magic[3] =
+                                pjs->directory_entry.member_filename[3];
+
+    if ((JAR_DIRECTORY_SIGNATURE_BYTE1 !=
+                                      pjs->directory_entry.magic[0] ) ||
+        (JAR_DIRECTORY_SIGNATURE_BYTE2 !=
+                                      pjs->directory_entry.magic[1] ) ||
+        (JAR_DIRECTORY_SIGNATURE_BYTE3 !=
+                                      pjs->directory_entry.magic[2] ) ||
+        (JAR_DIRECTORY_SIGNATURE_BYTE4 !=
+                                      pjs->directory_entry.magic[3] ))
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_DIRENT_CORRUPT ": index '%d'",
+                  direntidx);
+
+        pjs->jar_code = JAR_BAD_DIRECTORY_ENTRY;
+        pjs->jar_msg  = JAR_MSG_DIRENT_CORRUPT;
+        return(pjs->jar_code);
+    }
+
+    /* Transcribe other fields */
+    TRANSCRIBE_DIRENT_FIELD(version,              rushort, s2, 4);
+    TRANSCRIBE_DIRENT_FIELD(version_needed,       rushort, s2, 6);
+    TRANSCRIBE_DIRENT_FIELD(flags,                rushort, s2, 8);
+    TRANSCRIBE_DIRENT_FIELD(compression_method,   rushort, s2, 10);
+    TRANSCRIBE_DIRENT_FIELD(dos_date,             ruint,   i4, 12);
+    TRANSCRIBE_DIRENT_FIELD(crc32,                ruint,   i4, 16);
+    TRANSCRIBE_DIRENT_FIELD(compressed_size,      ruint,   i4, 20);
+    TRANSCRIBE_DIRENT_FIELD(uncompressed_size,    ruint,   i4, 24);
+    TRANSCRIBE_DIRENT_FIELD(size_member_filename, rushort, s2, 28);
+    TRANSCRIBE_DIRENT_FIELD(size_member_extra,    rushort, s2, 30);
+    TRANSCRIBE_DIRENT_FIELD(size_member_comment,  rushort, s2, 32);
+    TRANSCRIBE_DIRENT_FIELD(disk_num_start,       rushort, s2, 34);
+    TRANSCRIBE_DIRENT_FIELD(internal_fa,          rushort, s2, 36);
+    TRANSCRIBE_DIRENT_FIELD(external_fa,          ruint,   i4, 38);
+    TRANSCRIBE_DIRENT_FIELD(member_offset,        ruint,   i4, 42);
+
+    /* Convert date into generic format */
+    jarutil_dos_date_to_generic(pjs->directory_entry.dos_date,
+                                &pjs->directory_entry.generic_date);
+
+    /*
+     * Verify valid file name length (to avoid read() buffer overflow)
+     */
+    if (JAR_MAX_MEMBER_FILENAME_LENGTH <
+        pjs->directory_entry.size_member_filename)
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_DIRENT_FILENAME_SIZE ": index %d",
+                  direntidx);
+
+        pjs->jar_code = JAR_BAD_DIRECTORY_ENTRY;
+        pjs->jar_msg  = JAR_MSG_DIRENT_FILENAME_SIZE;
+        return(pjs->jar_code);
+    }
+
+    /*
+     * Verify valid volume number (no multi-volume support)
+     */
+    if (0 != pjs->directory_entry.disk_num_start)
+    {
+        sysDbgMsg(DML2, pjs->jar_filename, JAR_MSG_DIRENT_MULTIVOLUME);
+
+        pjs->jar_code = JAR_BAD_DIRECTORY_ENTRY;
+        pjs->jar_msg  = JAR_MSG_DIRENT_MULTIVOLUME;
+        return(pjs->jar_code);
+    }
+
+    /*
+     * Read file name and skip 'file extra field' and 'file comment'
+     * areas with lseek(2) beyond them to next directory entry.
+     */
+    if (pjs->directory_entry.size_member_filename !=
+        portable_read(pjs->fd,
+                      pjs->directory_entry.member_filename,
+                      pjs->directory_entry.size_member_filename))
+    {
+       sysDbgMsg(DML2,pjs->jar_filename,JAR_MSG_DIRENT_READ_MEMBERNAME);
+
+        pjs->jar_code = JAR_READ;
+        pjs->jar_msg  = JAR_MSG_DIRENT_READ_MEMBERNAME;
+        return(pjs->jar_code);
+    }
+
+    /*
+     * Insert final NUL byte
+     */
+    pjs->directory_entry.member_filename
+      [pjs->directory_entry.size_member_filename] = '\0';
+
+
+    /*
+     * Seek to next directory entry, report any errors
+     */
+    lseek_next_entry =
+        portable_lseek(pjs->fd,
+                       pjs->directory_entry.size_member_extra +
+                           pjs->directory_entry.size_member_comment,
+                       SEEK_CUR);
+
+    if (JAR_MEMBER_IMPOSSIBLE_LSEEK == lseek_next_entry)
+    {
+        sysDbgMsg(DML2, pjs->jar_filename, JAR_MSG_DIRENT_SEEK);
+
+        pjs->jar_code = JAR_LSEEK;
+        pjs->jar_msg  = JAR_MSG_DIRENT_SEEK;
+        return(pjs->jar_code);
+    }
+
+    /*
+     * Fill in remaining fields with calculated values
+     */
+    pjs->directory_entry.extra_offset =
+        lseek_next_entry -
+        pjs->directory_entry.size_member_extra -
+        pjs->directory_entry.size_member_comment;
+
+    pjs->directory_entry.comment_offset =
+        lseek_next_entry -
+        pjs->directory_entry.size_member_comment;
+
+    /*
+     * Valid scan
+     */
+    return(JAR_OKAY);
+
+} /* END of jarutil_read_next_directory_entry() */
+
+
+/*!
+ * @brief Search for a Java archive member file in a Java archive file.
+ *
+ *
+ * @param[in,out] pjs                State of open Java archive file.
+ *                                   Of particular interest for input is
+ *                                   the @link jar_state#member_filename
+                                     pjs->member_filename@endlink ,
+ *                                   which states which member to look
+ *                                   for in the archive.
+ *                                   Of particular interest for output
+ *                                   is the @link #jar_directory_entry
+                                     pjs->directory_entry@endlink
+ *                                   member, which contains the
+ *                                   directory entry of that member
+ *                                   file in the archive.
+ *                                   A compressed read will extract
+ *                                   it from the archive.
+ *
+ *
+ * @returns jar_api_enum status of file system activity,
+ *          particularly @b JAR_OKAY when successful.
+ *          Other values indicate errors.
+ *
+ */
+
+static jar_api_enum jarutil_locate_member(jar_state *pjs)
+{
+    ARCH_FUNCTION_NAME(jarutil_locate_member);
+
+    jar_api_enum rc;
+
+    rushort direntidx;
+
+    /*
+     * Search for global trailer at end of Java archive,
+     * which contains location of directory.  With this
+     * information, all member files may be located.
+     */
+    rc = jarutil_locate_zip_directory(pjs);
+    if (JAR_OKAY != rc)
+    {
+        return(rc);
+    }
+
+    /* Seek to location of directory, report any errors */
+    if (JAR_MEMBER_IMPOSSIBLE_LSEEK == 
+        portable_lseek(pjs->fd,
+                       pjs->trailer.directory_position,
+                       SEEK_SET))
+    {
+        sysDbgMsg(DML2, pjs->jar_filename, JAR_MSG_DIRENT_SEEK);
+
+        pjs->jar_code = JAR_LSEEK;
+        pjs->jar_msg  = JAR_MSG_DIRENT_SEEK;
+        return(pjs->jar_code);
+    }
+
+    /* Point directory entry to offset of first one in ZIP file */
+    pjs->directory_entry.position = pjs->trailer.directory_position;
+
+    /*
+     * Read each directory entry until match is found.
+     */
+    for (direntidx = 0;
+         direntidx < pjs->trailer.total_directory_entries;
+         direntidx++)
+    {
+        /* First time through reads first directory entry */
+        rc = jarutil_read_next_directory_entry(pjs, direntidx);
+        if (JAR_OKAY != rc)
+        {
+            return(rc);
+        }
+        
+        /*
+         * Check if this slot contains the requested member file.
+         * Return match if so.
+         */
+        if (0 == portable_strcmp(pjs->member_filename,
+                                 pjs->directory_entry.member_filename))
+        {
+            pjs->jar_code = JAR_OKAY;
+            pjs->jar_msg  = (rchar *) rnull;
+            return(JAR_OKAY);
+        }
+
+    } /* for (dirent) */
+
+    /*
+     * Valid scan, but requested member not found in archive
+     */
+    sysDbgMsg(DML2,
+              pjs->jar_filename,
+              JAR_MSG_DIRENT_MEMBER_NOT_FOUND ": '%s'",
+              pjs->member_filename);
+
+    pjs->jar_code = JAR_MEMBER_NOT_FOUND;
+    pjs->jar_msg  = JAR_MSG_DIRENT_MEMBER_NOT_FOUND;
+    return(pjs->jar_code);
+
+} /* END of jarutil_locate_member() */
+
+
+/*!
+ * @brief Read header information of a Java archive member file entry
+ * and verify it against its directory entry.
+ *
+ * Seek to the starting point of a member file entry,
+ * read its header information, and compare that to the equivalent
+ * from the directory entry (presumably a legacy requirement).  If
+ * valid, then the member data itself may be read.
+ *
+ * @param[in,out] pjs  State of open Java archive file.  Of particular
+ *                     interest is the @link #jar_directory_entry
+                       pjs->directory_entry@endlink member, which
+ *                     contains the directory entry of that member
+ *                     file in the archive.  The @link jar_state.bfr
+ *                     jar_state.bfr@endlink field contains a pointer
+ *                     to a buffer to use to store the data when it is
+ *                     read from the member file.  The
+ *                     @link jar_directory_entry.uncompressed_size
+                       jar_directory_entry.uncompressed_size@endlink
+ *                     field tells how many bytes to read.  The
+ *                     @link jar_directory_entry.member_offset
+                       jar_directory_entry.member_offset@endlink field
+ *                     tells where the member starts in the archive.
+ *
+ *
+ * @returns jar_api_enum status of file system activity,
+ *          particularly @b JAR_OKAY when successful.
+ *          Other values indicate errors.  If successful,
+ *          the @link jar_state.fd jar_state.fd@endlink file will
+ *          be in the correct place to read the data area, whether
+ *          it be compressed data or uncompressed.
+ *
+ */
+
+static jar_api_enum jarutil_read_and_verify_member_header(jar_state
+                                                                   *pjs)
+{
+    ARCH_FUNCTION_NAME(jarutil_read_and_verify_member_header);
+
+    /*
+     * Seek to beginning of member, read its header,
+     * and verify that the header is valid.  If so,
+     * seek to the beginning of the uncompressed data
+     * area for forthcoming read. 
+     */
+
+    if (pjs->directory_entry.member_offset !=
+        portable_lseek(pjs->fd,
+                       pjs->directory_entry.member_offset,
+                       SEEK_SET))
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_SEEK_HEADER ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_LSEEK;
+        pjs->jar_msg  = JAR_MSG_MEMBER_SEEK_HEADER;
+        return(pjs->jar_code);
+    }
+
+    /*
+     * Read the member entry up to before the file name.
+     * Use the file name area to read, then byte-swap into
+     * the individual fields.  This can be done for two
+     * reasons:  (1) the data format is fixed and well established,
+     * and (2) the file name field is much large than the requested
+     * data.  Therefore, no temporary buffer storage is needed.
+     */
+#define MEMBER_ENTRY_READ_SIZE                               \
+           (JAR_NUM_MEMBER_SIGNATURE_BYTES +                 \
+            sizeof(pjs->member_entry.version) +              \
+            sizeof(pjs->member_entry.flags) +                \
+            sizeof(pjs->member_entry.compression_method) +   \
+            sizeof(pjs->member_entry.dos_date) +             \
+            sizeof(pjs->member_entry.crc32) +                \
+            sizeof(pjs->member_entry.compressed_size) +      \
+            sizeof(pjs->member_entry.uncompressed_size) +    \
+            sizeof(pjs->member_entry.size_member_filename) + \
+            sizeof(pjs->member_entry.size_member_extra))
+
+#define TRANSCRIBE_MEMENT_FIELD(name, type, lefn, idx)               \
+    pjs->member_entry.name =                                         \
+        bytegames_getr##lefn##_le((type *)                           \
+                             &pjs->member_entry.member_filename[idx])
+
+    /*
+     * Read this member entry header.
+     */
+
+    if (MEMBER_ENTRY_READ_SIZE !=
+        portable_read(pjs->fd,
+                      pjs->member_entry.member_filename,
+                      MEMBER_ENTRY_READ_SIZE))
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_READ_HEADER ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_READ;
+        pjs->jar_msg = JAR_MSG_MEMBER_READ_HEADER;
+        return(pjs->jar_code);
+    }
+
+    /* Transcribe and validate member signature */
+    pjs->member_entry.magic[0] = pjs->member_entry.member_filename[0];
+    pjs->member_entry.magic[1] = pjs->member_entry.member_filename[1];
+    pjs->member_entry.magic[2] = pjs->member_entry.member_filename[2];
+    pjs->member_entry.magic[3] = pjs->member_entry.member_filename[3];
+
+    if ((JAR_MEMBER_SIGNATURE_BYTE1 != pjs->member_entry.magic[0] ) ||
+        (JAR_MEMBER_SIGNATURE_BYTE2 != pjs->member_entry.magic[1] ) ||
+        (JAR_MEMBER_SIGNATURE_BYTE3 != pjs->member_entry.magic[2] ) ||
+        (JAR_MEMBER_SIGNATURE_BYTE4 != pjs->member_entry.magic[3] ))
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_SEEK_HEADER ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_BAD_MEMBER_ENTRY;
+        pjs->jar_msg = JAR_MSG_MEMBER_SEEK_HEADER;
+        return(pjs->jar_code);
+    }
+
+    /* Transcribe other fields */
+    TRANSCRIBE_MEMENT_FIELD(version,              rushort, s2, 4);
+    TRANSCRIBE_MEMENT_FIELD(flags,                rushort, s2, 6);
+    TRANSCRIBE_MEMENT_FIELD(compression_method,   rushort, s2, 8);
+    TRANSCRIBE_MEMENT_FIELD(dos_date,             ruint,   i4, 10);
+    TRANSCRIBE_MEMENT_FIELD(crc32,                ruint,   i4, 14);
+    TRANSCRIBE_MEMENT_FIELD(compressed_size,      ruint,   i4, 18);
+    TRANSCRIBE_MEMENT_FIELD(uncompressed_size,    ruint,   i4, 22);
+    TRANSCRIBE_MEMENT_FIELD(size_member_filename, rushort, s2, 26);
+    TRANSCRIBE_MEMENT_FIELD(size_member_extra,    rushort, s2, 28);
+
+    /*
+     * Verify valid file name length (to avoid read() buffer overflow)
+     */
+    if (JAR_MAX_MEMBER_FILENAME_LENGTH <
+        pjs->member_entry.size_member_filename)
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_FILENAME_SIZE ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_BAD_MEMBER_ENTRY;
+        pjs->jar_msg  = JAR_MSG_MEMBER_FILENAME_SIZE;
+        return(pjs->jar_code);
+    }
+
+    switch(pjs->member_entry.compression_method)
+    {
+        case JAR_COMPRESSION_METHOD_STORE:
+        case JAR_COMPRESSION_METHOD_DEFLATE:
+            if (pjs->directory_entry.compression_method ==
+                pjs->member_entry.compression_method)
+            {
+                break;
+            }
+            /* ... fall through to error condition: */
+
+        default:
+            sysDbgMsg(DML2,
+                      pjs->jar_filename,
+                      JAR_MSG_COMPRESSION_METHOD ": method %d on '%s'",
+                      pjs->member_entry.compression_method,
+                      pjs->directory_entry.member_filename);
+
+            pjs->jar_code = JAR_COMPRESSION_METHOD;
+            pjs->jar_msg  = JAR_MSG_COMPRESSION_METHOD;
+            return(pjs->jar_code);
+    }
+
+    if (!(MEMBER_ENTRY_FLAG_BIT3 & pjs->member_entry.flags))
+    {
+        if ((pjs->directory_entry.crc32 !=
+             pjs->member_entry.crc32)                      ||
+
+            (pjs->directory_entry.compressed_size !=
+             pjs->member_entry.compressed_size)            ||
+
+            (pjs->directory_entry.uncompressed_size !=
+             pjs->member_entry.uncompressed_size))
+        {
+            sysDbgMsg(DML2,
+                      pjs->jar_filename,
+                      JAR_MSG_MEMBER_DIRENT_MISMATCH ": '%s'",
+                      pjs->directory_entry.member_filename);
+
+            pjs->jar_code = JAR_FIELD_MISMATCH;
+            pjs->jar_msg  = JAR_MSG_MEMBER_DIRENT_MISMATCH;
+            return(pjs->jar_code);
+        }
+    }
+
+    if (pjs->directory_entry.size_member_filename !=
+        pjs->member_entry.size_member_filename)
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_DIRENT_MISMATCH ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_FIELD_MISMATCH;
+        pjs->jar_msg  = JAR_MSG_MEMBER_DIRENT_MISMATCH;
+        return(pjs->jar_code);
+    }
+
+    /*
+     * Do NOT need to verify valid file name length
+     * since member version matches directory version
+     * and directory version was valid.
+     *
+     * Therefore, go read file name and compare against directory.
+     *
+     * Then go read file extra field and compare against directory.
+     */
+    if (pjs->member_entry.size_member_filename !=
+        portable_read(pjs->fd,
+                      pjs->member_entry.member_filename,
+                      pjs->member_entry.size_member_filename))
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_READ_MEMBERNAME ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_READ;
+        pjs->jar_msg  = JAR_MSG_MEMBER_READ_MEMBERNAME;
+        return(pjs->jar_code);
+    }
+
+    /*
+     * Insert final NUL byte
+     */
+    pjs->member_entry.member_filename
+      [pjs->member_entry.size_member_filename] = '\0';
+
+
+    /*
+     * Keep checking directory entry against member header...
+     */
+    if ((pjs->directory_entry.member_offset +
+         MEMBER_ENTRY_READ_SIZE +
+         pjs->member_entry.size_member_filename +
+         pjs->member_entry.size_member_extra) !=
+        portable_lseek(pjs->fd,
+                       pjs->member_entry.size_member_extra,
+                       SEEK_CUR))
+    {
+        /* Report invalid seek */
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_SEEK_DATA ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_LSEEK;
+        pjs->jar_msg  = JAR_MSG_MEMBER_SEEK_DATA;
+        return(pjs->jar_code);
+    }
+
+    /*!
+     * @todo HARMONY-6-jvm-jarutil.c-2 Make sure file name is the same
+     * in the member field as in the directory.  Is this needed, or not?
+     *
+     */
+    if (0 != portable_strcmp(pjs->directory_entry.member_filename,
+                             pjs->member_entry.member_filename))
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_DIRENT_MISMATCH ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_FIELD_MISMATCH;
+        pjs->jar_msg  = JAR_MSG_MEMBER_DIRENT_MISMATCH;
+        return(pjs->jar_code);
+    }
+
+    /* Report success */
+    pjs->jar_code  = JAR_OKAY;
+    return(pjs->jar_code);
+
+} /* END of jarutil_read_and_verify_member_header() */
+
+
+/*!
+ * @name Read an archive member file from a Java archive.
+ *
+ * Read a member file from an archive by seeking to its starting point,
+ * reading its header information, compariing that to the equivalent
+ * from the directory entry, and read the uncompressed data out of
+ * the archive into an output buffer.
+ *
+ * @param[in,out] pjs    State of open Java archive file.  Of particular
+ *                       interest is the @link #jar_directory_entry
+                         pjs->directory_entry@endlink field, which
+ *                       contains the directory entry of that member
+ *                       file in the archive.  The @link #jar_state.bfr
+ *                       jar_state.bfr@endlink field contains a pointer
+ *                       to a buffer to use to store the data when it is
+ *                       read from the member file.  The
+ *                       @link #jar_directory_entry.uncompressed_size
+                         jar_directory_entry.uncompressed_size@endlink
+ *                       field tells how many bytes to read.  The
+ *                       @link #jar_directory_entry.member_offset
+                         jar_directory_entry.member_offset@endlink field
+ *                       tells where the member starts in the archive.
+ *
+ * @param[in] member_bfr Meaningful only for compressed read.
+ *                       Scratch buffer of size
+ *                       @link #jar_directory_entry.compressed_size
+                         jar_directory_entry.compressed_size@endlink
+ *                       to hold intermediate compressed data.
+ *
+ * @returns jar_api_enum status of file system activity,
+ *          particularly @b JAR_OKAY when successful.
+ *          Other values indicate errors.  When successful,
+ *          the buffer pointer will be non-null and will
+ *          contain the requested data.  The uncompressed
+ *          size will be the length of that buffer in bytes.
+ *          At this time, the file pointer will also point
+ *          to the first byte following the data that was read.
+ *
+ */
+
+/*@{ */ /* Begin grouped definitions */
+
+/*!
+ * @brief Read an @e uncompressed archive member file.
+ *
+ */
+static jar_api_enum jarutil_read_uncompressed_member(jar_state *pjs)
+{
+    ARCH_FUNCTION_NAME(jarutil_read_uncompressed_member);
+
+    jar_api_enum rc;
+
+
+    /* Read member header data.  If valid, read member data itself */
+    rc = jarutil_read_and_verify_member_header(pjs);
+
+    if (JAR_OKAY != rc)
+    {
+        return(rc);
+    }
+
+    if (pjs->directory_entry.uncompressed_size !=
+        portable_read(pjs->fd,
+                      pjs->bfr,
+                      pjs->directory_entry.uncompressed_size))
+    {
+        /* Report invalid read */
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_READ_DATA ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_READ;
+        pjs->jar_msg  = JAR_MSG_MEMBER_READ_DATA;
+        return(pjs->jar_code);
+    }
+
+    ruint crc = crc32(0,
+                      pjs->bfr,
+                      pjs->directory_entry.uncompressed_size);
+
+    if (crc != pjs->directory_entry.crc32)
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_CRC32 ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_CRC32;
+        pjs->jar_msg=JAR_MSG_MEMBER_CRC32 "(uncompressed member file)";
+        return(pjs->jar_code);
+    }
+
+    /* Report successful read */
+    pjs->jar_code  = JAR_OKAY;
+    return(pjs->jar_code);
+
+} /* END of jarutil_read_uncompressed_member() */
+
+
+/*!
+ * @brief Read a @e compressed archive member file.
+ *
+ */
+
+static jar_api_enum jarutil_read_compressed_member(jar_state *pjs,
+                                                   rchar    *member_bfr)
+{
+    ARCH_FUNCTION_NAME(jarutil_read_compressed_member);
+
+    jar_api_enum rc;
+
+
+    /* Read member header data.  If valid, read member data itself */
+    rc = jarutil_read_and_verify_member_header(pjs);
+
+    if (JAR_OKAY != rc)
+    {
+        return(rc);
+    }
+
+    /* Done if result is zero size */
+    /*!
+     * @todo HARMONY-6-jvm-jarutil.c-4 Is returning immediately
+     * the best thing to do here?  Or should a zero-sized
+     * decompression happen?
+     */
+#if 0
+    if (0 == pjs->directory_entry.uncompressed_size)
+    {
+        pjs->jar_code  = JAR_OKAY;
+        return(pjs->jar_code);
+    }
+#endif
+
+    if (pjs->directory_entry.compressed_size !=
+        portable_read(pjs->fd,
+                      member_bfr,
+                      pjs->directory_entry.compressed_size))
+    {
+        /* Report invalid read */
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_READ_DATA ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_READ;
+        pjs->jar_msg  = JAR_MSG_MEMBER_READ_DATA;
+        return(pjs->jar_code);
+    }
+
+    /* Initialize, run, and finalize the decompressor */
+    z_stream *pzs = (pjs->heap_get_data)(sizeof(z_stream), rfalse);
+
+    pzs->total_in  = 0;  /* not specified in reference implementation */
+    pzs->total_out = 0;
+
+    pzs->zalloc    = jarutil_zlib_malloc;
+    pzs->zfree     = jarutil_zlib_free;
+
+    pzs->opaque    = (voidpf) pjs;
+
+    pzs->next_in   = member_bfr;
+    pzs->avail_in  = pjs->directory_entry.compressed_size;
+
+    pzs->next_out  = pjs->bfr;
+    pzs->avail_out = pjs->directory_entry.uncompressed_size;
+
+    /*!
+     * @internal  @e Always use @b inflateInit2() on all platforms
+     * because it apparently does not expect the @e gzip header.
+     * Instead, it simply decompresses the data and returns.
+     * By using @b inflateInit() for non-Windows platforms, this
+     * @e gzip header always makes @b inflate() return an error
+     * in @b pzs->msg stating, "incorrect header check".
+     *
+     */
+    int irc = inflateInit2(pzs, -MAX_WBITS);
+    if (irc != Z_OK)
+    {
+        pjs->jar_code = JAR_INFLATEINIT;
+        pjs->jar_msg = jarutil_zlib_code(irc);
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_ZLIB_INITD ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->zlib_code = irc;
+        pjs->zlib_msg = pzs->msg;
+
+        (pjs->heap_free_data)(pzs);
+        return(pjs->jar_code);
+    }
+
+    irc = inflate(pzs, Z_FINISH);
+
+    if ((Z_OK <= irc) && (pzs->msg))
+    {
+        pjs->jar_code = JAR_INFLATE;
+        pjs->jar_msg = jarutil_zlib_code(irc);
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_ZLIB_DECOMPRESS ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+
+        pjs->zlib_code = irc;
+        pjs->zlib_msg = pzs->msg;
+
+        inflateEnd(pzs);
+        (pjs->heap_free_data)(pzs);
+        return(pjs->jar_code);
+    }
+
+    if ((0 > irc) || (Z_STREAM_END != irc))
+    {
+        pjs->jar_code = JAR_INFLATE;
+        pjs->jar_msg = jarutil_zlib_code(irc);
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_ZLIB_DECOMPRESS ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+
+        pjs->zlib_code = irc;
+        pjs->zlib_msg = pzs->msg;
+
+        inflateEnd(pzs);
+        (pjs->heap_free_data)(pzs);
+        return(pjs->jar_code);
+    }
+
+    ruint crc = crc32(0,
+                      pjs->bfr,
+                      pjs->directory_entry.uncompressed_size);
+
+    if (crc != pjs->directory_entry.crc32)
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_MEMBER_CRC32 ": '%s'",
+                  pjs->directory_entry.member_filename);
+
+        pjs->jar_code = JAR_CRC32;
+        pjs->jar_msg = JAR_MSG_MEMBER_CRC32 " (compressed member file)";
+
+        inflateEnd(pzs);
+        (pjs->heap_free_data)(pzs);
+        return(pjs->jar_code);
+    }
+
+    /* Report success */
+    pjs->jar_code  = JAR_OKAY;
+    inflateEnd(pzs);
+    (pjs->heap_free_data)(pzs);
+    return(pjs->jar_code);
+
+} /* END of jarutil_read_compressed_member() */
+
+/*@} */ /* END of grouped definitions */
+
+
+/*!
+ * @brief Extract currently located Java archive member file
+ * into memory buffer.
+ *
+ *
+ * @param pjs   State of an open Java archive file, the result of
+ *              a previous jarutil_find_member().
+ *
+ * @returns input parameter @b pjs .  Status of action will be
+ *          contained in structure members, especially in
+ *          @link #jar_state.jar_code jar_code@endlink.
+ *
+ */
+jar_state *jarutil_read_current_member(jar_state *pjs)
+{
+    ARCH_FUNCTION_NAME(jarutil_read_current_member);
+
+    /*
+     * Make minor adjustment if reading JAR manifest file.
+     * After reading, append NUL byte for use with
+     * manifest_get_main_from_bfr()
+     */
+    rint adjust_manifest =
+        (0 == portable_strcmp(&pjs->directory_entry.member_filename[0],
+                              JVMCFG_JARFILE_MANIFEST_FILENAME))
+              ? sizeof(rchar)
+              : 0;
+
+    /*
+     * Since member is known to be available, go read it.
+     *
+     * If data is not compressed, simply read it.  If it
+     * is compressed, allocate and initialize ZLIB interface,
+     * then call its initialization function, the decompressor
+     * itself, and the finalization function.
+     */
+
+    if (JAR_COMPRESSION_METHOD_STORE ==
+        pjs->directory_entry.compression_method)
+    {
+        /* Buffer to hold contents of requested member file */
+        pjs->bfr =
+           (pjs->heap_get_data)(pjs->directory_entry.uncompressed_size +
+                                  adjust_manifest,
+                                rfalse);
+
+        if (JAR_OKAY == jarutil_read_uncompressed_member(pjs))
+        {
+            /* Add NUL byte to bfr if it is a JAR manifest file image */
+            if (0 != adjust_manifest)
+            {
+                pjs->bfr[pjs->directory_entry.uncompressed_size] = '\0';
+            }
+        }
+        else
+        {
+             (pjs->heap_free_data)(pjs->bfr);
+             pjs->bfr = (rchar *) rnull;
+        }
+
+        /* Return with member data or with error report */
+        return(pjs);
+    }
+    else
+    {
+        if (JAR_COMPRESSION_METHOD_DEFLATE ==
+            pjs->directory_entry.compression_method)
+        {
+            /* Temportary buffer to hold compressed member file data */
+            rchar *member_bfr =
+              (pjs->heap_get_data)(pjs->directory_entry.compressed_size,
+                                   rfalse);
+
+            /* Buffer to hold contents of requested member file */
+            pjs->bfr =
+            (pjs->heap_get_data)(pjs->directory_entry.uncompressed_size+
+                                   adjust_manifest,
+                                 rfalse);
+
+
+            pjs->jar_code = jarutil_read_compressed_member(pjs,
+                                                            member_bfr);
+
+            if (JAR_OKAY == pjs->jar_code)
+            {
+                /* Add NUL byte to bfr if it is JAR manifest file img */
+                if (0 != adjust_manifest)
+                {
+                    pjs->bfr[pjs->directory_entry.uncompressed_size] =
+                                                                   '\0';
+                }
+            }
+            else
+            {
+                 (pjs->heap_free_data)(member_bfr);
+                 (pjs->heap_free_data)(pjs->bfr);
+                 pjs->bfr = (rchar *) rnull;
+            }
+
+            /* Return with member data or with error report */
+            return(pjs);
+        }
+        else
+        {
+/*NOTREACHED*/
+            /*
+             * This compression method is not supported.
+             */
+
+            sysDbgMsg(DML2,
+                      pjs->jar_filename,
+                      JAR_MSG_COMPRESSION_METHOD ": '%s'",
+                      pjs->directory_entry.member_filename);
+
+            portable_close(pjs->fd);
+
+            pjs->fd = -1;  /* file was closed due to error */
+
+            pjs->jar_code = JAR_COMPRESSION_METHOD;
+            pjs->jar_msg  = JAR_MSG_COMPRESSION_METHOD;
+            pjs->bfr      = (rchar *) rnull;
+
+            /* Return with member data or with error report */
+            return(pjs);
+        }
+    }
+/*NOTREACHED*/
+} /* END of jarutil_read_current_member() */
+
+
+
+
+/*!
+ * @name Examine and/or read Java archive member files in an archive.
+ *
+ *
+ * @param jar_filename    Name of Java archive file to search.
+ *
+ * @param member_filename Name of member within this Java archive file.
+ *
+ * @param heap_get_data   Name of
+ *                        @link jvm/src/gc.h heap allocation@endlink
+ *                        function to be used internally by this
+ *                        JAR function for allocating blocks of data.
+ *                        Must be paired with matching
+ *                        @b heap_free_data function parameter.
+ *
+ * @param heap_free_data  Name of
+ *                        @link jvm/src/gc.h heap freeing@endlink
+ *                        function to be used internally by this
+ *                        JAR function for freeing allocated blocks
+ *                        of data.  Must be paired with matching
+ *                        @b heap_get_data function parameter.
+ *
+ * @returns Pointer to jar_state containing information about the class
+ *          file data found in this JAR file, including especially
+ *          its @link #jar_state.directory_entry directory_entry@endlink
+ *          that contains the
+ *          @link #jar_directory_entry.uncompressed_size
+            uncompressed_size@endlink member that describes the length
+ *          of the @link #jar_state.bfr jar_state.bfr@endlink buffer
+ *          area that holds the return data. If the member was found
+ *          then @link #jar_state.jar_code jar_code@endlink
+ *          will contain @link #JAR_OKAY JAR_OKAY@endlink and the
+ *          buffer pointer will be valid.  Otherwise, a different
+ *          value will be stored there and the buffer pointer will not
+ *          be valid.  <i>In @b all of these cases, the returned pointer
+ *          will need to be freed.</i>  There will @e never be a
+ *          @link #rnull rnull@endlink pointer returned when the input
+ *          parameters are valid.)  If a @link #rnull rnull@endlink
+ *          pointer actually @e is returned, then there was an error
+ *          in an input parameter.
+ *
+ */
+
+
+/*@{ */ /* Begin grouped definitions */
+
+/*!
+ * @brief Query for presence a Java archive member file in an archive.
+ *
+ */
+
+jar_state *jarutil_find_member(const rchar *jar_filename,
+                               const rchar *member_filename,
+                               rvoid *(* const heap_get_data)(rint,
+                                                              rboolean),
+                               rvoid  (* const heap_free_data)(rvoid *))
+{
+    ARCH_FUNCTION_NAME(jarutil_find_member);
+
+    /* Check input parameters */
+    if ((rnull == jar_filename)        ||
+        (rnull == member_filename)     ||
+        (rnull == heap_get_data)       ||
+        (rnull == heap_free_data)      ||
+        (0 == portable_strlen(jar_filename))    ||
+        (0 == portable_strlen(member_filename)) ||
+        (JAR_MAX_MEMBER_FILENAME_LENGTH <
+         portable_strlen(member_filename)))
+    {
+        return((jar_state *) rnull);
+    }
+    /*
+     * This structure is used by ALL subsidiary function
+     * calls to retain current state of JAR file access.
+     */
+    jar_state *pjs = (heap_get_data)(sizeof(jar_state), rtrue);
+
+    /* Store input parameters to be passed around the code */
+    pjs->jar_filename    = jar_filename;
+    pjs->member_filename = member_filename;
+    pjs->heap_get_data   = heap_get_data;
+    pjs->heap_free_data  = heap_free_data;
+
+    /*
+     * Initialize return codes, etc.
+     */
+    pjs->fd  = JAR_MEMBER_IMPOSSIBLE_OPEN_FD;
+    pjs->jar_code  = JAR_OKAY;
+    pjs->jar_msg   = (rchar *) rnull;
+    pjs->zlib_code = Z_OK; /* Valid decompress sets to Z_STREAM_END */
+    pjs->zlib_msg  = (rchar *) rnull;
+
+    /*!
+      * @todo HARMONY-6-jvm-jarutil.c-1 Should JAR files be able to
+      *       be opened with O_LARGEFILE so that huge archives may
+      *       be automatically supported?
+      */
+    /* Open JAR file for read and report any errors */
+    pjs->fd = portable_open(pjs->jar_filename, O_RDONLY);
+
+    if (0 > pjs->fd)
+    {
+        sysDbgMsg(DML2,
+                  pjs->jar_filename,
+                  JAR_MSG_OPEN);
+
+        pjs->jar_msg = JAR_MSG_OPEN;
+        pjs->jar_code = JAR_OPEN;
+        return(pjs);
+    }
+
+    /* Search for requested member of this JAR file */
+    if (JAR_OKAY != jarutil_locate_member(pjs))
+    {
+        portable_close(pjs->fd);
+
+        /* file was closed due to error */
+        pjs->fd = JAR_MEMBER_IMPOSSIBLE_OPEN_FD;
+    }
+
+    /* Report current state, whether good or bad */
+    return(pjs);
+
+} /* END of jarutil_find_member() */
+
+
+/*!
+ * @brief Extract a Java archive member file into memory buffer.
+ *
+ */
+jar_state *jarutil_read_member(const rchar *jar_filename,
+                               const rchar *member_filename,
+                               rvoid *(* const heap_get_data)(rint,
+                                                              rboolean),
+                               rvoid  (* const heap_free_data)(rvoid *))
+{
+    ARCH_FUNCTION_NAME(jarutil_read_member);
+
+    jar_state *pjs = jarutil_find_member(jar_filename,
+                                         member_filename,
+                                         heap_get_data,
+                                         heap_free_data);
+
+    if (pjs == (jar_state *) rnull)
+    {
+        return(pjs);
+    }
+
+    /*
+     * Since member is known to be available, go read it.
+     */
+    return(jarutil_read_current_member(pjs));
+
+} /* END of jarutil_read_member() */
+
+
+/*@} */ /* End of grouped definitions */
+
+
+/* EOF */

Propchange: harmony/enhanced/sandbox/bootjvm/bootJVM/jvm/src/jarutil.c
------------------------------------------------------------------------------
    svn:keywords = Date Revision Author HeadURL Id