I'm checking this in.

This adds 'jar -i' support and also fixes some problems here and there
in jar.  It also cleans up a couple oddities in related areas that I
noticed while working.

This is PR 27514.

Tom

2006-05-14  Tom Tromey  <[EMAIL PROTECTED]>

        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.

Index: gnu/java/net/IndexListParser.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/net/IndexListParser.java,v
retrieving revision 1.1
diff -u -r1.1 IndexListParser.java
--- gnu/java/net/IndexListParser.java   8 May 2006 21:30:06 -0000       1.1
+++ gnu/java/net/IndexListParser.java   14 May 2006 20:29:21 -0000
@@ -63,8 +63,9 @@
  */
 public class IndexListParser
 {
-  String filePath = "META-INF/INDEX.LIST";
-  String versInfo = "JarIndex-Version: ";
+  public static final String JAR_INDEX_FILE = "META-INF/INDEX.LIST";
+  public static final String JAR_INDEX_VERSION_KEY = "JarIndex-Version: ";
+
   double versionNumber;
   ArrayList headers = new ArrayList();
   
@@ -80,16 +81,16 @@
     try
     {
     // Parse INDEX.LIST if it exists
-    if (jarfile.getEntry(filePath) != null)
+    if (jarfile.getEntry(JAR_INDEX_FILE) != null)
       {
         BufferedReader br = new BufferedReader(new InputStreamReader(new 
URL(baseJarURL,
-                                                                             
filePath).openStream()));
+                                                                             
JAR_INDEX_FILE).openStream()));
         
         // Must start with version info
         String line = br.readLine();
-        if (!line.startsWith(versInfo))
+        if (!line.startsWith(JAR_INDEX_VERSION_KEY))
           return;
-        versionNumber = 
Double.parseDouble(line.substring(versInfo.length()).trim());
+        versionNumber = 
Double.parseDouble(line.substring(JAR_INDEX_VERSION_KEY.length()).trim());
         
         // Blank line must be next
         line = br.readLine();
@@ -134,7 +135,7 @@
    */
   public String getVersionInfo()
   {
-    return versInfo + getVersionNumber();
+    return JAR_INDEX_VERSION_KEY + getVersionNumber();
   }
   
   /**
Index: java/util/jar/JarOutputStream.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/util/jar/JarOutputStream.java,v
retrieving revision 1.7
diff -u -r1.7 JarOutputStream.java
--- java/util/jar/JarOutputStream.java  2 Jul 2005 20:32:44 -0000       1.7
+++ java/util/jar/JarOutputStream.java  14 May 2006 20:29:24 -0000
@@ -101,7 +101,7 @@
 
   /**
    * Prepares the JarOutputStream for writing the next entry. 
-   * This implementation just calls <code>super.putNextEntre()</code>.
+   * This implementation just calls <code>super.putNextEntry()</code>.
    *
    * @param entry The information for the next entry
    * @exception IOException when some unexpected I/O exception occurred
Index: java/util/jar/Manifest.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/util/jar/Manifest.java,v
retrieving revision 1.12
diff -u -r1.12 Manifest.java
--- java/util/jar/Manifest.java 25 Mar 2006 11:51:25 -0000      1.12
+++ java/util/jar/Manifest.java 14 May 2006 20:29:25 -0000
@@ -1,4 +1,4 @@
-/* Manifest.java -- Reads, writes and manipulaties jar manifest files
+/* Manifest.java -- Reads, writes and manipulates jar manifest files
    Copyright (C) 2000, 2004 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
@@ -152,7 +152,7 @@
   }
 
   /**
-   * Read and merge a <code>Mainfest</code> from the designated input stream.
+   * Read and merge a <code>Manifest</code> from the designated input stream.
    * 
    * @param in the input stream to read from.
    * @throws IOException if an I/O related exception occurs during the process.
Index: tools/gnu/classpath/tools/jar/Action.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/tools/gnu/classpath/tools/jar/Action.java,v
retrieving revision 1.1
diff -u -r1.1 Action.java
--- tools/gnu/classpath/tools/jar/Action.java   8 May 2006 18:38:20 -0000       
1.1
+++ tools/gnu/classpath/tools/jar/Action.java   14 May 2006 20:29:26 -0000
@@ -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 void run(Main parameters) throws IOException;
+  public abstract void run(Main parameters)
+    throws IOException, OptionException;
 }
Index: tools/gnu/classpath/tools/jar/Creator.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/tools/gnu/classpath/tools/jar/Creator.java,v
retrieving revision 1.2
diff -u -r1.2 Creator.java
--- tools/gnu/classpath/tools/jar/Creator.java  8 May 2006 23:51:49 -0000       
1.2
+++ tools/gnu/classpath/tools/jar/Creator.java  14 May 2006 20:29:26 -0000
@@ -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.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 @@
     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 @@
       }
   }
 
-  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);
+        InputStream contents = new FileInputStream(parameters.manifestFile);
+        return new Manifest(contents);
       }
-    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"));
-      }
-    // 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();
   }
 }
Index: tools/gnu/classpath/tools/jar/Main.java
===================================================================
RCS file: /cvsroot/classpath/classpath/tools/gnu/classpath/tools/jar/Main.java,v
retrieving revision 1.2
diff -u -r1.2 Main.java
--- tools/gnu/classpath/tools/jar/Main.java     8 May 2006 23:51:49 -0000       
1.2
+++ tools/gnu/classpath/tools/jar/Main.java     14 May 2006 20:29:26 -0000
@@ -82,6 +82,14 @@
   /** 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 @@
       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 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 @@
       }
     });
     p.add(grp);
-    // -i - need to parse classes
 
     return p;
   }
@@ -192,11 +210,11 @@
   {
     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 @@
     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)
Index: tools/gnu/classpath/tools/jar/Updater.java
===================================================================
RCS file: 
/cvsroot/classpath/classpath/tools/gnu/classpath/tools/jar/Updater.java,v
retrieving revision 1.2
diff -u -r1.2 Updater.java
--- tools/gnu/classpath/tools/jar/Updater.java  8 May 2006 23:51:49 -0000       
1.2
+++ tools/gnu/classpath/tools/jar/Updater.java  14 May 2006 20:29:26 -0000
@@ -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();
Index: tools/gnu/classpath/tools/jar/Indexer.java
===================================================================
RCS file: tools/gnu/classpath/tools/jar/Indexer.java
diff -N tools/gnu/classpath/tools/jar/Indexer.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/jar/Indexer.java  1 Jan 1970 00:00:00 -0000
@@ -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);
+  }
+}

Reply via email to