changeset 82:772872ac68d5

Remove dependency on libjli so we can work with IcedTea7. 2010-12-14 Andrew John Hughes <ahughes@redhat.com> * Makefile.am: (LAUNCHER_OBJECTS): Add jli_util.o, parse_manifest.o, version_comp.o, wildcard.o. (LAUNCEHR_FLAGS): Add -DEXPAND_CLASSPATH_WILDCARDS as used in build of libjli in OpenJDK. (LAUNCHER_LINK): Don't link to libjli. * launcher/jli_util.c, * launcher/parse_manifest.c, * launcher/version_comp.c, * launcher/wildcard.c: Add source files from OpenJDK6 to match header files already used.
author Andrew John Hughes <ahughes@redhat.com>
date Wed, 15 Dec 2010 00:54:34 +0000
parents adef5d4159ee
children c45620d437e1
files ChangeLog Makefile.am launcher/jli_util.c launcher/parse_manifest.c launcher/version_comp.c launcher/wildcard.c
diffstat 6 files changed, 1564 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Mon Dec 13 17:28:01 2010 -0500
+++ b/ChangeLog	Wed Dec 15 00:54:34 2010 +0000
@@ -1,3 +1,18 @@
+2010-12-14  Andrew John Hughes  <ahughes@redhat.com>
+
+	* Makefile.am:
+	(LAUNCHER_OBJECTS): Add jli_util.o, parse_manifest.o,
+	version_comp.o, wildcard.o.
+	(LAUNCEHR_FLAGS): Add -DEXPAND_CLASSPATH_WILDCARDS
+	as used in build of libjli in OpenJDK.
+	(LAUNCHER_LINK): Don't link to libjli.
+	* launcher/jli_util.c,
+	* launcher/parse_manifest.c,
+	* launcher/version_comp.c,
+	* launcher/wildcard.c:
+	Add source files from OpenJDK6 to match header files
+	already used.
+
 2010-12-13  Omair Majid  <omajid@redhat.com>
 
 	* netx/net/sourceforge/jnlp/config/ValueValidator.java: New file.
--- a/Makefile.am	Mon Dec 13 17:28:01 2010 -0500
+++ b/Makefile.am	Wed Dec 15 00:54:34 2010 +0000
@@ -71,17 +71,16 @@
 # Launcher
 
 LAUNCHER_SRCDIR = $(abs_top_srcdir)/launcher
-LAUNCHER_OBJECTS = java.o java_md.o splashscreen_stubs.o
+LAUNCHER_OBJECTS = java.o java_md.o splashscreen_stubs.o jli_util.o parse_manifest.o version_comp.o wildcard.o
 PLUGIN_LAUNCHER_OBJECTS = $(addprefix $(PLUGIN_DIR)/launcher/,$(LAUNCHER_OBJECTS))
 NETX_LAUNCHER_OBJECTS = $(addprefix $(NETX_DIR)/launcher/,$(LAUNCHER_OBJECTS))
 CONTROLPANEL_LAUNCHER_OBJECTS = $(addprefix $(NETX_DIR)/launcher/controlpanel/,$(LAUNCHER_OBJECTS))
 LAUNCHER_FLAGS = -O2 -fno-strict-aliasing -fPIC -pthread -W -Wall -Wno-unused -Wno-parentheses -pipe -fno-omit-frame-pointer \
 	-g -D_LARGEFILE64_SOURCE -D_GNU_SOURCE -D_REENTRANT -DLAUNCHER_NAME='"java"' -I$(LAUNCHER_SRCDIR) \
-	-DJDK_MAJOR_VERSION='"1"' -DJDK_MINOR_VERSION='"6"' -DLIBARCHNAME='"$(JRE_ARCH_DIR)"'
+	-DJDK_MAJOR_VERSION='"1"' -DJDK_MINOR_VERSION='"6"' -DLIBARCHNAME='"$(JRE_ARCH_DIR)"' \
+	-DEXPAND_CLASSPATH_WILDCARDS
 LAUNCHER_LINK = -o $@ -pthread -Xlinker -O1 -Xlinker -z -Xlinker defs -L$(BOOT_DIR)/lib/$(INSTALL_ARCH_DIR) \
