[cp-patches] Patch: RFC: argument parsing and 'jar' utility

Tom Tromey tromey at redhat.com
Mon May 8 18:34:50 UTC 2006


>>>>> "Tom" == Thomas Fitzsimmons <fitzsim at redhat.com> writes:

Tom> I adapted the appletviewer to use the new API.  The API is nice and
Tom> Java-like. Moving to this API allowed me to remove some ugly code and
Tom> handle all appletviewer's needs cleanly.

Tom> I'll hold off committing the appletviewer until you've committed
Tom> this option parser.

Two approvals, and it blocks the appletviewer?
I'm checking it in :-)

Tom> Yeah, I think the test sub-package should just be deleted.

Done.

I'm checking in the appended version of the patch.  I added a new
class which wraps Parser for tools distributed with classpath -- this
class auto-computes the correct version string.  See ClasspathToolParser.


FWIW I've considered adding Option subclasses which know how to parse
their arguments (eg an IntegerOption which parses the argument as an
int), and which use reflection to set fields on a provided object.
This seemed kinda cool but also overkill.  Still, we could do it if
someone thought it was worthwhile.

Tom

2006-05-08  Tom Tromey  <tromey at redhat.com>

	* tools/gnu/classpath/tools/getopt/ClasspathToolParser.java: New file.
	* tools/gnu/classpath/tools/jar/Action.java: New file.
	* tools/gnu/classpath/tools/jar/Creator.java: New file.
	* tools/gnu/classpath/tools/jar/Entry.java: New file.
	* tools/gnu/classpath/tools/jar/Extractor.java: New file.
	* tools/gnu/classpath/tools/jar/Lister.java: New file.
	* tools/gnu/classpath/tools/jar/Main.java: New file.
	* tools/gnu/classpath/tools/jar/Updater.java: New file.
	* tools/gnu/classpath/tools/getopt/Option.java: New file.
	* tools/gnu/classpath/tools/getopt/OptionException.java: New file.
	* tools/gnu/classpath/tools/getopt/OptionGroup.java: New file.
	* tools/gnu/classpath/tools/getopt/Parser.java: New file.
	* tools/gnu/classpath/tools/getopt/FileArgumentCallback.java: New
	file.

