aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew John Hughes <[email protected]>2010-12-15 00:54:34 +0000
committerAndrew John Hughes <[email protected]>2010-12-15 00:54:34 +0000
commit1a459d905ea2d925df771508f17d05fa5f259225 (patch)
treedd05fa1679ee341ce3ecc0918149dafea4be3f47
parent41bd2f399b754a0cba5dd64ce5f32191cfe9ea38 (diff)
Remove dependency on libjli so we can work with IcedTea7.
2010-12-14 Andrew John Hughes <[email protected]> * 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.
-rw-r--r--ChangeLog15
-rw-r--r--Makefile.am9
-rw-r--r--launcher/jli_util.c83
-rw-r--r--launcher/parse_manifest.c610
-rw-r--r--launcher/version_comp.c357
-rw-r--r--launcher/wildcard.c495
6 files changed, 1564 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index 12bc9d4..ddd6737 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2010-12-14 Andrew John Hughes <[email protected]>
+
+ * 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 <[email protected]>
* netx/net/sourceforge/jnlp/config/ValueValidator.java: New file.
diff --git a/Makefile.am b/Makefile.am
index a999133..539fc4b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -71,17 +71,16 @@ endif
# 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 \
diff --git a/launcher/jli_util.c b/launcher/jli_util.c
new file mode 100644
index 0000000..6b64e65
--- /dev/null
+++ b/launcher/jli_util.c
@@ -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);
+}
diff --git a/launcher/parse_manifest.c b/launcher/parse_manifest.c
new file mode 100644
index 0000000..4ff0e00
--- /dev/null
+++ b/launcher/parse_manifest.c
@@ -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);
+}
diff --git a/launcher/version_comp.c b/launcher/version_comp.c
new file mode 100644
index 0000000..9df10d4
--- /dev/null
+++ b/launcher/version_comp.c
@@ -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);
+}
diff --git a/launcher/wildcard.c b/launcher/wildcard.c
new file mode 100644
index 0000000..7c5ca5e
--- /dev/null
+++ b/launcher/wildcard.c
@@ -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;
+
+*/