-	-Wl,-soname=lib.so  -L $(BOOT_DIR)/jre/lib/$(INSTALL_ARCH_DIR)/jli -Wl,-z -Wl,origin \
-	-Wl,--allow-shlib-undefined -Wl,-rpath -Wl,\$$ORIGIN/../lib/$(INSTALL_ARCH_DIR)/jli -Wl,-rpath \
-	-Wl,\$$ORIGIN/../jre/lib/$(INSTALL_ARCH_DIR)/jli $(X11_CFLAGS) $(X11_LIBS) -ljli  -ldl -lz
+	-Wl,-soname=lib.so -Wl,-z -Wl,origin -Wl,--allow-shlib-undefined $(X11_CFLAGS) $(X11_LIBS) -ldl -lz
 PLUGIN_VERSION = IcedTea-Web $(PACKAGE_VERSION)$(ICEDTEA_REV)$(ICEDTEA_PKG)
 
 EXTRA_DIST = $(top_srcdir)/netx $(top_srcdir)/plugin javaws.png javaws.desktop.in extra launcher \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/jli_util.c	Wed Dec 15 00:54:34 2010 +0000
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "jli_util.h"
+
+/*
+ * Returns a pointer to a block of at least 'size' bytes of memory.
+ * Prints error message and exits if the memory could not be allocated.
+ */
+void *
+JLI_MemAlloc(size_t size)
+{
+    void *p = malloc(size);
+    if (p == 0) {
+        perror("malloc");
+        exit(1);
+    }
+    return p;
+}
+
+/*
+ * Equivalent to realloc(size).
+ * Prints error message and exits if the memory could not be reallocated.
+ */
+void *
+JLI_MemRealloc(void *ptr, size_t size)
+{
+    void *p = realloc(ptr, size);
+    if (p == 0) {
+        perror("realloc");
+        exit(1);
+    }
+    return p;
+}
+
+/*
+ * Wrapper over strdup(3C) which prints an error message and exits if memory
+ * could not be allocated.
+ */
+char *
+JLI_StringDup(const char *s1)
+{
+    char *s = strdup(s1);
+    if (s == NULL) {
+        perror("strdup");
+        exit(1);
+    }
+    return s;
+}
+
+/*
+ * Very equivalent to free(ptr).
+ * Here to maintain pairing with the above routines.
+ */
+void
+JLI_MemFree(void *ptr)
+{
+    free(ptr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/parse_manifest.c	Wed Dec 15 00:54:34 2010 +0000
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * If Windows is POSIX compliant, why isn't the prototype for lseek where
+ * POSIX says it should be?
+ */
+#ifdef _WIN32
+#include <windows.h>
+#include <io.h>
+#else   /* Unix */
+#include <unistd.h>
+#endif  /* Unix */
+
+#include <zlib.h>
+#include "manifest_info.h"
+
+/*
+ * On Windows, str[n]casecmp() are known as str[n]icmp().
+ */
+#ifdef _WIN32
+#define strcasecmp(p1, p2)      stricmp((p1), (p2))
+#define strncasecmp(p1, p2, p3) strnicmp((p1), (p2), (p3))
+#endif
+
+static char     *manifest;
+
+static const char       *manifest_name = "META-INF/MANIFEST.MF";
+
+/*
+ * Inflate the manifest file (or any file for that matter).
+ *
+ *   fd:        File descriptor of the jar file.
+ *   entry:     Contains the information necessary to perform the inflation
+ *              (the compressed and uncompressed sizes and the offset in
+ *              the file where the compressed data is located).
+ *   size_out:  Returns the size of the inflated file.
+ *
+ * Upon success, it returns a pointer to a NUL-terminated malloc'd buffer
+ * containing the inflated manifest file.  When the caller is done with it,
+ * this buffer should be released by a call to free().  Upon failure,
+ * returns NULL.
+ */
+static char *
+inflate_file(int fd, zentry *entry, int *size_out)
+{
+    char        *in;
+    char        *out;
+    z_stream    zs;
+
+    if (entry->csize == 0xffffffff || entry->isize == 0xffffffff)
+        return (NULL);
+    if (lseek(fd, entry->offset, SEEK_SET) < (off_t)0)
+        return (NULL);
+    if ((in = malloc(entry->csize + 1)) == NULL)
+        return (NULL);
+    if ((size_t)(read(fd, in, (unsigned int)entry->csize)) != entry->csize) {
+        free(in);
+        return (NULL);
+    }
+    if (entry->how == STORED) {
+        *(char *)((size_t)in + entry->csize) = '\0';
+        if (size_out) {
+            *size_out = entry->csize;
+        }
+        return (in);
+    } else if (entry->how == DEFLATED) {
+        zs.zalloc = (alloc_func)Z_NULL;
+        zs.zfree = (free_func)Z_NULL;
+        zs.opaque = (voidpf)Z_NULL;
+        zs.next_in = (Byte*)in;
+        zs.avail_in = (uInt)entry->csize;
+        if (inflateInit2(&zs, -MAX_WBITS) < 0) {
+            free(in);
+            return (NULL);
+        }
+        if ((out = malloc(entry->isize + 1)) == NULL) {
+            free(in);
+            return (NULL);
+        }
+        zs.next_out = (Byte*)out;
+        zs.avail_out = (uInt)entry->isize;
+        if (inflate(&zs, Z_PARTIAL_FLUSH) < 0) {
+            free(in);
+            free(out);
+            return (NULL);
+        }
+        *(char *)((size_t)out + entry->isize) = '\0';
+        free(in);
+        if (inflateEnd(&zs) < 0) {
+            free(out);
+            return (NULL);
+        }
+        if (size_out) {
+            *size_out = entry->isize;
+        }
+        return (out);
+    } else
+        return (NULL);
+}
+
+/*
+ * A very little used routine to handle the case that zip file has
+ * a comment at the end. Believe it or not, the only way to find the
+ * END record is to walk backwards, byte by bloody byte looking for
+ * the END record signature.
+ *
+ *      fd:     File descriptor of the jar file.
+ *      eb:     Pointer to a buffer to receive a copy of the END header.
+ *
+ * Returns the offset of the END record in the file on success,
+ * -1 on failure.
+ */
+static off_t
+find_end(int fd, Byte *eb)
+{
+    off_t   len;
+    off_t   pos;
+    off_t   flen;
+    int     bytes;
+    Byte    *cp;
+    Byte    *endpos;
+    Byte    *buffer;
+
+    /*
+     * 99.44% (or more) of the time, there will be no comment at the
+     * end of the zip file.  Try reading just enough to read the END
+     * record from the end of the file.
+     */
+    if ((pos = lseek(fd, -ENDHDR, SEEK_END)) < (off_t)0)
+        return (-1);
+    if ((bytes = read(fd, eb, ENDHDR)) < 0)
+        return (-1);
+    if (GETSIG(eb) == ENDSIG)
+        return (pos);
+
+    /*
+     * Shucky-Darn,... There is a comment at the end of the zip file.
+     *
+     * Allocate and fill a buffer with enough of the zip file
+     * to meet the specification for a maximal comment length.
+     */
+    if ((flen = lseek(fd, 0, SEEK_END)) < (off_t)0)
+        return (-1);
+    len = (flen < END_MAXLEN) ? flen : END_MAXLEN;
+    if (lseek(fd, -len, SEEK_END) < (off_t)0)
+        return (-1);
+    if ((buffer = malloc(END_MAXLEN)) == NULL)
+        return (-1);
+    if ((bytes = read(fd, buffer, len)) < 0) {
+        free(buffer);
+        return (-1);
+    }
+
+    /*
+     * Search backwards from the end of file stopping when the END header
+     * signature is found. (The first condition of the "if" is just a
+     * fast fail, because the GETSIG macro isn't always cheap.  The
+     * final condition protects against false positives.)
+     */
+    endpos = &buffer[bytes];
+    for (cp = &buffer[bytes - ENDHDR]; cp >= &buffer[0]; cp--)
+        if ((*cp == (ENDSIG & 0xFF)) && (GETSIG(cp) == ENDSIG) &&
+          (cp + ENDHDR + ENDCOM(cp) == endpos)) {
+            (void) memcpy(eb, cp, ENDHDR);
+            free(buffer);
+            return (flen - (endpos - cp));
+        }
+    free(buffer);
+    return (-1);
+}
+
+/*
+ * Locate the manifest file with the zip/jar file.
+ *
+ *      fd:     File descriptor of the jar file.
+ *      entry:  To be populated with the information necessary to perform
+ *              the inflation (the compressed and uncompressed sizes and
+ *              the offset in the file where the compressed data is located).
+ *
+ * Returns zero upon success. Returns a negative value upon failure.
+ *
+ * The buffer for reading the Central Directory if the zip/jar file needs
+ * to be large enough to accommodate the largest possible single record
+ * and the signature of the next record which is:
+ *
+ *      3*2**16 + CENHDR + SIGSIZ
+ *
+ * Each of the three variable sized fields (name, comment and extension)
+ * has a maximum possible size of 64k.
+ *
+ * Typically, only a small bit of this buffer is used with bytes shuffled
+ * down to the beginning of the buffer.  It is one thing to allocate such
+ * a large buffer and another thing to actually start faulting it in.
+ *
+ * In most cases, all that needs to be read are the first two entries in
+ * a typical jar file (META-INF and META-INF/MANIFEST.MF). Keep this factoid
+ * in mind when optimizing this code.
+ */
+#define BUFSIZE (3 * 65536 + CENHDR + SIGSIZ)
+#define MINREAD 1024
+
+static int
+find_file(int fd, zentry *entry, const char *file_name)
+{
+    int     bytes;
+    int     res;
+    int     entry_size;
+    int     read_size;
+    int     base_offset;
+    Byte    *p;
+    Byte    *bp;
+    Byte    buffer[BUFSIZE];
+    Byte    locbuf[LOCHDR];
+
+    p = buffer;
+    bp = buffer;
+
+    /*
+     * Read the END Header, which is the starting point for ZIP files.
+     * (Clearly designed to make writing a zip file easier than reading
+     * one. Now isn't that precious...)
+     */
+    if ((base_offset = find_end(fd, bp)) == -1)
+        return (-1);
+
+    /*
+     * There is a historical, but undocumented, ability to allow for
+     * additional "stuff" to be prepended to the zip/jar file. It seems
+     * that this has been used to prepend an actual java launcher
+     * executable to the jar on Windows.  Although this is just another
+     * form of statically linking a small piece of the JVM to the
+     * application, we choose to continue to support it.  Note that no
+     * guarantees have been made (or should be made) to the customer that
+     * this will continue to work.
+     *
+     * Therefore, calculate the base offset of the zip file (within the
+     * expanded file) by assuming that the central directory is followed
+     * immediately by the end record.
+     */
+    base_offset = base_offset - ENDSIZ(p) - ENDOFF(p);
+
+    /*
+     * The END Header indicates the start of the Central Directory
+     * Headers. Remember that the desired Central Directory Header (CEN)
+     * will almost always be the second one and the first one is a small
+     * directory entry ("META-INF/"). Keep the code optimized for
+     * that case.
+     *
+     * Begin by seeking to the beginning of the Central Directory and
+     * reading in the first buffer full of bits.
+     */
+    if (lseek(fd, base_offset + ENDOFF(p), SEEK_SET) < (off_t)0)
+        return (-1);
+    if ((bytes = read(fd, bp, MINREAD)) < 0)
+        return (-1);
+
+    /*
+     * Loop through the Central Directory Headers. Note that a valid zip/jar
+     * must have an ENDHDR (with ENDSIG) after the Central Directory.
+     */
+    while (GETSIG(p) == CENSIG) {
+
+        /*
+         * If a complete header isn't in the buffer, shift the contents
+         * of the buffer down and refill the buffer.  Note that the check
+         * for "bytes < CENHDR" must be made before the test for the entire
+         * size of the header, because if bytes is less than CENHDR, the
+         * actual size of the header can't be determined. The addition of
+         * SIGSIZ guarantees that the next signature is also in the buffer
+         * for proper loop termination.
+         */
+        if (bytes < CENHDR) {
+            p = memmove(bp, p, bytes);
+            if ((res = read(fd, bp + bytes, MINREAD)) <= 0)
+                return (-1);
+            bytes += res;
+        }
+        entry_size = CENHDR + CENNAM(p) + CENEXT(p) + CENCOM(p);
+        if (bytes < entry_size + SIGSIZ) {
+            if (p != bp)
+                p = memmove(bp, p, bytes);
+            read_size = entry_size - bytes + SIGSIZ;
+            read_size = (read_size < MINREAD) ? MINREAD : read_size;
+            if ((res = read(fd, bp + bytes,  read_size)) <= 0)
+                return (-1);
+            bytes += res;
+        }
+
+        /*
+         * Check if the name is the droid we are looking for; the jar file
+         * manifest.  If so, build the entry record from the data found in
+         * the header located and return success.
+         */
+        if (CENNAM(p) == strlen(file_name) &&
+          memcmp((p + CENHDR), file_name, strlen(file_name)) == 0) {
+            if (lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (off_t)0)
+                return (-1);
+            if (read(fd, locbuf, LOCHDR) < 0)
+                return (-1);
+            if (GETSIG(locbuf) != LOCSIG)
+                return (-1);
+            entry->isize = CENLEN(p);
+            entry->csize = CENSIZ(p);
+            entry->offset = base_offset + CENOFF(p) + LOCHDR +
+                LOCNAM(locbuf) + LOCEXT(locbuf);
+            entry->how = CENHOW(p);
+            return (0);
+        }
+
+        /*
+         * Point to the next entry and decrement the count of valid remaining
+         * bytes.
+         */
+        bytes -= entry_size;
+        p += entry_size;
+    }
+
+    return (-1);        /* Fell off the end the loop without a Manifest */
+}
+
+/*
+ * Parse a Manifest file header entry into a distinct "name" and "value".
+ * Continuation lines are joined into a single "value". The documented
+ * syntax for a header entry is:
+ *
+ *      header: name ":" value
+ *
+ *      name: alphanum *headerchar
+ *
+ *      value: SPACE *otherchar newline *continuation
+ *
+ *      continuation: SPACE *otherchar newline
+ *
+ *      newline: CR LF | LF | CR (not followed by LF)
+ *
+ *      alphanum: {"A"-"Z"} | {"a"-"z"} | {"0"-"9"}
+ *
+ *      headerchar: alphanum | "-" | "_"
+ *
+ *      otherchar: any UTF-8 character except NUL, CR and LF
+ *
+ * Note that a manifest file may be composed of multiple sections,
+ * each of which may contain multiple headers.
+ *
+ *      section: *header +newline
+ *
+ *      nonempty-section: +header +newline
+ *
+ * (Note that the point of "nonempty-section" is unclear, because it isn't
+ * referenced elsewhere in the full specification for the Manifest file.)
+ *
+ * Arguments:
+ *      lp      pointer to a character pointer which points to the start
+ *              of a valid header.
+ *      name    pointer to a character pointer which will be set to point
+ *              to the name portion of the header (nul terminated).
+ *      value   pointer to a character pointer which will be set to point
+ *              to the value portion of the header (nul terminated).
+ *
+ * Returns:
+ *    1 Successful parsing of an NV pair.  lp is updated to point to the
+ *      next character after the terminating newline in the string
+ *      representing the Manifest file. name and value are updated to
+ *      point to the strings parsed.
+ *    0 A valid end of section indicator was encountered.  lp, name, and
+ *      value are not modified.
+ *   -1 lp does not point to a valid header. Upon return, the values of
+ *      lp, name, and value are undefined.
+ */
+static int
+parse_nv_pair(char **lp, char **name, char **value)
+{
+    char    *nl;
+    char    *cp;
+
+    /*
+     * End of the section - return 0. The end of section condition is
+     * indicated by either encountering a blank line or the end of the
+     * Manifest "string" (EOF).
+     */
+    if (**lp == '\0' || **lp == '\n' || **lp == '\r')
+        return (0);
+
+    /*
+     * Getting to here, indicates that *lp points to an "otherchar".
+     * Turn the "header" into a string on its own.
+     */
+    nl = strpbrk(*lp, "\n\r");
+    if (nl == NULL) {
+        nl = strchr(*lp, (int)'\0');
+    } else {
+        cp = nl;                        /* For merging continuation lines */
+        if (*nl == '\r' && *(nl+1) == '\n')
+            *nl++ = '\0';
+        *nl++ = '\0';
+
+        /*
+         * Process any "continuation" line(s), by making them part of the
+         * "header" line. Yes, I know that we are "undoing" the NULs we
+         * just placed here, but continuation lines are the fairly rare
+         * case, so we shouldn't unnecessarily complicate the code above.
+         *
+         * Note that an entire continuation line is processed each iteration
+         * through the outer while loop.
+         */
+        while (*nl == ' ') {
+            nl++;                       /* First character to be moved */
+            while (*nl != '\n' && *nl != '\r' && *nl != '\0')
+                *cp++ = *nl++;          /* Shift string */
+            if (*nl == '\0')
+                return (-1);            /* Error: newline required */
+            *cp = '\0';
+            if (*nl == '\r' && *(nl+1) == '\n')
+                *nl++ = '\0';
+            *nl++ = '\0';
+        }
+    }
+
+    /*
+     * Separate the name from the value;
+     */
+    cp = strchr(*lp, (int)':');
+    if (cp == NULL)
+        return (-1);
+    *cp++ = '\0';               /* The colon terminates the name */
+    if (*cp != ' ')
+        return (-1);
+    *cp++ = '\0';               /* Eat the required space */
+    *name = *lp;
+    *value = cp;
+    *lp = nl;
+    return (1);
+}
+
+/*
+ * Read the manifest from the specified jar file and fill in the manifest_info
+ * structure with the information found within.
+ *
+ * Error returns are as follows:
+ *    0 Success
+ *   -1 Unable to open jarfile
+ *   -2 Error accessing the manifest from within the jarfile (most likely
+ *      a manifest is not present, or this isn't a valid zip/jar file).
+ */
+int
+JLI_ParseManifest(char *jarfile, manifest_info *info)
+{
+    int     fd;
+    zentry  entry;
+    char    *lp;
+    char    *name;
+    char    *value;
+    int     rc;
+    char    *splashscreen_name = NULL;
+
+    if ((fd = open(jarfile, O_RDONLY
+#ifdef O_BINARY
+        | O_BINARY /* use binary mode on windows */
+#endif
+        )) == -1)
+        return (-1);
+
+    info->manifest_version = NULL;
+    info->main_class = NULL;
+    info->jre_version = NULL;
+    info->jre_restrict_search = 0;
+    info->splashscreen_image_file_name = NULL;
+    if (rc = find_file(fd, &entry, manifest_name) != 0) {
+        close(fd);
+        return (-2);
+    }
+    manifest = inflate_file(fd, &entry, NULL);
+    if (manifest == NULL) {
+        close(fd);
+        return (-2);
+    }
+    lp = manifest;
+    while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) {
+        if (strcasecmp(name, "Manifest-Version") == 0)
+            info->manifest_version = value;
+        else if (strcasecmp(name, "Main-Class") == 0)
+            info->main_class = value;
+        else if (strcasecmp(name, "JRE-Version") == 0)
+            info->jre_version = value;
+        else if (strcasecmp(name, "JRE-Restrict-Search") == 0) {
+            if (strcasecmp(value, "true") == 0)
+                info->jre_restrict_search = 1;
+        } else if (strcasecmp(name, "Splashscreen-Image") == 0) {
+            info->splashscreen_image_file_name = value;
+        }
+    }
+    close(fd);
+    if (rc == 0)
+        return (0);
+    else
+        return (-2);
+}
+
+/*
+ * Opens the jar file and unpacks the specified file from its contents.
+ * Returns NULL on failure.
+ */
+void *
+JLI_JarUnpackFile(const char *jarfile, const char *filename, int *size) {
+    int     fd;
+    zentry  entry;
+    void    *data = NULL;
+
+    fd = open(jarfile, O_RDONLY
+#ifdef O_BINARY
+        | O_BINARY /* use binary mode on windows */
+#endif
+        );
+    if (fd != -1 && find_file(fd, &entry, filename) == 0) {
+        data = inflate_file(fd, &entry, size);
+    }
+    close(fd);
+    return (data);
+}
+
+/*
+ * Specialized "free" function.
+ */
+void
+JLI_FreeManifest()
+{
+    if (manifest)
+        free(manifest);
+}
+
+/*
+ * Iterate over the manifest of the specified jar file and invoke the provided
+ * closure function for each attribute encountered.
+ *
+ * Error returns are as follows:
+ *    0 Success
+ *   -1 Unable to open jarfile
+ *   -2 Error accessing the manifest from within the jarfile (most likely
+ *      this means a manifest is not present, or it isn't a valid zip/jar file).
+ */
+int
+JLI_ManifestIterate(const char *jarfile, attribute_closure ac, void *user_data)
+{
+    int     fd;
+    zentry  entry;
+    char    *mp;        /* manifest pointer */
+    char    *lp;        /* pointer into manifest, updated during iteration */
+    char    *name;
+    char    *value;
+    int     rc;
+
+    if ((fd = open(jarfile, O_RDONLY
+#ifdef O_BINARY
+        | O_BINARY /* use binary mode on windows */
+#endif
+        )) == -1)
+        return (-1);
+
+    if (rc = find_file(fd, &entry, manifest_name) != 0) {
+        close(fd);
+        return (-2);
+    }
+
+    mp = inflate_file(fd, &entry, NULL);
+    if (mp == NULL) {
+        close(fd);
+        return (-2);
+    }
+
+    lp = mp;
+    while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) {
+        (*ac)(name, value, user_data);
+    }
+    free(mp);
+    close(fd);
+    if (rc == 0)
+        return (0);
+    else
+        return (-2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/version_comp.c	Wed Dec 15 00:54:34 2010 +0000
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "jni.h"
+#include "jli_util.h"
+#include "version_comp.h"
+
+/*
+ *      A collection of useful strings. One should think of these as #define
+ *      entries, but actual strings can be more efficient (with many compilers).
+ */
+static const char *separators   = ".-_";
+static const char *zero_string  = "0";
+
+/*
+ *      Validate a string as parsable as a "Java int". If so parsable,
+ *      return true (non-zero) and store the numeric value at the address
+ *      passed in as "value"; otherwise return false (zero).
+ *
+ *      Note that the maximum allowable value is 2147483647 as defined by
+ *      the "Java Language Specification" which precludes the use of native
+ *      conversion routines which may have other limits.
+ *
+ *      Also note that we don't have to worry about the alternate maximum
+ *      allowable value of 2147483648 because it is only allowed after
+ *      the unary negation operator and this grammar doesn't have one
+ *      of those.
+ *
+ *      Finally, note that a value which exceeds the maximum jint value will
+ *      return false (zero). This results in the otherwise purely numeric
+ *      string being compared as a string of characters (as per the spec.)
+ */
+static int
+isjavaint(const char *s, jint *value)
+{
+    jlong sum = 0;
+    jint digit;
+    while (*s != '\0')
+        if (isdigit(*s)) {
+            digit = (jint)((int)(*s++) - (int)('0'));
+            sum = (sum * 10) + digit;
+            if (sum > 2147483647)
+                return (0);     /* Overflows jint (but not jlong) */
+        } else
+            return (0);
+    *value = (jint)sum;
+    return (1);
+}
+
+/*
+ *      Modeled after strcmp(), compare two strings (as in the grammar defined
+ *      in Appendix A of JSR 56).  If both strings can be interpreted as
+ *      Java ints, do a numeric comparison, else it is strcmp().
+ */
+static int
+comp_string(const char *s1, const char *s2)
+{
+    jint v1, v2;
+    if (isjavaint(s1, &v1) && isjavaint(s2, &v2))
+        return ((int)(v1 - v2));
+    else
+        return (strcmp(s1, s2));
+}
+
+/*
+ *      Modeled after strcmp(), compare two version-ids for a Prefix
+ *      Match as defined in JSR 56.
+ */
+int
+JLI_PrefixVersionId(char *id1, char *id2)
+{
+    char        *s1 = JLI_StringDup(id1);
+    char        *s2 = JLI_StringDup(id2);
+    char        *m1 = s1;
+    char        *m2 = s2;
+    char        *end1 = NULL;
+    char        *end2 = NULL;
+    int res = 0;
+
+    do {
+
+        if ((s1 != NULL) && ((end1 = strpbrk(s1, ".-_")) != NULL))
+            *end1 = '\0';
+        if ((s2 != NULL) && ((end2 = strpbrk(s2, ".-_")) != NULL))
+            *end2 = '\0';
+
+        res = comp_string(s1, s2);
+
+        if (end1 != NULL)
+            s1 = end1 + 1;
+        else
+            s1 = NULL;
+        if (end2 != NULL)
+            s2 = end2 + 1;
+        else
+            s2 = NULL;
+
+    } while (res == 0 && ((s1 != NULL) && (s2 != NULL)));
+
+    JLI_MemFree(m1);
+    JLI_MemFree(m2);
+    return (res);
+}
+
+/*
+ *      Modeled after strcmp(), compare two version-ids for an Exact
+ *      Match as defined in JSR 56.
+ */
+int
+JLI_ExactVersionId(char *id1, char *id2)
+{
+    char        *s1 = JLI_StringDup(id1);
+    char        *s2 = JLI_StringDup(id2);
+    char        *m1 = s1;
+    char        *m2 = s2;
+    char        *end1 = NULL;
+    char        *end2 = NULL;
+    int res = 0;
+
+    do {
+
+        if ((s1 != NULL) && ((end1 = strpbrk(s1, separators)) != NULL))
+            *end1 = '\0';
+        if ((s2 != NULL) && ((end2 = strpbrk(s2, separators)) != NULL))
+            *end2 = '\0';
+
+        if ((s1 != NULL) && (s2 == NULL))
+            res = comp_string(s1, zero_string);
+        else if ((s1 == NULL) && (s2 != NULL))
+            res = comp_string(zero_string, s2);
+        else
+            res = comp_string(s1, s2);
+
+        if (end1 != NULL)
+            s1 = end1 + 1;
+        else
+            s1 = NULL;
+        if (end2 != NULL)
+            s2 = end2 + 1;
+        else
+            s2 = NULL;
+
+    } while (res == 0 && ((s1 != NULL) || (s2 != NULL)));
+
+    JLI_MemFree(m1);
+    JLI_MemFree(m2);
+    return (res);
+}
+
+/*
+ *      Return true if this simple-element (as defined in JSR 56) forms
+ *      an acceptable match.
+ *
+ *      JSR 56 is modified by the Java Web Start <rel> Developer Guide
+ *      where it is stated "... Java Web Start will not consider an installed
+ *      non-FCS (i.e., milestone) JRE as a match. ... a JRE from Sun
+ *      Microsystems, Inc., is by convention a non-FCS (milestone) JRE
+ *      if there is a dash (-) in the version string."
+ *
+ *      An undocumented caveat to the above is that an exact match with a
+ *      hyphen is accepted as a development extension.
+ *
+ *      These modifications are addressed by the specific comparisons
+ *      for releases with hyphens.
+ */
+static int
+acceptable_simple_element(char *release, char *simple_element)
+{
+    char        *modifier;
+    modifier = simple_element + strlen(simple_element) - 1;
+    if (*modifier == '*') {
+        *modifier = '\0';
+        if (strchr(release, '-'))
+            return ((strcmp(release, simple_element) == 0)?1:0);
+        return ((JLI_PrefixVersionId(release, simple_element) == 0)?1:0);
+    } else if (*modifier == '+') {
+        *modifier = '\0';
+        if (strchr(release, '-'))
+            return ((strcmp(release, simple_element) == 0)?1:0);
+        return ((JLI_ExactVersionId(release, simple_element) >= 0)?1:0);
+    } else {
+        return ((JLI_ExactVersionId(release, simple_element) == 0)?1:0);
+    }
+}
+
+/*
+ *      Return true if this element (as defined in JSR 56) forms
+ *      an acceptable match. An element is the intersection (and)
+ *      of multiple simple-elements.
+ */
+static int
+acceptable_element(char *release, char *element)
+{
+    char        *end;
+    do {
+        if ((end = strchr(element, '&')) != NULL)
+            *end = '\0';
+        if (!acceptable_simple_element(release, element))
+            return (0);
+        if (end != NULL)
+            element = end + 1;
+    } while (end != NULL);
+    return (1);
+}
+
+/*
+ *      Checks if release is acceptable by the specification version-string.
+ *      Return true if this version-string (as defined in JSR 56) forms
+ *      an acceptable match. A version-string is the union (or) of multiple
+ *      elements.
+ */
+int
+JLI_AcceptableRelease(char *release, char *version_string)
+{
+    char        *vs;
+    char        *m1;
+    char        *end;
+    m1 = vs = JLI_StringDup(version_string);
+    do {
+        if ((end = strchr(vs, ' ')) != NULL)
+            *end = '\0';
+        if (acceptable_element(release, vs)) {
+            JLI_MemFree(m1);
+            return (1);
+        }
+        if (end != NULL)
+            vs = end + 1;
+    } while (end != NULL);
+    JLI_MemFree(m1);
+    return (0);
+}
+
+/*
+ *      Return true if this is a valid simple-element (as defined in JSR 56).
+ *
+ *      The official grammar for a simple-element is:
+ *
+ *              simple-element  ::= version-id | version-id modifier
+ *              modifier        ::= '+' | '*'
+ *              version-id      ::= string ( separator  string )*
+ *              string          ::= char ( char )*
+ *              char            ::= Any ASCII character except a space, an
+ *                                  ampersand, a separator or a modifier
+ *              separator       ::= '.' | '-' | '_'
+ *
+ *      However, for efficiency, it is time to abandon the top down parser
+ *      implementation.  After deleting the potential trailing modifier, we
+ *      are left with a version-id.
+ *
+ *      Note that a valid version-id has three simple properties:
+ *
+ *      1) Doesn't contain a space, an ampersand or a modifier.
+ *
+ *      2) Doesn't begin or end with a separator.
+ *
+ *      3) Doesn't contain two adjacent separators.
+ *
+ *      Any other line noise constitutes a valid version-id.
+ */
+static int
+valid_simple_element(char *simple_element)
+{
+    char        *last;
+    size_t      len;
+
+    if ((simple_element == NULL) || ((len = strlen(simple_element)) == 0))
+        return (0);
+    last = simple_element + len - 1;
+    if (*last == '*' || *last == '+') {
+        if (--len == 0)
+            return (0);
+        *last-- = '\0';
+    }
+    if (strpbrk(simple_element, " &+*") != NULL)        /* Property #1 */
+        return (0);
+    if ((strchr(".-_", *simple_element) != NULL) ||     /* Property #2 */
+      (strchr(".-_", *last) != NULL))
+        return (0);
+    for (; simple_element != last; simple_element++)    /* Property #3 */
+        if ((strchr(".-_", *simple_element) != NULL) &&
+          (strchr(".-_", *(simple_element + 1)) != NULL))
+            return (0);
+    return (1);
+}
+
+/*
+ *      Return true if this is a valid element (as defined in JSR 56).
+ *      An element is the intersection (and) of multiple simple-elements.
+ */
+static int
+valid_element(char *element)
+{
+    char        *end;
+    if ((element == NULL) || (strlen(element) == 0))
+        return (0);
+    do {
+        if ((end = strchr(element, '&')) != NULL)
+            *end = '\0';
+        if (!valid_simple_element(element))
+            return (0);
+        if (end != NULL)
+            element = end + 1;
+    } while (end != NULL);
+    return (1);
+}
+
+/*
+ *      Validates a version string by the extended JSR 56 grammar.
+ */
+int
+JLI_ValidVersionString(char *version_string)
+{
+    char        *vs;
+    char        *m1;
+    char        *end;
+    if ((version_string == NULL) || (strlen(version_string) == 0))
+        return (0);
+    m1 = vs = JLI_StringDup(version_string);
+    do {
+        if ((end = strchr(vs, ' ')) != NULL)
+            *end = '\0';
+        if (!valid_element(vs)) {
+            JLI_MemFree(m1);
+            return (0);
+        }
+        if (end != NULL)
+            vs = end + 1;
+    } while (end != NULL);
+    JLI_MemFree(m1);
+    return (1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/launcher/wildcard.c	Wed Dec 15 00:54:34 2010 +0000
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Class-Path Wildcards
+ *
+ * The syntax for wildcards is a single asterisk. The class path
+ * foo/"*", e.g., loads all jar files in the directory named foo.
+ * (This requires careful quotation when used in shell scripts.)
+ *
+ * Only files whose names end in .jar or .JAR are matched.
+ * Files whose names end in .zip, or which have a particular
+ * magic number, regardless of filename extension, are not
+ * matched.
+ *
+ * Files are considered regardless of whether or not they are
+ * "hidden" in the UNIX sense, i.e., have names beginning with '.'.
+ *
+ * A wildcard only matches jar files, not class files in the same
+ * directory.  If you want to load both class files and jar files from
+ * a single directory foo then you can say foo:foo/"*", or foo/"*":foo
+ * if you want the jar files to take precedence.
+ *
+ * Subdirectories are not searched recursively, i.e., foo/"*" only
+ * looks for jar files in foo, not in foo/bar, foo/baz, etc.
+ *
+ * Expansion of wildcards is done early, prior to the invocation of a
+ * program's main method, rather than late, during the class-loading
+ * process itself.  Each element of the input class path containing a
+ * wildcard is replaced by the (possibly empty) sequence of elements
+ * generated by enumerating the jar files in the named directory.  If
+ * the directory foo contains a.jar, b.jar, and c.jar,
+ * e.g., then the class path foo/"*" is expanded into
+ * foo/a.jar:foo/b.jar:foo/c.jar, and that string would be the value
+ * of the system property java.class.path.
+ *
+ * The order in which the jar files in a directory are enumerated in
+ * the expanded class path is not specified and may vary from platform
+ * to platform and even from moment to moment on the same machine.  A
+ * well-constructed application should not depend upon any particular
+ * order.  If a specific order is required then the jar files can be
+ * enumerated explicitly in the class path.
+ *
+ * The CLASSPATH environment variable is not treated any differently
+ * from the -classpath (equiv. -cp) command-line option,
+ * i.e. wildcards are honored in all these cases.
+ *
+ * Class-path wildcards are not honored in the Class-Path jar-manifest
+ * header.
+ *
+ * Class-path wildcards are honored not only by the Java launcher but
+ * also by most other command-line tools that accept class paths, and
+ * in particular by javac and javadoc.
+ *
+ * Class-path wildcards are not honored in any other kind of path, and
+ * especially not in the bootstrap class path, which is a mere
+ * artifact of our implementation and not something that developers
+ * should use.
+ *
+ * Classpath wildcards are only expanded in the Java launcher code,
+ * supporting the use of wildcards on the command line and in the
+ * CLASSPATH environment variable.  We do not support the use of
+ * wildcards by applications that embed the JVM.
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "java.h"       /* Strictly for PATH_SEPARATOR/FILE_SEPARATOR */
+#include "jli_util.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#else /* Unix */
+#include <unistd.h>
+#include <dirent.h>
+#endif /* Unix */
+
+static int
+exists(const char* filename)
+{
+#ifdef _WIN32
+    return _access(filename, 0) == 0;
+#else
+    return access(filename, F_OK) == 0;
+#endif
+}
+
+#define NEW_(TYPE) ((TYPE) JLI_MemAlloc(sizeof(struct TYPE##_)))
+
+/*
+ * Wildcard directory iteration.
+ * WildcardIterator_for(wildcard) returns an iterator.
+ * Each call to that iterator's next() method returns the basename
+ * of an entry in the wildcard's directory.  The basename's memory
+ * belongs to the iterator.  The caller is responsible for prepending
+ * the directory name and file separator, if necessary.
+ * When done with the iterator, call the close method to clean up.
+ */
+typedef struct WildcardIterator_* WildcardIterator;
+
+#ifdef _WIN32
+struct WildcardIterator_
+{
+    HANDLE handle;
+    char *firstFile; /* Stupid FindFirstFile...FindNextFile */
+};
+
+static WildcardIterator
+WildcardIterator_for(const char *wildcard)
+{
+    WIN32_FIND_DATA find_data;
+    WildcardIterator it = NEW_(WildcardIterator);
+    HANDLE handle = FindFirstFile(wildcard, &find_data);
+    if (handle == INVALID_HANDLE_VALUE)
+        return NULL;
+    it->handle = handle;
+    it->firstFile = find_data.cFileName;
+    return it;
+}
+
+static char *
+WildcardIterator_next(WildcardIterator it)
+{
+    WIN32_FIND_DATA find_data;
+    if (it->firstFile != NULL) {
+        char *firstFile = it->firstFile;
+        it->firstFile = NULL;
+        return firstFile;
+    }
+    return FindNextFile(it->handle, &find_data)
+        ? find_data.cFileName : NULL;
+}
+
+static void
+WildcardIterator_close(WildcardIterator it)
+{
+    if (it) {
+        FindClose(it->handle);
+        JLI_MemFree(it->firstFile);
+        JLI_MemFree(it);
+    }
+}
+
+#else /* Unix */
+struct WildcardIterator_
+{
+    DIR *dir;
+};
+
+static WildcardIterator
+WildcardIterator_for(const char *wildcard)
+{
+    DIR *dir;
+    int wildlen = strlen(wildcard);
+    if (wildlen < 2) {
+        dir = opendir(".");
+    } else {
+        char *dirname = JLI_StringDup(wildcard);
+        dirname[wildlen - 1] = '\0';
+        dir = opendir(dirname);
+        JLI_MemFree(dirname);
+    }
+    if (dir == NULL)
+        return NULL;
+    else {
+        WildcardIterator it = NEW_(WildcardIterator);
+        it->dir = dir;
+        return it;
+    }
+}
+
+static char *
+WildcardIterator_next(WildcardIterator it)
+{
+    struct dirent* dirp = readdir(it->dir);
+    return dirp ? dirp->d_name : NULL;
+}
+
+static void
+WildcardIterator_close(WildcardIterator it)
+{
+    if (it) {
+        closedir(it->dir);
+        JLI_MemFree(it);
+    }
+}
+#endif /* Unix */
+
+static int
+equal(const char *s1, const char *s2)
+{
+    return strcmp(s1, s2) == 0;
+}
+
+/*
+ * FileList ADT - a dynamic list of C filenames
+ */
+struct FileList_
+{
+    char **files;
+    int size;
+    int capacity;
+};
+typedef struct FileList_ *FileList;
+
+static FileList
+FileList_new(int capacity)
+{
+    FileList fl = NEW_(FileList);
+    fl->capacity = capacity;
+    fl->files = (char **) JLI_MemAlloc(capacity * sizeof(fl->files[0]));
+    fl->size = 0;
+    return fl;
+}
+
+#ifdef DEBUG_WILDCARD
+static void
+FileList_print(FileList fl)
+{
+    int i;
+    putchar('[');
+    for (i = 0; i < fl->size; i++) {
+        if (i > 0) printf(", ");
+        printf("\"%s\"",fl->files[i]);
+    }
+    putchar(']');
+}
+#endif
+
+static void
+FileList_free(FileList fl)
+{
+    if (fl) {
+        if (fl->files) {
+            int i;
+            for (i = 0; i < fl->size; i++)
+                JLI_MemFree(fl->files[i]);
+            JLI_MemFree(fl->files);
+        }
+        JLI_MemFree(fl);
+    }
+}
+
+static void
+FileList_ensureCapacity(FileList fl, int capacity)
+{
+    if (fl->capacity < capacity) {
+        while (fl->capacity < capacity)
+            fl->capacity *= 2;
+        fl->files = JLI_MemRealloc(fl->files,
+                               fl->capacity * sizeof(fl->files[0]));
+    }
+}
+
+static void
+FileList_add(FileList fl, char *file)
+{
+    FileList_ensureCapacity(fl, fl->size+1);
+    fl->files[fl->size++] = file;
+}
+
+static void
+FileList_addSubstring(FileList fl, const char *beg, int len)
+{
+    char *filename = (char *) JLI_MemAlloc(len+1);
+    memcpy(filename, beg, len);
+    filename[len] = '\0';
+    FileList_ensureCapacity(fl, fl->size+1);
+    fl->files[fl->size++] = filename;
+}
+
+static char *
+FileList_join(FileList fl, char sep)
+{
+    int i;
+    int size;
+    char *path;
+    char *p;
+    for (i = 0, size = 1; i < fl->size; i++)
+        size += strlen(fl->files[i]) + 1;
+
+    path = JLI_MemAlloc(size);
+
+    for (i = 0, p = path; i < fl->size; i++) {
+        int len = strlen(fl->files[i]);
+        if (i > 0) *p++ = sep;
+        memcpy(p, fl->files[i], len);
+        p += len;
+    }
+    *p = '\0';
+
+    return path;
+}
+
+static FileList
+FileList_split(const char *path, char sep)
+{
+    const char *p, *q;
+    int len = strlen(path);
+    int count;
+    FileList fl;
+    for (count = 1, p = path; p < path + len; p++)
+        count += (*p == sep);
+    fl = FileList_new(count);
+    for (p = path;;) {
+        for (q = p; q <= path + len; q++) {
+            if (*q == sep || *q == '\0') {
+                FileList_addSubstring(fl, p, q - p);
+                if (*q == '\0')
+                    return fl;
+                p = q + 1;
+            }
+        }
+    }
+}
+
+static int
+isJarFileName(const char *filename)
+{
+    int len = strlen(filename);
+    return (len >= 4) &&
+        (filename[len - 4] == '.') &&
+        (equal(filename + len - 3, "jar") ||
+         equal(filename + len - 3, "JAR")) &&
+        /* Paranoia: Maybe filename is "DIR:foo.jar" */
+        (strchr(filename, PATH_SEPARATOR) == NULL);
+}
+
+static char *
+wildcardConcat(const char *wildcard, const char *basename)
+{
+    int wildlen = strlen(wildcard);
+    int baselen = strlen(basename);
+    char *filename = (char *) JLI_MemAlloc(wildlen + baselen);
+    /* Replace the trailing '*' with basename */
+    memcpy(filename, wildcard, wildlen-1);
+    memcpy(filename+wildlen-1, basename, baselen+1);
+    return filename;
+}
+
+static FileList
+wildcardFileList(const char *wildcard)
+{
+    const char *basename;
+    FileList fl = FileList_new(16);
+    WildcardIterator it = WildcardIterator_for(wildcard);
+    if (it == NULL)
+        return NULL;
+    while ((basename = WildcardIterator_next(it)) != NULL)
+        if (isJarFileName(basename))
+            FileList_add(fl, wildcardConcat(wildcard, basename));
+    WildcardIterator_close(it);
+    return fl;
+}
+
+static int
+isWildcard(const char *filename)
+{
+    int len = strlen(filename);
+    return (len > 0) &&
+        (filename[len - 1] == '*') &&
+        (len == 1 || IS_FILE_SEPARATOR(filename[len - 2])) &&
+        (! exists(filename));
+}
+
+static void
+FileList_expandWildcards(FileList fl)
+{
+    int i, j;
+    for (i = 0; i < fl->size; i++) {
+        if (isWildcard(fl->files[i])) {
+            FileList expanded = wildcardFileList(fl->files[i]);
+            if (expanded != NULL && expanded->size > 0) {
+                JLI_MemFree(fl->files[i]);
+                FileList_ensureCapacity(fl, fl->size + expanded->size);
+                for (j = fl->size - 1; j >= i+1; j--)
+                    fl->files[j+expanded->size-1] = fl->files[j];
+                for (j = 0; j < expanded->size; j++)
+                    fl->files[i+j] = expanded->files[j];
+                i += expanded->size - 1;
+                fl->size += expanded->size - 1;
+                /* fl expropriates expanded's elements. */
+                expanded->size = 0;
+            }
+            FileList_free(expanded);
+        }
+    }
+}
+
+const char *
+JLI_WildcardExpandClasspath(const char *classpath)
+{
+    char *expanded;
+    FileList fl;
+
+    if (strchr(classpath, '*') == NULL)
+        return classpath;
+    fl = FileList_split(classpath, PATH_SEPARATOR);
+    FileList_expandWildcards(fl);
+    expanded = FileList_join(fl, PATH_SEPARATOR);
+    FileList_free(fl);
+    if (getenv("_JAVA_LAUNCHER_DEBUG") != 0)
+        printf("Expanded wildcards:\n"
+               "    before: \"%s\"\n"
+               "    after : \"%s\"\n",
+               classpath, expanded);
+    return expanded;
+}
+
+#ifdef DEBUG_WILDCARD
+static void
+wildcardExpandArgv(const char ***argv)
+{
+    int i;
+    for (i = 0; (*argv)[i]; i++) {
+        if (equal((*argv)[i], "-cp") ||
+            equal((*argv)[i], "-classpath")) {
+            i++;
+            (*argv)[i] = wildcardExpandClasspath((*argv)[i]);
+        }
+    }
+}
+
+static void
+debugPrintArgv(char *argv[])
+{
+    int i;
+    putchar('[');
+    for (i = 0; argv[i]; i++) {
+        if (i > 0) printf(", ");
+        printf("\"%s\"", argv[i]);
+    }
+    printf("]\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+    argv[0] = "java";
+    wildcardExpandArgv((const char***)&argv);
+    debugPrintArgv(argv);
+    /* execvp("java", argv); */
+    return 0;
+}
+#endif /* DEBUG_WILDCARD */
+
+/* Cute little perl prototype implementation....
+
+my $sep = ($^O =~ /^(Windows|cygwin)/) ? ";" : ":";
+
+sub expand($) {
+  opendir DIR, $_[0] or return $_[0];
+  join $sep, map {"$_[0]/$_"} grep {/\.(jar|JAR)$/} readdir DIR;
+}
+
+sub munge($) {
+  join $sep,
+    map {(! -r $_ and s/[\/\\]+\*$//) ? expand $_ : $_} split $sep, $_[0];
+}
+
+for (my $i = 0; $i < @ARGV - 1; $i++) {
+  $ARGV[$i+1] = munge $ARGV[$i+1] if $ARGV[$i] =~ /^-c(p|lasspath)$/;
+}
+
+$ENV{CLASSPATH} = munge $ENV{CLASSPATH} if exists $ENV{CLASSPATH};
+@ARGV = ("java", @ARGV);
+print "@ARGV\n";
+exec @ARGV;
+
+*/