Index: tools/gnu/classpath/tools/getopt/ClasspathToolParser.java
===================================================================
RCS file: tools/gnu/classpath/tools/getopt/ClasspathToolParser.java
diff -N tools/gnu/classpath/tools/getopt/ClasspathToolParser.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/getopt/ClasspathToolParser.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,71 @@
+/* ClasspathToolParser.java -- Parser subclass for classpath tools
+   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.getopt;
+
+import gnu.classpath.Configuration;
+
+/**
+ * This is like the Parser class, but is specialized for use by
+ * tools distributed with GNU Classpath.  In particular it automatically
+ * computes the version string using the program's name.
+ */
+public class ClasspathToolParser
+    extends Parser
+{
+  private static String getVersionString(String programName)
+  {
+    return (programName + " (GNU Classpath) "
+            + Configuration.CLASSPATH_VERSION
+            + "\n\n"
+            + "Copyright 2006 Free Software Foundation, Inc.\n"
+            + "This is free software; see the source for copying conditions.  There is NO\n"
+            + "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
+
+  }
+
+  public ClasspathToolParser(String programName)
+  {
+    super(programName, getVersionString(programName));
+  }
+
+  public ClasspathToolParser(String programName, boolean longOnly)
+  {
+    super(programName, getVersionString(programName), longOnly);
+  }
+}
Index: tools/gnu/classpath/tools/getopt/FileArgumentCallback.java
===================================================================
RCS file: tools/gnu/classpath/tools/getopt/FileArgumentCallback.java
diff -N tools/gnu/classpath/tools/getopt/FileArgumentCallback.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/getopt/FileArgumentCallback.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,61 @@
+/* FileArgumentCallback.java - handle non-option command line arguments
+   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.getopt;
+
+/**
+ * This is a callback class which is used when a "file name" is found by the
+ * command-line parser. A file name is any command-line argument which does not
+ * start with a dash and which is not the argument of some preceding option.
+ */
+public abstract class FileArgumentCallback
+{
+  /**
+   * Create a new instance.
+   */
+  protected FileArgumentCallback()
+  {
+  }
+
+  /**
+   * This is called when a file argument is seen.
+   * 
+   * @param fileArgument the file name
+   */
+  public abstract void notifyFile(String fileArgument);
+}
Index: tools/gnu/classpath/tools/getopt/Option.java
===================================================================
RCS file: tools/gnu/classpath/tools/getopt/Option.java
diff -N tools/gnu/classpath/tools/getopt/Option.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/getopt/Option.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,201 @@
+/* Option.java - represent a command-line option
+ 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.getopt;
+
+/**
+ * This is the base class representing an option. An option can have a short
+ * form. This is a single character, like '-x'. An option can have a long form,
+ * like '--verbose'; if the parser is working in "long option only" mode, then a
+ * long flag has a single dash, like '-verbose'. Both a long and a short form
+ * may be specified; it is not valid to have neither. A description is mandatory
+ * for options; this is used to automatically generate '--help' output.
+ */
+public abstract class Option
+{
+  private char shortName;
+
+  private String longName;
+
+  private String description;
+
+  private String argumentName;
+
+  /**
+   * Create a new option with the given short name and description.
+   * 
+   * @param shortName the short name
+   * @param description the description
+   */
+  protected Option(char shortName, String description)
+  {
+    this.shortName = shortName;
+    this.description = description;
+  }
+
+  /**
+   * Create a new option with the given short name and description.
+   * 
+   * @param shortName the short name
+   * @param description the description
+   * @param argumentName the descriptive name of the argument, if this option
+   *          takes an argument; otherwise null
+   */
+  protected Option(char shortName, String description, String argumentName)
+  {
+    this.shortName = shortName;
+    this.description = description;
+    this.argumentName = argumentName;
+  }
+
+  /**
+   * Create a new option with the given long name and description. The long name
+   * should be specified without any leading dashes.
+   * 
+   * @param longName the long name
+   * @param description the description
+   */
+  protected Option(String longName, String description)
+  {
+    this.longName = longName;
+    this.description = description;
+  }
+
+  /**
+   * Create a new option with the given long name and description. The long name
+   * should be specified without any leading dashes.
+   * 
+   * @param longName the long name
+   * @param description the description
+   * @param argumentName the descriptive name of the argument, if this option
+   *          takes an argument; otherwise null
+   */
+  protected Option(String longName, String description, String argumentName)
+  {
+    this.longName = longName;
+    this.description = description;
+    this.argumentName = argumentName;
+  }
+
+  /**
+   * Create a new option with the given short and long names and description.
+   * The long name should be specified without any leading dashes.
+   * 
+   * @param longName the long name
+   * @param shortName the short name
+   * @param description the description
+   */
+  protected Option(String longName, char shortName, String description)
+  {
+    this.shortName = shortName;
+    this.longName = longName;
+    this.description = description;
+  }
+
+  /**
+   * Create a new option with the given short and long names and description.
+   * The long name should be specified without any leading dashes.
+   * 
+   * @param longName the long name
+   * @param shortName the short name
+   * @param description the description
+   * @param argumentName the descriptive name of the argument, if this option
+   *          takes an argument; otherwise null
+   */
+  protected Option(String longName, char shortName, String description,
+                   String argumentName)
+  {
+    this.shortName = shortName;
+    this.longName = longName;
+    this.argumentName = argumentName;
+    this.description = description;
+  }
+
+  /**
+   * Return the short name of the option, or \0 if none.
+   */
+  public char getShortName()
+  {
+    return shortName;
+  }
+
+  /**
+   * Return the long name of the option, or null if none.
+   */
+  public String getLongName()
+  {
+    return longName;
+  }
+
+  /**
+   * Return true if the argument takes an option.
+   */
+  public boolean getTakesArgument()
+  {
+    return argumentName != null;
+  }
+
+  /**
+   * Return the name of the argument. If the option does not take an argument,
+   * returns null.
+   */
+  public String getArgumentName()
+  {
+    return argumentName;
+  }
+
+  /**
+   * Return the description of the option.
+   */
+  public String getDescription()
+  {
+    // FIXME: localization.
+    return description;
+  }
+
+  /**
+   * This is called by the parser when this option is recognized. It may be
+   * called multiple times during a single parse. If this option takes an
+   * argument, the argument will be passed in. Otherwise the argument will be
+   * null.
+   * 
+   * @param argument the argument
+   * @throws OptionException if the option or its argument is somehow invalid
+   */
+  public abstract void parsed(String argument) throws OptionException;
+}
Index: tools/gnu/classpath/tools/getopt/OptionException.java
===================================================================
RCS file: tools/gnu/classpath/tools/getopt/OptionException.java
diff -N tools/gnu/classpath/tools/getopt/OptionException.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/getopt/OptionException.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,52 @@
+/* OptionException.java - when command-line processing fails
+ 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.getopt;
+
+/**
+ * An OptionException is thrown internally when an error is seen when parsing a
+ * command line.
+ */
+public class OptionException
+    extends Exception
+{
+  public OptionException(String message)
+  {
+    super(message);
+  }
+}
Index: tools/gnu/classpath/tools/getopt/OptionGroup.java
===================================================================
RCS file: tools/gnu/classpath/tools/getopt/OptionGroup.java
diff -N tools/gnu/classpath/tools/getopt/OptionGroup.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/getopt/OptionGroup.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,171 @@
+/* OptionGroup.java - a group of related command-line options
+ 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.getopt;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * An option group holds a collection of Options. It also has a name. Option
+ * groups are primarily useful for grouping help output.
+ */
+public class OptionGroup
+{
+  private String name;
+
+  ArrayList options = new ArrayList();
+
+  /**
+   * Create a new nameless option group. This can only be used by Parser.
+   */
+  OptionGroup()
+  {
+  }
+
+  /**
+   * Create a new option group with the indicated name.
+   * 
+   * @param name the name
+   */
+  public OptionGroup(String name)
+  {
+    this.name = name;
+  }
+
+  /**
+   * Add an option to this option group.
+   * 
+   * @param opt the option to add
+   */
+  public void add(Option opt)
+  {
+    options.add(opt);
+  }
+
+  /**
+   * Print the help output for this option group.
+   * 
+   * @param out the stream to which to print
+   */
+  public void printHelp(PrintStream out)
+  {
+    // Compute maximum lengths.
+    int maxArgLen = 0;
+    int maxShortLen = 0;
+    Iterator it = options.iterator();
+    while (it.hasNext())
+      {
+        Option option = (Option) it.next();
+        String argName = option.getArgumentName();
+        // First compute the width required for the short
+        // option. "2" is the initial indentation. In the
+        // GNU style we don't print an argument name for
+        // a short option if there is also a long name for
+        // the option.
+        int thisArgLen = 2;
+        if (option.getShortName() != '\0')
+          {
+            // The name of the option plus some padding.
+            thisArgLen += 4;
+          }
+        maxShortLen = Math.max(thisArgLen, maxShortLen);
+        if (option.getLongName() != null)
+          {
+            // FIXME: different if parsing in 'long option only'
+            // mode.
+            thisArgLen += 2 + option.getLongName().length();
+          }
+        // We only need to add the argument name in once,
+        // and we don't let it contribute to the width of
+        // the short name.
+        if (argName != null)
+          thisArgLen += 1 + argName.length();
+        maxArgLen = Math.max(maxArgLen, thisArgLen);
+      }
+
+    // Print the help.
+    if (name != null)
+      out.println(name + ":");
+    it = options.iterator();
+    while (it.hasNext())
+      {
+        Option option = (Option) it.next();
+        String argName = option.getArgumentName();
+        int column = 0;
+        if (option.getShortName() != '\0')
+          {
+            out.print("  -");
+            out.print(option.getShortName());
+            column += 4;
+            if (option.getLongName() == null)
+              {
+                if (argName != null)
+                  {
+                    out.print(' ');
+                    out.print(argName);
+                    column += 1 + argName.length();
+                  }
+                out.print("  ");
+              }
+            else
+              out.print(", ");
+            column += 2;
+          }
+        for (; column < maxShortLen; ++column)
+          out.print(' ');
+        if (option.getLongName() != null)
+          {
+            out.print("--");
+            out.print(option.getLongName());
+            column += 2 + option.getLongName().length();
+            if (argName != null)
+              {
+                out.print("=" + argName);
+                column += 1 + argName.length();
+              }
+          }
+        for (; column < maxArgLen; ++column)
+          out.print(' ');
+        // FIXME: should have a better heuristic for padding.
+        out.print("    ");
+        out.println(option.getDescription());
+      }
+  }
+}
Index: tools/gnu/classpath/tools/getopt/Parser.java
===================================================================
RCS file: tools/gnu/classpath/tools/getopt/Parser.java
diff -N tools/gnu/classpath/tools/getopt/Parser.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/getopt/Parser.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,314 @@
+/* Parser.java - parse command line options
+ 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.getopt;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * An instance of this class is used to parse command-line options. It does "GNU
+ * style" argument recognition and also automatically handles "--help" and
+ * "--version" processing. It can also be put in "long option only" mode. In
+ * this mode long options are recognized with a single dash (as well as a double
+ * dash) and strings of options like "-abc" are never parsed as a collection of
+ * short options.
+ */
+public class Parser
+{
+  private String programName;
+
+  private String headerText;
+
+  private String footerText;
+
+  private boolean longOnly;
+
+  private ArrayList options = new ArrayList();
+
+  private ArrayList optionGroups = new ArrayList();
+
+  private OptionGroup defaultGroup = new OptionGroup();
+
+  // These are used while parsing.
+  private int currentIndex;
+
+  private String[] args;
+
+  /**
+   * Create a new parser. The program name is used when printing error messages.
+   * The version string is printed verbatim in response to "--version".
+   * 
+   * @param programName the name of the program
+   * @param versionString the program's version information
+   */
+  public Parser(String programName, String versionString)
+  {
+    this(programName, versionString, false);
+  }
+
+  /**
+   * Create a new parser. The program name is used when printing error messages.
+   * The version string is printed verbatim in response to "--version".
+   * 
+   * @param programName the name of the program
+   * @param versionString the program's version information
+   * @param longOnly true if the parser should work in long-option-only mode
+   */
+  public Parser(String programName, final String versionString, boolean longOnly)
+  {
+    this.programName = programName;
+    this.longOnly = longOnly;
+    defaultGroup.add(new Option("help", "print this help, then exit")
+    {
+      public void parsed(String argument) throws OptionException
+      {
+        printHelp(System.out);
+        System.exit(0);
+      }
+    });
+    defaultGroup.add(new Option("version", "print version number, then exit")
+    {
+      public void parsed(String argument) throws OptionException
+      {
+        System.out.println(versionString);
+        System.exit(0);
+      }
+    });
+    add(defaultGroup);
+  }
+
+  /**
+   * Set the header text that is printed by --help.
+   * 
+   * @param headerText the header text
+   */
+  public void setHeader(String headerText)
+  {
+    this.headerText = headerText;
+  }
+
+  /**
+   * Set the footer text that is printed by --help.
+   * 
+   * @param footerText the footer text
+   */
+  public void setFooter(String footerText)
+  {
+    this.footerText = footerText;
+  }
+
+  /**
+   * Add an option to this parser. The option is added to the default option
+   * group; this affects where it is placed in the help output.
+   * 
+   * @param opt the option
+   */
+  public synchronized void add(Option opt)
+  {
+    options.add(opt);
+    defaultGroup.add(opt);
+  }
+
+  /**
+   * Add an option group to this parser. All the options in this group will be
+   * recognized by the parser.
+   * 
+   * @param group the option group
+   */
+  public synchronized void add(OptionGroup group)
+  {
+    options.addAll(group.options);
+    optionGroups.add(group);
+  }
+
+  void printHelp(PrintStream out)
+  {
+    if (headerText != null)
+      {
+        out.println(headerText);
+        out.println();
+      }
+
+    Iterator it = optionGroups.iterator();
+    while (it.hasNext())
+      {
+        OptionGroup group = (OptionGroup) it.next();
+        group.printHelp(out);
+        out.println();
+      }
+
+    if (footerText != null)
+      out.println(footerText);
+  }
+
+  private String getArgument(String request) throws OptionException
+  {
+    ++currentIndex;
+    if (currentIndex >= args.length)
+      throw new OptionException("option '" + request + "' requires an argument");
+    return args[currentIndex];
+  }
+
+  private void handleLongOption(String real, int index) throws OptionException
+  {
+    String option = real.substring(index);
+    String justName = option;
+    int eq = option.indexOf('=');
+    if (eq != - 1)
+      justName = option.substring(0, eq);
+    Option found = null;
+    for (int i = options.size() - 1; i >= 0; --i)
+      {
+        Option opt = (Option) options.get(i);
+        if (justName.equals(opt.getLongName()))
+          {
+            found = opt;
+            break;
+          }
+      }
+    if (found == null)
+      throw new OptionException("unrecognized option '" + real + "'");
+    String argument = null;
+    if (found.getTakesArgument())
+      {
+        if (eq == - 1)
+          argument = getArgument(real);
+        else
+          argument = option.substring(eq + 1);
+      }
+    else if (eq != - 1)
+      {
+        throw new OptionException("option '" + real.substring(0, eq + index)
+                                  + "' doesn't allow an argument");
+      }
+    found.parsed(argument);
+  }
+
+  private void handleShortOption(char option) throws OptionException
+  {
+    Option found = null;
+    for (int i = options.size() - 1; i >= 0; --i)
+      {
+        Option opt = (Option) options.get(i);
+        if (option == opt.getShortName())
+          {
+            found = opt;
+            break;
+          }
+      }
+    if (found == null)
+      throw new OptionException("unrecognized option '-" + option + "'");
+    String argument = null;
+    if (found.getTakesArgument())
+      argument = getArgument("-" + option);
+    found.parsed(argument);
+  }
+
+  private void handleShortOptions(String option) throws OptionException
+  {
+    for (int i = 1; i < option.length(); ++i)
+      {
+        handleShortOption(option.charAt(i));
+      }
+  }
+
+  /**
+   * Parse a command line. Any files which are found will be passed to the file
+   * argument callback. This method will exit on error or when --help or
+   * --version is specified.
+   * 
+   * @param inArgs the command-line arguments
+   * @param files the file argument callback
+   */
+  public synchronized void parse(String[] inArgs, FileArgumentCallback files)
+  {
+    try
+      {
+        args = inArgs;
+        for (currentIndex = 0; currentIndex < args.length; ++currentIndex)
+          {
+            if (args[currentIndex].length() == 0
+                || args[currentIndex].charAt(0) != '-'
+                || "-".equals(args[currentIndex]))
+              {
+                files.notifyFile(args[currentIndex]);
+                continue;
+              }
+            if ("--".equals(args[currentIndex]))
+              break;
+            if (args[currentIndex].charAt(1) == '-')
+              handleLongOption(args[currentIndex], 2);
+            else if (longOnly)
+              handleLongOption(args[currentIndex], 1);
+            else
+              handleShortOptions(args[currentIndex]);
+          }
+        // Add remaining arguments to leftovers.
+        for (++currentIndex; currentIndex < args.length; ++currentIndex)
+          files.notifyFile(args[currentIndex]);
+      }
+    catch (OptionException err)
+      {
+        System.err.println(programName + ": " + err.getMessage());
+        System.err.println(programName + ": Try '" + programName
+                           + " --help' for more information.");
+        System.exit(1);
+      }
+  }
+
+  /**
+   * Parse a command line. Any files which are found will be returned. This
+   * method will exit on error or when --help or --version is specified.
+   * 
+   * @param inArgs the command-line arguments
+   */
+  public String[] parse(String[] inArgs)
+  {
+    final ArrayList fileResult = new ArrayList();
+    parse(inArgs, new FileArgumentCallback()
+    {
+      public void notifyFile(String fileArgument)
+      {
+        fileResult.add(fileArgument);
+      }
+    });
+    return (String[]) fileResult.toArray(new String[0]);
+  }
+}
Index: tools/gnu/classpath/tools/jar/Action.java
===================================================================
RCS file: tools/gnu/classpath/tools/jar/Action.java
diff -N tools/gnu/classpath/tools/jar/Action.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/jar/Action.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,50 @@
+/* Action.java - an action taken by the jar driver
+ 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 java.io.IOException;
+
+public abstract class Action
+{
+  protected Action()
+  {
+  }
+
+  public abstract void run(Main parameters) throws IOException;
+}
Index: tools/gnu/classpath/tools/jar/Creator.java
===================================================================
RCS file: tools/gnu/classpath/tools/jar/Creator.java
diff -N tools/gnu/classpath/tools/jar/Creator.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/jar/Creator.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,192 @@
+/* Creator.java - create a new jar file
+ 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 java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+public class Creator
+    extends Action
+{
+  ZipOutputStream outputStream;
+
+  private long copyFile(CRC32 crc, InputStream is, OutputStream output)
+      throws IOException
+  {
+    byte[] buffer = new byte[1024];
+    long size = 0;
+    while (true)
+      {
+        int len = is.read(buffer);
+        if (len == - 1)
+          break;
+        size += len;
+        output.write(buffer, 0, len);
+        crc.update(buffer, 0, len);
+      }
+    output.close();
+    return size;
+  }
+
+  protected void writeFile(boolean isDirectory, InputStream inputFile,
+                           String filename, boolean verbose) throws IOException
+  {
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    CRC32 crc = new CRC32();
+    long size;
+    if (isDirectory)
+      {
+        size = 0;
+      }
+    else
+      {
+        size = copyFile(crc, inputFile, out);
+      }
+
+    ZipEntry entry = new ZipEntry(filename);
+    entry.setCrc(crc.getValue());
+    entry.setSize(size);
+
+    outputStream.putNextEntry(entry);
+    out.writeTo(outputStream);
+    outputStream.closeEntry();
+
+    if (verbose)
+      {
+        long csize = entry.getCompressedSize();
+        long perc;
+        if (size == 0)
+          perc = 0;
+        else
+          perc = 100 - (100 * csize) / size;
+        System.out.println("adding: " + filename + " (in=" + size + ") (out="
+                           + entry.getSize() + ") (stored " + perc + "%)");
+      }
+  }
+
+  protected void writeFile(File file, String filename, boolean verbose)
+      throws IOException
+  {
+    boolean isDirectory = file.isDirectory();
+    InputStream inputStream = null;
+    if (isDirectory)
+      {
+        if (filename.charAt(filename.length() - 1) != '/')
+          filename += '/';
+      }
+    else
+      inputStream = new FileInputStream(file);
+    writeFile(isDirectory, inputStream, filename, verbose);
+  }
+
+  private void addEntries(ArrayList result, Entry entry)
+  {
+    if (entry.file.isDirectory())
+      {
+        String name = entry.name;
+        if (name.charAt(name.length() - 1) != '/')
+          {
+            name += '/';
+            entry = new Entry(entry.file, name);
+          }
+        result.add(entry);
+        String[] files = entry.file.list();
+        for (int i = 0; i < files.length; ++i)
+          addEntries(result, new Entry(new File(entry.file, files[i]),
+                                       entry.name + '/' + files[i]));
+      }
+    else
+      result.add(entry);
+  }
+
+  private ArrayList getAllEntries(Main parameters)
+  {
+    Iterator it = parameters.entries.iterator();
+    ArrayList allEntries = new ArrayList();
+    while (it.hasNext())
+      {
+        Entry entry = (Entry) it.next();
+        addEntries(allEntries, entry);
+      }
+    return allEntries;
+  }
+
+  protected ArrayList writeCommandLineEntries(Main parameters, File zipFile)
+      throws IOException
+  {
+    outputStream = new ZipOutputStream(
+                                       new BufferedOutputStream(
+                                                                new FileOutputStream(
+                                                                                     zipFile)));
+    outputStream.setMethod(parameters.storageMode);
+    ArrayList allEntries = getAllEntries(parameters);
+    Iterator it = parameters.entries.iterator();
+    while (it.hasNext())
+      {
+        Entry entry = (Entry) it.next();
+        writeFile(entry.file, entry.name, parameters.verbose);
+      }
+    return allEntries;
+  }
+
+  protected void close() throws IOException
+  {
+    // FIXME: handle manifest options here.
+    // FIXME: handle index file here ...?
+    outputStream.finish();
+    outputStream.close();
+  }
+
+  public void run(Main parameters) throws IOException
+  {
+    writeCommandLineEntries(parameters, parameters.archiveFile);
+    close();
+  }
+}
Index: tools/gnu/classpath/tools/jar/Entry.java
===================================================================
RCS file: tools/gnu/classpath/tools/jar/Entry.java
diff -N tools/gnu/classpath/tools/jar/Entry.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/jar/Entry.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,60 @@
+/* Entry.java - represent a single file to write to a 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 java.io.File;
+
+public class Entry
+{
+  public File file;
+
+  public String name;
+
+  public Entry(File file, String name)
+  {
+    this.file = file;
+    this.name = name;
+  }
+
+  public Entry(File file)
+  {
+    this.file = file;
+    this.name = file.toString();
+  }
+}
Index: tools/gnu/classpath/tools/jar/Extractor.java
===================================================================
RCS file: tools/gnu/classpath/tools/jar/Extractor.java
diff -N tools/gnu/classpath/tools/jar/Extractor.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/jar/Extractor.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,100 @@
+/* Extractor.java - action to extract from a jar file
+ 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 java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class Extractor
+    extends Action
+{
+  private void copyFile(InputStream input, File output) throws IOException
+  {
+    FileOutputStream os = new FileOutputStream(output);
+    byte[] buffer = new byte[1024];
+    while (true)
+      {
+        int len = input.read(buffer);
+        if (len == - 1)
+          break;
+        os.write(buffer, 0, len);
+      }
+    os.close();
+  }
+
+  public void run(Main parameters) throws IOException
+  {
+    ZipFile zip = new ZipFile(parameters.archiveFile);
+    Enumeration e = zip.entries();
+    while (e.hasMoreElements())
+      {
+        ZipEntry entry = (ZipEntry) e.nextElement();
+        File file = new File(entry.getName());
+        if (entry.isDirectory())
+          {
+            if (file.mkdirs())
+              {
+                if (parameters.verbose)
+                  System.out.println("  created: " + file);
+              }
+            continue;
+          }
+
+        File parent = file.getParentFile();
+        if (parent != null)
+          parent.mkdirs();
+
+        InputStream input = zip.getInputStream(entry);
+        copyFile(input, file);
+        input.close();
+
+        if (parameters.verbose)
+          {
+            String leader = (entry.getMethod() == ZipEntry.STORED ? " extracted"
+                                                                 : "  inflated");
+            System.out.println(leader + ": " + file);
+          }
+      }
+  }
+}
Index: tools/gnu/classpath/tools/jar/Lister.java
===================================================================
RCS file: tools/gnu/classpath/tools/jar/Lister.java
diff -N tools/gnu/classpath/tools/jar/Lister.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/jar/Lister.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,84 @@
+/* Lister.java - action to list contents of a jar file
+ 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 java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class Lister
+    extends Action
+{
+  private void listJar(File jarFile, boolean verbose) throws IOException
+  {
+    ZipFile zipFile = new ZipFile(jarFile);
+    Enumeration i = zipFile.entries();
+    MessageFormat format = null;
+    if (verbose)
+      format = new MessageFormat(" {0,date,E M dd HH:mm:ss z yyyy} {1}");
+    while (i.hasMoreElements())
+      {
+        ZipEntry entry = (ZipEntry) i.nextElement();
+        if (verbose)
+          {
+            // No easy way to right-justify the size using
+            // MessageFormat -- how odd.
+            long size = entry.getSize();
+            String s = "     " + size;
+            int index = Math.min(s.length() - 5, 5);
+            System.out.print(s.substring(index));
+            Object[] values = new Object[] { new Date(entry.getTime()),
+                                            entry.getName() };
+            System.out.println(format.format(values));
+          }
+        else
+          System.out.println(entry.getName());
+      }
+    zipFile.close();
+  }
+
+  public void run(Main parameters) throws IOException
+  {
+    listJar(parameters.archiveFile, parameters.verbose);
+  }
+}
Index: tools/gnu/classpath/tools/jar/Main.java
===================================================================
RCS file: tools/gnu/classpath/tools/jar/Main.java
diff -N tools/gnu/classpath/tools/jar/Main.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/jar/Main.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,225 @@
+/* Main.java - jar program main()
+ 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.ClasspathToolParser;
+import gnu.classpath.tools.getopt.FileArgumentCallback;
+import gnu.classpath.tools.getopt.Option;
+import gnu.classpath.tools.getopt.OptionException;
+import gnu.classpath.tools.getopt.OptionGroup;
+import gnu.classpath.tools.getopt.Parser;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.zip.ZipOutputStream;
+
+public class Main
+{
+  // The mode of operation. This is the class representing
+  // the action; we make a new instance before using it. It
+  // must be a subclass of Action. 'null' means the mode
+  // has not yet been set.
+  Class operationMode;
+
+  // The archive file name.
+  File archiveFile;
+
+  // The zip storage mode.
+  int storageMode = ZipOutputStream.DEFLATED;
+
+  // True if we should read file names from stdin.
+  boolean readNamesFromStdin = false;
+
+  // True for verbose mode.
+  boolean verbose = false;
+
+  // True if we want a manifest file.
+  boolean wantManifest = true;
+
+  // Name of manifest file to use.
+  File manifestFile;
+
+  // A list of Entry objects, each describing a file to write.
+  ArrayList entries = new ArrayList();
+
+  // Used only while parsing.
+  String changedDirectory;
+
+  class HandleFile
+      extends FileArgumentCallback
+  {
+    public void notifyFile(String fileArgument)
+    {
+      Entry entry;
+      if (changedDirectory != null)
+        {
+          entry = new Entry(new File(changedDirectory, fileArgument),
+                            fileArgument);
+          changedDirectory = null;
+        }
+      else
+        entry = new Entry(new File(fileArgument));
+      entries.add(entry);
+    }
+  }
+
+  // An option that knows how to set the operation mode.
+  private class ModeOption
+      extends Option
+  {
+    private Class mode;
+
+    public ModeOption(char shortName, String description, Class mode)
+    {
+      super(shortName, description);
+      this.mode = mode;
+    }
+
+    public void parsed(String argument) throws OptionException
+    {
+      if (operationMode != null)
+        throw new OptionException("operation mode already specified");
+      operationMode = mode;
+    }
+  }
+
+  private Parser initializeParser()
+  {
+    Parser p = new ClasspathToolParser("jar");
+    p.setHeader("Usage: jar -ctxu [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));
+    p.add(grp);
+
+    grp = new OptionGroup("Operation modifiers");
+    grp.add(new Option('f', "specify archive file name", "FILE")
+    {
+      public void parsed(String argument) throws OptionException
+      {
+        // FIXME: error if already set.
+        archiveFile = new File(argument);
+      }
+    });
+    grp.add(new Option('0', "store only; no ZIP compression")
+    {
+      public void parsed(String argument) throws OptionException
+      {
+        storageMode = ZipOutputStream.STORED;
+      }
+    });
+    grp.add(new Option('v', "verbose operation")
+    {
+      public void parsed(String argument) throws OptionException
+      {
+        verbose = true;
+      }
+    });
+    grp.add(new Option('M', "do not create a manifest file")
+    {
+      public void parsed(String argument) throws OptionException
+      {
+        wantManifest = false;
+      }
+    });
+    grp.add(new Option('m', "specify manifest file", "FILE")
+    {
+      public void parsed(String argument) throws OptionException
+      {
+        manifestFile = new File(argument);
+      }
+    });
+    // -@
+    p.add(grp);
+
+    grp = new OptionGroup("File name selection");
+    grp.add(new Option('C', "change to directory before the next file",
+                       "DIR FILE")
+    {
+      public void parsed(String argument) throws OptionException
+      {
+        changedDirectory = argument;
+      }
+    });
+    p.add(grp);
+    // -i - need to parse classes
+
+    return p;
+  }
+
+  private void run(String[] args) throws OptionException,
+      InstantiationException, IllegalAccessException, IOException
+  {
+    Parser p = initializeParser();
+    // Special hack to emulate old tar-style commands.
+    if (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");
+    if (changedDirectory != null)
+      throw new OptionException("-C argument requires both directory and filename");
+    Action t = (Action) operationMode.newInstance();
+    t.run(this);
+  }
+
+  public static void main(String[] args)
+  {
+    Main jarprogram = new Main();
+    try
+      {
+        jarprogram.run(args);
+      }
+    catch (OptionException arg)
+      {
+        System.err.println("jar: " + arg.getMessage());
+        System.exit(1);
+      }
+    catch (Exception e)
+      {
+        System.err.println("jar: internal error:");
+        e.printStackTrace(System.err);
+        System.exit(1);
+      }
+  }
+}
Index: tools/gnu/classpath/tools/jar/Updater.java
===================================================================
RCS file: tools/gnu/classpath/tools/jar/Updater.java
diff -N tools/gnu/classpath/tools/jar/Updater.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tools/gnu/classpath/tools/jar/Updater.java	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,81 @@
+/* Updater.java - action to update a jar file
+ 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 java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class Updater
+    extends Creator
+{
+  public void run(Main parameters) throws IOException
+  {
+    // Write all the new entries to a temporary file.
+    File tmpFile = File.createTempFile("jarcopy", null);
+    ArrayList newEntries = writeCommandLineEntries(parameters, tmpFile);
+    HashSet set = new HashSet();
+    Iterator it = newEntries.iterator();
+    while (it.hasNext())
+      {
+        Entry entry = (Entry) it.next();
+        set.add(entry.name);
+      }
+
+    // Now read the old file and copy extra entries to the new file.
+    ZipFile zip = new ZipFile(parameters.archiveFile);
+    Enumeration e = zip.entries();
+    while (e.hasMoreElements())
+      {
+        ZipEntry entry = (ZipEntry) e.nextElement();
+        if (set.contains(entry.getName()))
+          continue;
+        writeFile(entry.isDirectory(), zip.getInputStream(entry),
+                  zip.getName(), parameters.verbose);
+      }
+
+    close();
+    tmpFile.renameTo(parameters.archiveFile);
+  }
+}



More information about the Classpath-patches mailing list