From 192554e63278ffff52e9eb985e8f9fae33b9638e Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Sun, 14 May 2006 20:38:32 +0000 Subject: PR classpath/27514: * gnu/java/net/IndexListParser.java (JAR_INDEX_FILE): Renamed. Now constant. (JAR_INDEX_VERSION_KEY): Likewise. (IndexListParser): Updated. (getVersionInfo): Likewise. * tools/gnu/classpath/tools/jar/Indexer.java: New file. * tools/gnu/classpath/tools/jar/Action.java (run): Now throws OptionException. * tools/gnu/classpath/tools/jar/Main.java (initializeParser): Handle -i. (ModeOption): New constructor. (parsed): Updated. Use setArchiveFile. (setArchiveFile): New method. (run): Handle no-argument case. (main): Emit --help message on option error. * tools/gnu/classpath/tools/jar/Updater.java (inputJar): New field. (createManifest): New method. (run): Updated. Throws OptionException. Correctly copy zip entry. * tools/gnu/classpath/tools/jar/Creator.java (createManifest): New method. (writeManifest): Removed. (outputStream): Now a JarOutputStream. (writeCommandLineEntries): Changed parameters. Updated callers. (run): Throws OptionException. * java/util/jar/JarOutputStream.java (putNextEntry): Typo fix. * java/util/jar/Manifest.java (read): Typo fix. --- tools/gnu/classpath/tools/jar/Action.java | 5 +- tools/gnu/classpath/tools/jar/Creator.java | 74 +++++++-------- tools/gnu/classpath/tools/jar/Indexer.java | 148 +++++++++++++++++++++++++++++ tools/gnu/classpath/tools/jar/Main.java | 32 +++++-- tools/gnu/classpath/tools/jar/Updater.java | 36 +++++-- 5 files changed, 242 insertions(+), 53 deletions(-) create mode 100644 tools/gnu/classpath/tools/jar/Indexer.java (limited to 'tools') diff --git a/tools/gnu/classpath/tools/jar/Action.java b/tools/gnu/classpath/tools/jar/Action.java index fea60f797..b6398936d 100644 --- a/tools/gnu/classpath/tools/jar/Action.java +++ b/tools/gnu/classpath/tools/jar/Action.java @@ -38,6 +38,8 @@ package gnu.classpath.tools.jar; +import gnu.classpath.tools.getopt.OptionException; + import java.io.IOException; public abstract class Action @@ -46,5 +48,6 @@ public abstract class Action { } - public abstract void run(Main parameters) throws IOException; + public abstract void run(Main parameters) + throws IOException, OptionException; } diff --git a/tools/gnu/classpath/tools/jar/Creator.java b/tools/gnu/classpath/tools/jar/Creator.java index 8a5d387a0..caf093b77 100644 --- a/tools/gnu/classpath/tools/jar/Creator.java +++ b/tools/gnu/classpath/tools/jar/Creator.java @@ -38,10 +38,9 @@ package gnu.classpath.tools.jar; -import gnu.classpath.SystemProperties; +import gnu.classpath.tools.getopt.OptionException; import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; @@ -52,15 +51,19 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; import java.util.zip.CRC32; import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; public class Creator extends Action { - ZipOutputStream outputStream; + JarOutputStream outputStream; HashSet writtenItems = new HashSet(); + // The manifest to use, or null if we don't want a manifest. + Manifest manifest; private long copyFile(CRC32 crc, InputStream is, OutputStream output) throws IOException @@ -172,13 +175,12 @@ public class Creator return allEntries; } - private void writeCommandLineEntries(Main parameters, ZipOutputStream os) + private void writeCommandLineEntries(Main parameters) throws IOException { - outputStream = os; - outputStream.setMethod(parameters.storageMode); - - writeManifest(parameters); + // We've already written the manifest, make sure to mark it. + writtenItems.add("META-INF/"); + writtenItems.add(JarFile.MANIFEST_NAME); ArrayList allEntries = getAllEntries(parameters); Iterator it = allEntries.iterator(); @@ -189,53 +191,47 @@ public class Creator } } - protected void writeCommandLineEntries(Main parameters, File zipFile) + protected Manifest createManifest(Main parameters) throws IOException { - OutputStream os = new BufferedOutputStream(new FileOutputStream(zipFile)); - writeCommandLineEntries(parameters, new ZipOutputStream(os)); - } - - protected void writeManifest(Main parameters) throws IOException - { - File manifestFile; - InputStream contents; + if (! parameters.wantManifest) + return null; if (parameters.manifestFile != null) { // User specified a manifest file. - contents = new FileInputStream(parameters.manifestFile); - } - else if (! parameters.wantManifest) - { - // User didn't want a manifest. - return; - } - else - { - String desc = ("Manifest-Version: 1.0\n" - + "Created-By: " - + SystemProperties.getProperty("java.version") - + " (GNU Classpath)\n\n"); - contents = new ByteArrayInputStream(desc.getBytes("UTF-8")); + InputStream contents = new FileInputStream(parameters.manifestFile); + return new Manifest(contents); } - // Make the META-INF directory and the manifest file. - writeFile(true, null, "META-INF/", parameters.verbose); - writeFile(false, contents, "META-INF/MANIFEST.MF", parameters.verbose); + return new Manifest(); + } + + protected void writeCommandLineEntries(Main parameters, OutputStream os) + throws IOException + { + manifest = createManifest(parameters); + outputStream = new JarOutputStream(os, manifest); + // FIXME: in Classpath this sets the method too late for the + // manifest file. + outputStream.setMethod(parameters.storageMode); + writeCommandLineEntries(parameters); } protected void close() throws IOException { - // FIXME: handle index file here ...? outputStream.finish(); outputStream.close(); } - public void run(Main parameters) throws IOException + public void run(Main parameters) throws IOException, OptionException { if (parameters.archiveFile == null || parameters.archiveFile.equals("-")) - writeCommandLineEntries(parameters, new ZipOutputStream(System.out)); + writeCommandLineEntries(parameters, System.out); else - writeCommandLineEntries(parameters, parameters.archiveFile); + { + OutputStream os + = new BufferedOutputStream(new FileOutputStream(parameters.archiveFile)); + writeCommandLineEntries(parameters, os); + } close(); } } diff --git a/tools/gnu/classpath/tools/jar/Indexer.java b/tools/gnu/classpath/tools/jar/Indexer.java new file mode 100644 index 000000000..62959dbf3 --- /dev/null +++ b/tools/gnu/classpath/tools/jar/Indexer.java @@ -0,0 +1,148 @@ +/* Indexer.java -- add index.list file to jar + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.classpath.tools.jar; + +import gnu.classpath.tools.getopt.OptionException; +import gnu.java.net.IndexListParser; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +public class Indexer + extends Updater +{ + private void indexJarFile(StringBuffer result, File fileName, + boolean verbose) + throws IOException + { + if (verbose) + System.err.println("indexing: " + fileName); + JarFile jf = new JarFile(fileName); + + // Index the files in this jar. + HashSet entries = new HashSet(); + Enumeration e = jf.entries(); + while (e.hasMoreElements()) + { + JarEntry entry = (JarEntry) e.nextElement(); + String name = entry.getName(); + if (name.startsWith("META-INF/")) + continue; + int index = name.lastIndexOf('/'); + if (index != -1) + name = name.substring(0, index); + entries.add(name); + } + if (! entries.isEmpty()) + { + result.append(fileName); + // Any line ending will do. + result.append('\n'); + Iterator i = entries.iterator(); + while (i.hasNext()) + { + result.append(i.next()); + result.append('\n'); + } + // Paragraph break. + result.append('\n'); + } + + // Now read pointed-to jars. + Manifest m = jf.getManifest(); + if (m != null) + { + File parent = fileName.getParentFile(); + Attributes attrs = m.getMainAttributes(); + String jars = attrs.getValue(Attributes.Name.CLASS_PATH); + if (jars != null) + { + StringTokenizer st = new StringTokenizer(jars, " "); + while (st.hasMoreTokens()) + { + String name = st.nextToken(); + indexJarFile(result, new File(parent, name), verbose); + } + } + } + + jf.close(); + } + + protected void writeCommandLineEntries(Main parameters, OutputStream os) + throws IOException + { + // This is a pretty lame design. We know the super call will + // only have side effects and won't actually write anything important. + super.writeCommandLineEntries(parameters, os); + + // Now compute our index file and write it. + StringBuffer contents = new StringBuffer(); + indexJarFile(contents, parameters.archiveFile, parameters.verbose); + if (contents.length() != 0) + { + contents.insert(0, (IndexListParser.JAR_INDEX_VERSION_KEY + + "1.0\n\n")); + ByteArrayInputStream in + = new ByteArrayInputStream(contents.toString().getBytes()); + writeFile(false, in, IndexListParser.JAR_INDEX_FILE, parameters.verbose); + } + } + + public void run(Main parameters) throws IOException, OptionException + { + if (! parameters.entries.isEmpty()) + throw new OptionException("can't specify file arguments when using -i"); + if (! parameters.wantManifest) + throw new OptionException("can't specify -M with -i"); + if (parameters.manifestFile != null) + throw new OptionException("can't specify -m with -i"); + super.run(parameters); + } +} diff --git a/tools/gnu/classpath/tools/jar/Main.java b/tools/gnu/classpath/tools/jar/Main.java index bccb32ad8..757c0bc93 100644 --- a/tools/gnu/classpath/tools/jar/Main.java +++ b/tools/gnu/classpath/tools/jar/Main.java @@ -82,6 +82,14 @@ public class Main /** Used only while parsing, holds the first argument for -C. */ String changedDirectory; + void setArchiveFile(String filename) throws OptionException + { + if (archiveFile != null) + throw new OptionException("archive file name already set to " + + archiveFile); + archiveFile = new File(filename); + } + class HandleFile extends FileArgumentCallback { @@ -112,24 +120,36 @@ public class Main this.mode = mode; } + public ModeOption(char shortName, String description, String argName, + Class mode) + { + super(shortName, description, argName); + this.mode = mode; + } + public void parsed(String argument) throws OptionException { if (operationMode != null) throw new OptionException("operation mode already specified"); operationMode = mode; + // We know this is only the case for -i. + if (argument != null) + setArchiveFile(argument); } } private Parser initializeParser() { Parser p = new ClasspathToolParser("jar"); - p.setHeader("Usage: jar -ctxu [OPTIONS] jar-file [-C DIR FILE] FILE..."); + p.setHeader("Usage: jar -ctxui [OPTIONS] jar-file [-C DIR FILE] FILE..."); OptionGroup grp = new OptionGroup("Operation mode"); grp.add(new ModeOption('c', "create a new archive", Creator.class)); grp.add(new ModeOption('x', "extract from archive", Extractor.class)); grp.add(new ModeOption('t', "list archive contents", Lister.class)); grp.add(new ModeOption('u', "update archive", Updater.class)); + // Note that -i works in-place and explicitly requires a file name. + grp.add(new ModeOption('i', "compute archive index", "FILE", Indexer.class)); p.add(grp); grp = new OptionGroup("Operation modifiers"); @@ -137,8 +157,7 @@ public class Main { public void parsed(String argument) throws OptionException { - // FIXME: error if already set. - archiveFile = new File(argument); + setArchiveFile(argument); } }); grp.add(new Option('0', "store only; no ZIP compression") @@ -182,7 +201,6 @@ public class Main } }); p.add(grp); - // -i - need to parse classes return p; } @@ -192,11 +210,11 @@ public class Main { Parser p = initializeParser(); // Special hack to emulate old tar-style commands. - if (args[0].charAt(0) != '-') + if (args.length > 0 && args[0].charAt(0) != '-') args[0] = '-' + args[0]; p.parse(args, new HandleFile()); if (operationMode == null) - throw new OptionException("must specify one of -t, -c, -u, or -x"); + throw new OptionException("must specify one of -t, -c, -u, -x, or -i"); if (changedDirectory != null) throw new OptionException("-C argument requires both directory and filename"); Action t = (Action) operationMode.newInstance(); @@ -213,6 +231,8 @@ public class Main catch (OptionException arg) { System.err.println("jar: " + arg.getMessage()); + // FIXME: this should be pushed into the parser somehow. + System.err.println("Try 'jar --help' for more information"); System.exit(1); } catch (Exception e) diff --git a/tools/gnu/classpath/tools/jar/Updater.java b/tools/gnu/classpath/tools/jar/Updater.java index 161418f4c..b717507a8 100644 --- a/tools/gnu/classpath/tools/jar/Updater.java +++ b/tools/gnu/classpath/tools/jar/Updater.java @@ -38,31 +38,53 @@ package gnu.classpath.tools.jar; +import gnu.classpath.tools.getopt.OptionException; + +import java.io.BufferedOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.util.Enumeration; +import java.util.jar.JarFile; +import java.util.jar.Manifest; import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; public class Updater extends Creator { - public void run(Main parameters) throws IOException + JarFile inputJar; + + protected Manifest createManifest(Main parameters) throws IOException + { + Manifest result = inputJar.getManifest(); + if (result == null) + return super.createManifest(parameters); + if (parameters.manifestFile != null) + result.read(new FileInputStream(parameters.manifestFile)); + return result; + } + + public void run(Main parameters) throws IOException, OptionException { + // Set this early so that createManifest can use it. + inputJar = new JarFile(parameters.archiveFile); + // Write all the new entries to a temporary file. File tmpFile = File.createTempFile("jarcopy", null); - writeCommandLineEntries(parameters, tmpFile); + OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile)); + writeCommandLineEntries(parameters, os); // Now read the old file and copy extra entries to the new file. - ZipFile zip = new ZipFile(parameters.archiveFile); - Enumeration e = zip.entries(); + Enumeration e = inputJar.entries(); while (e.hasMoreElements()) { ZipEntry entry = (ZipEntry) e.nextElement(); if (writtenItems.contains(entry.getName())) continue; - writeFile(entry.isDirectory(), zip.getInputStream(entry), - zip.getName(), parameters.verbose); + writeFile(entry.isDirectory(), inputJar.getInputStream(entry), + entry.getName(), parameters.verbose); } close(); -- cgit v1.2.1