add interalised commons cli classes (to prevent making "real" impls global classes)
remove commons io dependency
various fixes/tweeks to core functionality



git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@806255 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/common/runtime/sigil.properties b/sigil/common/runtime/sigil.properties
index af88a95..9f3ad7b 100644
--- a/sigil/common/runtime/sigil.properties
+++ b/sigil/common/runtime/sigil.properties
@@ -5,19 +5,15 @@
 	org.apache.felix.sigil.common.runtime, \
 
 -contents: \
-  org.apache.felix.sigil.common.runtime.*, \
-  org.apache.commons.cli.*, \
-  org.apache.commons.io.*, \
-  org.osgi.framework.launch,\
+	org.apache.felix.sigil.common.runtime.*, \
+	org.osgi.framework.launch, \
 
 -exports: \
 	org.apache.felix.sigil.common.runtime, \
 
 -imports: \
-	org.apache.commons.cli;version=1.2.0;resolve=compile, \
-	org.apache.commons.io.input;version=1.4.0;resolve=compile, \
 	org.osgi.framework;resolve=compile, \
-	org.osgi.framework.launch;version=1.0.0;resolve=compile, \
+	org.osgi.framework.launch;resolve=compile;version=1.0.0, \
 
 header;Main-Class: org.apache.felix.sigil.common.runtime.Main
 
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Client.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Client.java
index dff2c28..4b6c955 100644
--- a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Client.java
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Client.java
@@ -38,6 +38,9 @@
 import org.apache.felix.sigil.common.runtime.io.UpdateAction.Update;
 import org.osgi.framework.BundleException;
 
+import static org.apache.felix.sigil.common.runtime.Runtime.PORT_PROPERTY;
+import static org.apache.felix.sigil.common.runtime.Runtime.ADDRESS_PROPERTY;
+
 
 /**
  * @author dave
@@ -45,9 +48,6 @@
  */
 public class Client
 {
-    public static final String PORT_PROPERTY = "port";
-    public static final String ADDRESS_PROPERTY = "address";
-    
     private Socket socket;
     private DataInputStream in;
     private DataOutputStream out;
@@ -57,33 +57,16 @@
     {
     }
     
-    public static void main(String[] args) throws IOException, BundleException {
-        Client cl = new Client();
-        Properties props = new Properties();
-        props.put( ADDRESS_PROPERTY, "localhost" );
-        props.put( PORT_PROPERTY, "9090" );
-        cl.connect(  props );
-        System.out.println( cl.status() );
-        long id = cl.install( "file:/Users/dave/.m2/repository/org/apache/felix/org.apache.felix.log/1.1.0-SNAPSHOT/org.apache.felix.log-1.1.0-SNAPSHOT.jar" );
-        System.out.println( cl.status() );
-        cl.start( id );
-        System.out.println( cl.status() );
-        
-        id = cl.install( "file:/Users/dave/.m2/repository/org/apache/felix/org.apache.felix.shell/1.3.0-SNAPSHOT/org.apache.felix.shell-1.3.0-SNAPSHOT.jar" );
-        cl.start( id );
-        
-        id = cl.install( "file:/Users/dave/.m2/repository/org/apache/felix/org.apache.felix.shell.tui/1.3.0-SNAPSHOT/org.apache.felix.shell.tui-1.3.0-SNAPSHOT.jar" );
-        cl.start( id );
-        
-        System.out.println( cl.status() );
-    }
-
-
     public void connect(Properties props) throws IOException
     {
         String v = props.getProperty( ADDRESS_PROPERTY );
         InetAddress address = v == null ? null : InetAddress.getByName( v );
         int port = Integer.parseInt( props.getProperty( PORT_PROPERTY, "0" ) );
+        
+        if ( port < 1 ) {
+            throw new IOException( "Missing or invalid port" );
+        }
+        
         InetSocketAddress endpoint = new InetSocketAddress(address, port);
         
         socket = new Socket();
@@ -92,42 +75,53 @@
         Main.log( "Connected to " + endpoint );
         
         in = new DataInputStream( socket.getInputStream() );
-        out = new DataOutputStream( socket.getOutputStream() );
+        out = new DataOutputStream( socket.getOutputStream() );        
     }
 
-
-    public void close() throws IOException
+    public boolean isConnected() {
+        return socket != null;
+    }
+    
+    public void disconnect() throws IOException
     {
         socket.close();
+        socket = null;
+        in = null;
+        out = null;
     }
 
 
     public long install( String url ) throws IOException, BundleException
     {
+        if ( socket == null ) throw new IllegalStateException( "Not connected" );
         return new InstallAction( in, out ).client( url );
     }
 
 
     public void start( long bundle ) throws IOException, BundleException
     {
+        if ( socket == null ) throw new IllegalStateException( "Not connected" );
         new StartAction( in, out ).client( bundle );
     }
 
 
     public void stop( long bundle ) throws IOException, BundleException
     {
+        if ( socket == null ) throw new IllegalStateException( "Not connected" );
         new StopAction( in, out ).client( bundle );
     }
 
 
     public void uninstall( long bundle ) throws IOException, BundleException
     {
+        if ( socket == null ) throw new IllegalStateException( "Not connected" );
         new UninstallAction( in, out ).client( bundle );
     }
 
 
     public void update( long bundle ) throws IOException, BundleException
     {
+        if ( socket == null ) throw new IllegalStateException( "Not connected" );
         Update update = new UpdateAction.Update(bundle, null);
         new UpdateAction( in, out ).client(update);
     }
@@ -135,6 +129,7 @@
 
     public void update( long bundle, String url ) throws IOException, BundleException
     {
+        if ( socket == null ) throw new IllegalStateException( "Not connected" );
         Update update = new UpdateAction.Update(bundle, url);
         new UpdateAction( in, out ).client(update);
     }
@@ -142,6 +137,7 @@
 
     public Map<Long, String> status() throws IOException, BundleException
     {
+        if ( socket == null ) throw new IllegalStateException( "Not connected" );
         return new StatusAction( in, out ).client();
     }
 }
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Main.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Main.java
index 237d400..3b7cf72 100644
--- a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Main.java
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Main.java
@@ -20,21 +20,24 @@
 package org.apache.felix.sigil.common.runtime;
 
 
+import static org.apache.felix.sigil.common.runtime.Runtime.ADDRESS_PROPERTY;
+import static org.apache.felix.sigil.common.runtime.Runtime.PORT_PROPERTY;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.net.InetAddress;
 import java.net.URL;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Properties;
 
-import org.apache.commons.cli.CommandLine;
-import org.apache.commons.cli.HelpFormatter;
-import org.apache.commons.cli.Options;
-import org.apache.commons.cli.ParseException;
-import org.apache.commons.cli.Parser;
-import org.apache.commons.cli.PosixParser;
+import org.apache.felix.sigil.common.runtime.cli.CommandLine;
+import org.apache.felix.sigil.common.runtime.cli.HelpFormatter;
+import org.apache.felix.sigil.common.runtime.cli.Options;
+import org.apache.felix.sigil.common.runtime.cli.ParseException;
+import org.apache.felix.sigil.common.runtime.cli.Parser;
+import org.apache.felix.sigil.common.runtime.cli.PosixParser;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.launch.Framework;
 import org.osgi.framework.launch.FrameworkFactory;
@@ -54,6 +57,7 @@
         options.addOption( "p", "port", true, "Port to launch server on (0 implies auto allocate) [default 0]" );
         options.addOption( "a", "address", true, "Address to bind server to [default all]" );
         options.addOption( "c", "clean", false, "Clean bundle cache directory on init" );
+        options.addOption( "s", "startLevel", true, "Start level for framework" );
     }
 
 
@@ -76,6 +80,7 @@
 
                 framework = factory.newFramework( config );
                 framework.init();
+                framework.start();
 
                 Server server = launch( cl );
 
@@ -160,7 +165,11 @@
     {
         HashMap<String, String> config = new HashMap<String, String>();
         if ( cl.hasOption( 'c' ))
-        config.put(  "org.osgi.framework.storage.clean", "onFirstInit" );
+            config.put(  "org.osgi.framework.storage.clean", "onFirstInit" );
+        
+        if ( cl.hasOption( 's' ) )
+            config.put( "org.osgi.framework.startlevel.beginning", cl.getOptionValue( 's' ) );
+        
         return config;
     }
 
@@ -168,10 +177,10 @@
     private static Server launch( CommandLine line ) throws IOException
     {
         Server server = new Server( framework );
-        String v = line.getOptionValue( 'a' );
-        InetAddress addr = v == null ? null : InetAddress.getByName( v );
-        int port = Integer.parseInt( line.getOptionValue( 'p', "0" ) );
-        server.start( addr, port );
+        Properties props = new Properties();
+        props.put( ADDRESS_PROPERTY, line.getOptionValue( 'a' ) );
+        props.put( PORT_PROPERTY, line.getOptionValue( 'p' ) );
+        server.start( props );
         return server;
     }
 
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Runtime.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Runtime.java
new file mode 100644
index 0000000..87f3bfb
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Runtime.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime;
+
+public class Runtime
+{
+    public static final String PORT_PROPERTY = "port";
+    public static final String ADDRESS_PROPERTY = "address";
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Server.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Server.java
index 509ba38..36cef3a 100644
--- a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Server.java
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/Server.java
@@ -28,6 +28,7 @@
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.util.Properties;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -41,6 +42,8 @@
 import org.apache.felix.sigil.common.runtime.io.UpdateAction;
 import org.osgi.framework.launch.Framework;
 
+import static org.apache.felix.sigil.common.runtime.Runtime.ADDRESS_PROPERTY;
+import static org.apache.felix.sigil.common.runtime.Runtime.PORT_PROPERTY;
 import static org.apache.felix.sigil.common.runtime.io.Constants.*;
 
 
@@ -63,11 +66,15 @@
     }
 
 
-    public void start( InetAddress inetAddress, int port ) throws IOException
+    public void start( Properties props ) throws IOException
     {
         final ServerSocket socket = new ServerSocket();
-        InetSocketAddress socketAddress = new InetSocketAddress(inetAddress, port);
         
+        String v = props.getProperty( ADDRESS_PROPERTY );
+        InetAddress address = v == null ? null : InetAddress.getByName( v );
+        int port = Integer.parseInt( props.getProperty( PORT_PROPERTY, "0" ) );
+        
+        InetSocketAddress socketAddress = new InetSocketAddress(address, port);        
         socket.bind( socketAddress );
 
         Main.log( "Started server listening on " + socket.getLocalSocketAddress() + ":" + socket.getLocalPort() );
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/AlreadySelectedException.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/AlreadySelectedException.java
new file mode 100644
index 0000000..32483c3
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/AlreadySelectedException.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * Thrown when more than one option in an option group
+ * has been provided.
+ *
+ * @author John Keyes ( john at integralsource.com )
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class AlreadySelectedException extends ParseException
+{
+    /** The option group selected. */
+    private OptionGroup group;
+
+    /** The option that triggered the exception. */
+    private Option option;
+
+    /**
+     * Construct a new <code>AlreadySelectedException</code>
+     * with the specified detail message.
+     *
+     * @param message the detail message
+     */
+    public AlreadySelectedException(String message)
+    {
+        super(message);
+    }
+
+    /**
+     * Construct a new <code>AlreadySelectedException</code>
+     * for the specified option group.
+     *
+     * @param group  the option group already selected
+     * @param option the option that triggered the exception
+     * @since 1.2
+     */
+    public AlreadySelectedException(OptionGroup group, Option option)
+    {
+        this("The option '" + option.getKey() + "' was specified but an option from this group "
+                + "has already been selected: '" + group.getSelected() + "'");
+        this.group = group;
+        this.option = option;
+    }
+
+    /**
+     * Returns the option group where another option has been selected.
+     *
+     * @return the related option group
+     * @since 1.2
+     */
+    public OptionGroup getOptionGroup()
+    {
+        return group;
+    }
+
+    /**
+     * Returns the option that was added to the group and triggered the exception.
+     *
+     * @return the related option
+     * @since 1.2
+     */
+    public Option getOption()
+    {
+        return option;
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/BasicParser.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/BasicParser.java
new file mode 100644
index 0000000..2737285
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/BasicParser.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * The class BasicParser provides a very simple implementation of
+ * the {@link Parser#flatten(Options,String[],boolean) flatten} method.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class BasicParser extends Parser
+{
+    /**
+     * <p>A simple implementation of {@link Parser}'s abstract
+     * {@link Parser#flatten(Options, String[], boolean) flatten} method.</p>
+     *
+     * <p><b>Note:</b> <code>options</code> and <code>stopAtNonOption</code>
+     * are not used in this <code>flatten</code> method.</p>
+     *
+     * @param options The command line {@link Options}
+     * @param arguments The command line arguments to be parsed
+     * @param stopAtNonOption Specifies whether to stop flattening
+     * when an non option is found.
+     * @return The <code>arguments</code> String array.
+     */
+    protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption)
+    {
+        // just echo the arguments
+        return arguments;
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/CommandLine.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/CommandLine.java
new file mode 100644
index 0000000..02d9dbc
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/CommandLine.java
@@ -0,0 +1,385 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Represents list of arguments parsed against a {@link Options} descriptor.
+ *
+ * <p>It allows querying of a boolean {@link #hasOption(String opt)},
+ * in addition to retrieving the {@link #getOptionValue(String opt)}
+ * for options requiring arguments.</p>
+ *
+ * <p>Additionally, any left-over or unrecognized arguments,
+ * are available for further processing.</p>
+ *
+ * @author bob mcwhirter (bob @ werken.com)
+ * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 735247 $, $Date: 2009-01-17 00:23:35 -0800 (Sat, 17 Jan 2009) $
+ */
+public class CommandLine implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** the unrecognised options/arguments */
+    private List args = new LinkedList();
+
+    /** the processed options */
+    private List options = new ArrayList();
+
+    /**
+     * Creates a command line.
+     */
+    CommandLine()
+    {
+        // nothing to do
+    }
+
+    /** 
+     * Query to see if an option has been set.
+     *
+     * @param opt Short name of the option
+     * @return true if set, false if not
+     */
+    public boolean hasOption(String opt)
+    {
+        return options.contains(resolveOption(opt));
+    }
+
+    /** 
+     * Query to see if an option has been set.
+     *
+     * @param opt character name of the option
+     * @return true if set, false if not
+     */
+    public boolean hasOption(char opt)
+    {
+        return hasOption(String.valueOf(opt));
+    }
+
+    /**
+     * Return the <code>Object</code> type of this <code>Option</code>.
+     *
+     * @param opt the name of the option
+     * @return the type of this <code>Option</code>
+     * @deprecated due to System.err message. Instead use getParsedOptionValue(String)
+     */
+    public Object getOptionObject(String opt)
+    {
+        try {
+            return getParsedOptionValue(opt);
+        } catch(ParseException pe) {
+            System.err.println("Exception found converting " + opt + " to desired type: " + 
+                pe.getMessage() );
+            return null;
+        }
+    }
+
+    /**
+     * Return a version of this <code>Option</code> converted to a particular type. 
+     *
+     * @param opt the name of the option
+     * @return the value parsed into a particluar object
+     * @throws ParseException if there are problems turning the option value into the desired type
+     * @see PatternOptionBuilder
+     */
+    public Object getParsedOptionValue(String opt)
+    throws ParseException
+    {
+        String res = getOptionValue(opt);
+
+        Option option = resolveOption(opt);
+        if (option == null)
+        {
+            return null;
+        }
+
+        Object type = option.getType();
+
+        return (res == null)        ? null : TypeHandler.createValue(res, type);
+    }
+
+    /**
+     * Return the <code>Object</code> type of this <code>Option</code>.
+     *
+     * @param opt the name of the option
+     * @return the type of opt
+     */
+    public Object getOptionObject(char opt)
+    {
+        return getOptionObject(String.valueOf(opt));
+    }
+
+    /** 
+     * Retrieve the argument, if any, of this option.
+     *
+     * @param opt the name of the option
+     * @return Value of the argument if option is set, and has an argument,
+     * otherwise null.
+     */
+    public String getOptionValue(String opt)
+    {
+        String[] values = getOptionValues(opt);
+
+        return (values == null) ? null : values[0];
+    }
+
+    /** 
+     * Retrieve the argument, if any, of this option.
+     *
+     * @param opt the character name of the option
+     * @return Value of the argument if option is set, and has an argument,
+     * otherwise null.
+     */
+    public String getOptionValue(char opt)
+    {
+        return getOptionValue(String.valueOf(opt));
+    }
+
+    /** 
+     * Retrieves the array of values, if any, of an option.
+     *
+     * @param opt string name of the option
+     * @return Values of the argument if option is set, and has an argument,
+     * otherwise null.
+     */
+    public String[] getOptionValues(String opt)
+    {
+        List values = new ArrayList();
+
+        for (Iterator it = options.iterator(); it.hasNext();)
+        {
+            Option option = (Option) it.next();
+            if (opt.equals(option.getOpt()) || opt.equals(option.getLongOpt()))
+            {
+                values.addAll(option.getValuesList());
+            }
+        }
+
+        return values.isEmpty() ? null : (String[]) values.toArray(new String[values.size()]);
+    }
+
+    /**
+     * Retrieves the option object given the long or short option as a String
+     * 
+     * @param opt short or long name of the option
+     * @return Canonicalized option
+     */
+    private Option resolveOption(String opt)
+    {
+        opt = Util.stripLeadingHyphens(opt);
+        for (Iterator it = options.iterator(); it.hasNext();)
+        {
+            Option option = (Option) it.next();
+            if (opt.equals(option.getOpt()))
+            {
+                return option;
+            }
+
+            if (opt.equals(option.getLongOpt()))
+            {
+                return option;
+            }
+
+        }
+        return null;
+    }
+
+    /** 
+     * Retrieves the array of values, if any, of an option.
+     *
+     * @param opt character name of the option
+     * @return Values of the argument if option is set, and has an argument,
+     * otherwise null.
+     */
+    public String[] getOptionValues(char opt)
+    {
+        return getOptionValues(String.valueOf(opt));
+    }
+
+    /** 
+     * Retrieve the argument, if any, of an option.
+     *
+     * @param opt name of the option
+     * @param defaultValue is the default value to be returned if the option
+     * is not specified
+     * @return Value of the argument if option is set, and has an argument,
+     * otherwise <code>defaultValue</code>.
+     */
+    public String getOptionValue(String opt, String defaultValue)
+    {
+        String answer = getOptionValue(opt);
+
+        return (answer != null) ? answer : defaultValue;
+    }
+
+    /** 
+     * Retrieve the argument, if any, of an option.
+     *
+     * @param opt character name of the option
+     * @param defaultValue is the default value to be returned if the option
+     * is not specified
+     * @return Value of the argument if option is set, and has an argument,
+     * otherwise <code>defaultValue</code>.
+     */
+    public String getOptionValue(char opt, String defaultValue)
+    {
+        return getOptionValue(String.valueOf(opt), defaultValue);
+    }
+
+    /**
+     * Retrieve the map of values associated to the option. This is convenient
+     * for options specifying Java properties like <tt>-Dparam1=value1
+     * -Dparam2=value2</tt>. The first argument of the option is the key, and
+     * the 2nd argument is the value. If the option has only one argument
+     * (<tt>-Dfoo</tt>) it is considered as a boolean flag and the value is
+     * <tt>"true"</tt>.
+     *
+     * @param opt name of the option
+     * @return The Properties mapped by the option, never <tt>null</tt>
+     *         even if the option doesn't exists
+     * @since 1.2
+     */
+    public Properties getOptionProperties(String opt)
+    {
+        Properties props = new Properties();
+
+        for (Iterator it = options.iterator(); it.hasNext();)
+        {
+            Option option = (Option) it.next();
+
+            if (opt.equals(option.getOpt()) || opt.equals(option.getLongOpt()))
+            {
+                List values = option.getValuesList();
+                if (values.size() >= 2)
+                {
+                    // use the first 2 arguments as the key/value pair
+                    props.put(values.get(0), values.get(1));
+                }
+                else if (values.size() == 1)
+                {
+                    // no explicit value, handle it as a boolean
+                    props.put(values.get(0), "true");
+                }
+            }
+        }
+
+        return props;
+    }
+
+    /** 
+     * Retrieve any left-over non-recognized options and arguments
+     *
+     * @return remaining items passed in but not parsed as an array
+     */
+    public String[] getArgs()
+    {
+        String[] answer = new String[args.size()];
+
+        args.toArray(answer);
+
+        return answer;
+    }
+
+    /** 
+     * Retrieve any left-over non-recognized options and arguments
+     *
+     * @return remaining items passed in but not parsed as a <code>List</code>.
+     */
+    public List getArgList()
+    {
+        return args;
+    }
+
+    /** 
+     * jkeyes
+     * - commented out until it is implemented properly
+     * <p>Dump state, suitable for debugging.</p>
+     *
+     * @return Stringified form of this object
+     */
+
+    /*
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+            
+        buf.append("[ CommandLine: [ options: ");
+        buf.append(options.toString());
+        buf.append(" ] [ args: ");
+        buf.append(args.toString());
+        buf.append(" ] ]");
+            
+        return buf.toString();
+    }
+    */
+
+    /**
+     * Add left-over unrecognized option/argument.
+     *
+     * @param arg the unrecognised option/argument.
+     */
+    void addArg(String arg)
+    {
+        args.add(arg);
+    }
+
+    /**
+     * Add an option to the command line.  The values of the option are stored.
+     *
+     * @param opt the processed option
+     */
+    void addOption(Option opt)
+    {
+        options.add(opt);
+    }
+
+    /**
+     * Returns an iterator over the Option members of CommandLine.
+     *
+     * @return an <code>Iterator</code> over the processed {@link Option}
+     * members of this {@link CommandLine}
+     */
+    public Iterator iterator()
+    {
+        return options.iterator();
+    }
+
+    /**
+     * Returns an array of the processed {@link Option}s.
+     *
+     * @return an array of the processed {@link Option}s.
+     */
+    public Option[] getOptions()
+    {
+        Collection processed = options;
+
+        // reinitialise array
+        Option[] optionsArray = new Option[processed.size()];
+
+        // return the array
+        return (Option[]) processed.toArray(optionsArray);
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/CommandLineParser.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/CommandLineParser.java
new file mode 100644
index 0000000..237d637
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/CommandLineParser.java
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * A class that implements the <code>CommandLineParser</code> interface
+ * can parse a String array according to the {@link Options} specified
+ * and return a {@link CommandLine}.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public interface CommandLineParser
+{
+    /**
+     * Parse the arguments according to the specified options.
+     *
+     * @param options the specified Options
+     * @param arguments the command line arguments
+     * @return the list of atomic option and value tokens
+     *
+     * @throws ParseException if there are any problems encountered
+     * while parsing the command line tokens.
+     */
+    CommandLine parse(Options options, String[] arguments) throws ParseException;
+
+    /**
+     * Parse the arguments according to the specified options and
+     * properties.
+     *
+     * @param options the specified Options
+     * @param arguments the command line arguments
+     * @param properties command line option name-value pairs
+     * @return the list of atomic option and value tokens
+     *
+     * @throws ParseException if there are any problems encountered
+     * while parsing the command line tokens.
+     */
+    /* To maintain binary compatibility, this is commented out.
+       It is still in the abstract Parser class, so most users will
+       still reap the benefit.
+    CommandLine parse(Options options, String[] arguments, Properties properties)
+               throws ParseException;
+     */
+
+    /**
+     * Parse the arguments according to the specified options.
+     *
+     * @param options the specified Options
+     * @param arguments the command line arguments
+     * @param stopAtNonOption specifies whether to continue parsing the
+     * arguments if a non option is encountered.
+     *
+     * @return the list of atomic option and value tokens
+     * @throws ParseException if there are any problems encountered
+     * while parsing the command line tokens.
+     */
+    CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException;
+
+    /**
+     * Parse the arguments according to the specified options and
+     * properties.
+     *
+     * @param options the specified Options
+     * @param arguments the command line arguments
+     * @param properties command line option name-value pairs
+     * @param stopAtNonOption specifies whether to continue parsing the
+     *
+     * @return the list of atomic option and value tokens
+     * @throws ParseException if there are any problems encountered
+     * while parsing the command line tokens.
+     */
+    /* To maintain binary compatibility, this is commented out.
+       It is still in the abstract Parser class, so most users will
+       still reap the benefit.
+    CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
+            throws ParseException;
+     */
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/GnuParser.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/GnuParser.java
new file mode 100644
index 0000000..c53cacf
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/GnuParser.java
@@ -0,0 +1,113 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The class GnuParser provides an implementation of the
+ * {@link Parser#flatten(Options, String[], boolean) flatten} method.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class GnuParser extends Parser
+{
+    /**
+     * This flatten method does so using the following rules:
+     * <ol>
+     *   <li>If an {@link Option} exists for the first character of
+     *   the <code>arguments</code> entry <b>AND</b> an {@link Option}
+     *   does not exist for the whole <code>argument</code> then
+     *   add the first character as an option to the processed tokens
+     *   list e.g. "-D" and add the rest of the entry to the also.</li>
+     *   <li>Otherwise just add the token to the processed tokens list.</li>
+     * </ol>
+     *
+     * @param options         The Options to parse the arguments by.
+     * @param arguments       The arguments that have to be flattened.
+     * @param stopAtNonOption specifies whether to stop flattening when
+     *                        a non option has been encountered
+     * @return a String array of the flattened arguments
+     */
+    protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption)
+    {
+        List tokens = new ArrayList();
+
+        boolean eatTheRest = false;
+
+        for (int i = 0; i < arguments.length; i++)
+        {
+            String arg = arguments[i];
+
+            if ("--".equals(arg))
+            {
+                eatTheRest = true;
+                tokens.add("--");
+            }
+            else if ("-".equals(arg))
+            {
+                tokens.add("-");
+            }
+            else if (arg.startsWith("-"))
+            {
+                String opt = Util.stripLeadingHyphens(arg);
+
+                if (options.hasOption(opt))
+                {
+                    tokens.add(arg);
+                }
+                else
+                {
+                    if (opt.indexOf('=') != -1 && options.hasOption(opt.substring(0, opt.indexOf('='))))
+                    {
+                        // the format is --foo=value or -foo=value
+                        tokens.add(arg.substring(0, arg.indexOf('='))); // --foo
+                        tokens.add(arg.substring(arg.indexOf('=') + 1)); // value
+                    }
+                    else if (options.hasOption(arg.substring(0, 2)))
+                    {
+                        // the format is a special properties option (-Dproperty=value)
+                        tokens.add(arg.substring(0, 2)); // -D
+                        tokens.add(arg.substring(2)); // property=value
+                    }
+                    else
+                    {
+                        eatTheRest = stopAtNonOption;
+                        tokens.add(arg);
+                    }
+                }
+            }
+            else
+            {
+                tokens.add(arg);
+            }
+
+            if (eatTheRest)
+            {
+                for (i++; i < arguments.length; i++)
+                {
+                    tokens.add(arguments[i]);
+                }
+            }
+        }
+
+        return (String[]) tokens.toArray(new String[tokens.size()]);
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/HelpFormatter.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/HelpFormatter.java
new file mode 100644
index 0000000..46d9bf0
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/HelpFormatter.java
@@ -0,0 +1,984 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/** 
+ * A formatter of help messages for the current command line options
+ *
+ * @author Slawek Zachcial
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 751120 $, $Date: 2009-03-06 14:45:57 -0800 (Fri, 06 Mar 2009) $
+ */
+public class HelpFormatter
+{
+    // --------------------------------------------------------------- Constants
+
+    /** default number of characters per line */
+    public static final int DEFAULT_WIDTH = 74;
+
+    /** default padding to the left of each line */
+    public static final int DEFAULT_LEFT_PAD = 1;
+
+    /**
+     * the number of characters of padding to be prefixed
+     * to each description line
+     */
+    public static final int DEFAULT_DESC_PAD = 3;
+
+    /** the string to display at the beginning of the usage statement */
+    public static final String DEFAULT_SYNTAX_PREFIX = "usage: ";
+
+    /** default prefix for shortOpts */
+    public static final String DEFAULT_OPT_PREFIX = "-";
+
+    /** default prefix for long Option */
+    public static final String DEFAULT_LONG_OPT_PREFIX = "--";
+
+    /** default name for an argument */
+    public static final String DEFAULT_ARG_NAME = "arg";
+
+    // -------------------------------------------------------------- Attributes
+
+    /**
+     * number of characters per line
+     *
+     * @deprecated Scope will be made private for next major version
+     * - use get/setWidth methods instead.
+     */
+    public int defaultWidth = DEFAULT_WIDTH;
+
+    /**
+     * amount of padding to the left of each line
+     *
+     * @deprecated Scope will be made private for next major version
+     * - use get/setLeftPadding methods instead.
+     */
+    public int defaultLeftPad = DEFAULT_LEFT_PAD;
+
+    /**
+     * the number of characters of padding to be prefixed
+     * to each description line
+     *
+     * @deprecated Scope will be made private for next major version
+     * - use get/setDescPadding methods instead.
+     */
+    public int defaultDescPad = DEFAULT_DESC_PAD;
+
+    /**
+     * the string to display at the begining of the usage statement
+     *
+     * @deprecated Scope will be made private for next major version
+     * - use get/setSyntaxPrefix methods instead.
+     */
+    public String defaultSyntaxPrefix = DEFAULT_SYNTAX_PREFIX;
+
+    /**
+     * the new line string
+     *
+     * @deprecated Scope will be made private for next major version
+     * - use get/setNewLine methods instead.
+     */
+    public String defaultNewLine = System.getProperty("line.separator");
+
+    /**
+     * the shortOpt prefix
+     *
+     * @deprecated Scope will be made private for next major version
+     * - use get/setOptPrefix methods instead.
+     */
+    public String defaultOptPrefix = DEFAULT_OPT_PREFIX;
+
+    /**
+     * the long Opt prefix
+     *
+     * @deprecated Scope will be made private for next major version
+     * - use get/setLongOptPrefix methods instead.
+     */
+    public String defaultLongOptPrefix = DEFAULT_LONG_OPT_PREFIX;
+
+    /**
+     * the name of the argument
+     *
+     * @deprecated Scope will be made private for next major version
+     * - use get/setArgName methods instead.
+     */
+    public String defaultArgName = DEFAULT_ARG_NAME;
+
+    /**
+     * Comparator used to sort the options when they output in help text
+     * 
+     * Defaults to case-insensitive alphabetical sorting by option key
+     */
+    protected Comparator optionComparator = new OptionComparator();
+
+    /**
+     * Sets the 'width'.
+     *
+     * @param width the new value of 'width'
+     */
+    public void setWidth(int width)
+    {
+        this.defaultWidth = width;
+    }
+
+    /**
+     * Returns the 'width'.
+     *
+     * @return the 'width'
+     */
+    public int getWidth()
+    {
+        return defaultWidth;
+    }
+
+    /**
+     * Sets the 'leftPadding'.
+     *
+     * @param padding the new value of 'leftPadding'
+     */
+    public void setLeftPadding(int padding)
+    {
+        this.defaultLeftPad = padding;
+    }
+
+    /**
+     * Returns the 'leftPadding'.
+     *
+     * @return the 'leftPadding'
+     */
+    public int getLeftPadding()
+    {
+        return defaultLeftPad;
+    }
+
+    /**
+     * Sets the 'descPadding'.
+     *
+     * @param padding the new value of 'descPadding'
+     */
+    public void setDescPadding(int padding)
+    {
+        this.defaultDescPad = padding;
+    }
+
+    /**
+     * Returns the 'descPadding'.
+     *
+     * @return the 'descPadding'
+     */
+    public int getDescPadding()
+    {
+        return defaultDescPad;
+    }
+
+    /**
+     * Sets the 'syntaxPrefix'.
+     *
+     * @param prefix the new value of 'syntaxPrefix'
+     */
+    public void setSyntaxPrefix(String prefix)
+    {
+        this.defaultSyntaxPrefix = prefix;
+    }
+
+    /**
+     * Returns the 'syntaxPrefix'.
+     *
+     * @return the 'syntaxPrefix'
+     */
+    public String getSyntaxPrefix()
+    {
+        return defaultSyntaxPrefix;
+    }
+
+    /**
+     * Sets the 'newLine'.
+     *
+     * @param newline the new value of 'newLine'
+     */
+    public void setNewLine(String newline)
+    {
+        this.defaultNewLine = newline;
+    }
+
+    /**
+     * Returns the 'newLine'.
+     *
+     * @return the 'newLine'
+     */
+    public String getNewLine()
+    {
+        return defaultNewLine;
+    }
+
+    /**
+     * Sets the 'optPrefix'.
+     *
+     * @param prefix the new value of 'optPrefix'
+     */
+    public void setOptPrefix(String prefix)
+    {
+        this.defaultOptPrefix = prefix;
+    }
+
+    /**
+     * Returns the 'optPrefix'.
+     *
+     * @return the 'optPrefix'
+     */
+    public String getOptPrefix()
+    {
+        return defaultOptPrefix;
+    }
+
+    /**
+     * Sets the 'longOptPrefix'.
+     *
+     * @param prefix the new value of 'longOptPrefix'
+     */
+    public void setLongOptPrefix(String prefix)
+    {
+        this.defaultLongOptPrefix = prefix;
+    }
+
+    /**
+     * Returns the 'longOptPrefix'.
+     *
+     * @return the 'longOptPrefix'
+     */
+    public String getLongOptPrefix()
+    {
+        return defaultLongOptPrefix;
+    }
+
+    /**
+     * Sets the 'argName'.
+     *
+     * @param name the new value of 'argName'
+     */
+    public void setArgName(String name)
+    {
+        this.defaultArgName = name;
+    }
+
+    /**
+     * Returns the 'argName'.
+     *
+     * @return the 'argName'
+     */
+    public String getArgName()
+    {
+        return defaultArgName;
+    }
+
+    /**
+     * Comparator used to sort the options when they output in help text
+     * 
+     * Defaults to case-insensitive alphabetical sorting by option key
+     */
+    public Comparator getOptionComparator()
+    {
+        return optionComparator;
+    }
+
+    /**
+     * Set the comparator used to sort the options when they output in help text
+     * 
+     * Passing in a null parameter will set the ordering to the default mode
+     */
+    public void setOptionComparator(Comparator comparator)
+    {
+        if (comparator == null)
+        {
+            this.optionComparator = new OptionComparator();
+        }
+        else
+        {
+            this.optionComparator = comparator;
+        }
+    }
+
+    /**
+     * Print the help for <code>options</code> with the specified
+     * command line syntax.  This method prints help information to
+     * System.out.
+     *
+     * @param cmdLineSyntax the syntax for this application
+     * @param options the Options instance
+     */
+    public void printHelp(String cmdLineSyntax, Options options)
+    {
+        printHelp(defaultWidth, cmdLineSyntax, null, options, null, false);
+    }
+
+    /**
+     * Print the help for <code>options</code> with the specified
+     * command line syntax.  This method prints help information to 
+     * System.out.
+     *
+     * @param cmdLineSyntax the syntax for this application
+     * @param options the Options instance
+     * @param autoUsage whether to print an automatically generated
+     * usage statement
+     */
+    public void printHelp(String cmdLineSyntax, Options options, boolean autoUsage)
+    {
+        printHelp(defaultWidth, cmdLineSyntax, null, options, null, autoUsage);
+    }
+
+    /**
+     * Print the help for <code>options</code> with the specified
+     * command line syntax.  This method prints help information to
+     * System.out.
+     *
+     * @param cmdLineSyntax the syntax for this application
+     * @param header the banner to display at the begining of the help
+     * @param options the Options instance
+     * @param footer the banner to display at the end of the help
+     */
+    public void printHelp(String cmdLineSyntax, String header, Options options, String footer)
+    {
+        printHelp(cmdLineSyntax, header, options, footer, false);
+    }
+
+    /**
+     * Print the help for <code>options</code> with the specified
+     * command line syntax.  This method prints help information to 
+     * System.out.
+     *
+     * @param cmdLineSyntax the syntax for this application
+     * @param header the banner to display at the begining of the help
+     * @param options the Options instance
+     * @param footer the banner to display at the end of the help
+     * @param autoUsage whether to print an automatically generated
+     * usage statement
+     */
+    public void printHelp(String cmdLineSyntax, String header, Options options, String footer, boolean autoUsage)
+    {
+        printHelp(defaultWidth, cmdLineSyntax, header, options, footer, autoUsage);
+    }
+
+    /**
+     * Print the help for <code>options</code> with the specified
+     * command line syntax.  This method prints help information to
+     * System.out.
+     *
+     * @param width the number of characters to be displayed on each line
+     * @param cmdLineSyntax the syntax for this application
+     * @param header the banner to display at the beginning of the help
+     * @param options the Options instance
+     * @param footer the banner to display at the end of the help
+     */
+    public void printHelp(int width, String cmdLineSyntax, String header, Options options, String footer)
+    {
+        printHelp(width, cmdLineSyntax, header, options, footer, false);
+    }
+
+    /**
+     * Print the help for <code>options</code> with the specified
+     * command line syntax.  This method prints help information to
+     * System.out.
+     *
+     * @param width the number of characters to be displayed on each line
+     * @param cmdLineSyntax the syntax for this application
+     * @param header the banner to display at the begining of the help
+     * @param options the Options instance
+     * @param footer the banner to display at the end of the help
+     * @param autoUsage whether to print an automatically generated 
+     * usage statement
+     */
+    public void printHelp(int width, String cmdLineSyntax, String header,
+                          Options options, String footer, boolean autoUsage)
+    {
+        PrintWriter pw = new PrintWriter(System.out);
+
+        printHelp(pw, width, cmdLineSyntax, header, options, defaultLeftPad, defaultDescPad, footer, autoUsage);
+        pw.flush();
+    }
+
+    /**
+     * Print the help for <code>options</code> with the specified
+     * command line syntax.
+     *
+     * @param pw the writer to which the help will be written
+     * @param width the number of characters to be displayed on each line
+     * @param cmdLineSyntax the syntax for this application
+     * @param header the banner to display at the begining of the help
+     * @param options the Options instance
+     * @param leftPad the number of characters of padding to be prefixed
+     * to each line
+     * @param descPad the number of characters of padding to be prefixed
+     * to each description line
+     * @param footer the banner to display at the end of the help
+     *
+     * @throws IllegalStateException if there is no room to print a line
+     */
+    public void printHelp(PrintWriter pw, int width, String cmdLineSyntax, 
+                          String header, Options options, int leftPad, 
+                          int descPad, String footer)
+    {
+        printHelp(pw, width, cmdLineSyntax, header, options, leftPad, descPad, footer, false);
+    }
+
+
+    /**
+     * Print the help for <code>options</code> with the specified
+     * command line syntax.
+     *
+     * @param pw the writer to which the help will be written
+     * @param width the number of characters to be displayed on each line
+     * @param cmdLineSyntax the syntax for this application
+     * @param header the banner to display at the begining of the help
+     * @param options the Options instance
+     * @param leftPad the number of characters of padding to be prefixed
+     * to each line
+     * @param descPad the number of characters of padding to be prefixed
+     * to each description line
+     * @param footer the banner to display at the end of the help
+     * @param autoUsage whether to print an automatically generated
+     * usage statement
+     *
+     * @throws IllegalStateException if there is no room to print a line
+     */
+    public void printHelp(PrintWriter pw, int width, String cmdLineSyntax,
+                          String header, Options options, int leftPad,
+                          int descPad, String footer, boolean autoUsage)
+    {
+        if ((cmdLineSyntax == null) || (cmdLineSyntax.length() == 0))
+        {
+            throw new IllegalArgumentException("cmdLineSyntax not provided");
+        }
+
+        if (autoUsage)
+        {
+            printUsage(pw, width, cmdLineSyntax, options);
+        }
+        else
+        {
+            printUsage(pw, width, cmdLineSyntax);
+        }
+
+        if ((header != null) && (header.trim().length() > 0))
+        {
+            printWrapped(pw, width, header);
+        }
+
+        printOptions(pw, width, options, leftPad, descPad);
+
+        if ((footer != null) && (footer.trim().length() > 0))
+        {
+            printWrapped(pw, width, footer);
+        }
+    }
+
+    /**
+     * <p>Prints the usage statement for the specified application.</p>
+     *
+     * @param pw The PrintWriter to print the usage statement 
+     * @param width The number of characters to display per line
+     * @param app The application name
+     * @param options The command line Options
+     *
+     */
+    public void printUsage(PrintWriter pw, int width, String app, Options options)
+    {
+        // initialise the string buffer
+        StringBuffer buff = new StringBuffer(defaultSyntaxPrefix).append(app).append(" ");
+
+        // create a list for processed option groups
+        final Collection processedGroups = new ArrayList();
+
+        // temp variable
+        Option option;
+
+        List optList = new ArrayList(options.getOptions());
+        Collections.sort(optList, getOptionComparator());
+        // iterate over the options
+        for (Iterator i = optList.iterator(); i.hasNext();)
+        {
+            // get the next Option
+            option = (Option) i.next();
+
+            // check if the option is part of an OptionGroup
+            OptionGroup group = options.getOptionGroup(option);
+
+            // if the option is part of a group 
+            if (group != null)
+            {
+                // and if the group has not already been processed
+                if (!processedGroups.contains(group))
+                {
+                    // add the group to the processed list
+                    processedGroups.add(group);
+
+
+                    // add the usage clause
+                    appendOptionGroup(buff, group);
+                }
+
+                // otherwise the option was displayed in the group
+                // previously so ignore it.
+            }
+
+            // if the Option is not part of an OptionGroup
+            else
+            {
+                appendOption(buff, option, option.isRequired());
+            }
+
+            if (i.hasNext())
+            {
+                buff.append(" ");
+            }
+        }
+
+
+        // call printWrapped
+        printWrapped(pw, width, buff.toString().indexOf(' ') + 1, buff.toString());
+    }
+
+    /**
+     * Appends the usage clause for an OptionGroup to a StringBuffer.  
+     * The clause is wrapped in square brackets if the group is required.
+     * The display of the options is handled by appendOption
+     * @param buff the StringBuffer to append to
+     * @param group the group to append
+     * @see #appendOption(StringBuffer,Option,boolean)
+     */
+    private void appendOptionGroup(final StringBuffer buff, final OptionGroup group)
+    {
+        if (!group.isRequired())
+        {
+            buff.append("[");
+        }
+
+        List optList = new ArrayList(group.getOptions());
+        Collections.sort(optList, getOptionComparator());
+        // for each option in the OptionGroup
+        for (Iterator i = optList.iterator(); i.hasNext();)
+        {
+            // whether the option is required or not is handled at group level
+            appendOption(buff, (Option) i.next(), true);
+
+            if (i.hasNext())
+            {
+                buff.append(" | ");
+            }
+        }
+
+        if (!group.isRequired())
+        {
+            buff.append("]");
+        }
+    }
+
+    /**
+     * Appends the usage clause for an Option to a StringBuffer.  
+     *
+     * @param buff the StringBuffer to append to
+     * @param option the Option to append
+     * @param required whether the Option is required or not
+     */
+    private static void appendOption(final StringBuffer buff, final Option option, final boolean required)
+    {
+        if (!required)
+        {
+            buff.append("[");
+        }
+
+        if (option.getOpt() != null)
+        {
+            buff.append("-").append(option.getOpt());
+        }
+        else
+        {
+            buff.append("--").append(option.getLongOpt());
+        }
+
+        // if the Option has a value
+        if (option.hasArg() && option.hasArgName())
+        {
+            buff.append(" <").append(option.getArgName()).append(">");
+        }
+
+        // if the Option is not a required option
+        if (!required)
+        {
+            buff.append("]");
+        }
+    }
+
+    /**
+     * Print the cmdLineSyntax to the specified writer, using the
+     * specified width.
+     *
+     * @param pw The printWriter to write the help to
+     * @param width The number of characters per line for the usage statement.
+     * @param cmdLineSyntax The usage statement.
+     */
+    public void printUsage(PrintWriter pw, int width, String cmdLineSyntax)
+    {
+        int argPos = cmdLineSyntax.indexOf(' ') + 1;
+
+        printWrapped(pw, width, defaultSyntaxPrefix.length() + argPos, defaultSyntaxPrefix + cmdLineSyntax);
+    }
+
+    /**
+     * <p>Print the help for the specified Options to the specified writer, 
+     * using the specified width, left padding and description padding.</p>
+     *
+     * @param pw The printWriter to write the help to
+     * @param width The number of characters to display per line
+     * @param options The command line Options
+     * @param leftPad the number of characters of padding to be prefixed
+     * to each line
+     * @param descPad the number of characters of padding to be prefixed
+     * to each description line
+     */
+    public void printOptions(PrintWriter pw, int width, Options options, 
+                             int leftPad, int descPad)
+    {
+        StringBuffer sb = new StringBuffer();
+
+        renderOptions(sb, width, options, leftPad, descPad);
+        pw.println(sb.toString());
+    }
+
+    /**
+     * Print the specified text to the specified PrintWriter.
+     *
+     * @param pw The printWriter to write the help to
+     * @param width The number of characters to display per line
+     * @param text The text to be written to the PrintWriter
+     */
+    public void printWrapped(PrintWriter pw, int width, String text)
+    {
+        printWrapped(pw, width, 0, text);
+    }
+
+    /**
+     * Print the specified text to the specified PrintWriter.
+     *
+     * @param pw The printWriter to write the help to
+     * @param width The number of characters to display per line
+     * @param nextLineTabStop The position on the next line for the first tab.
+     * @param text The text to be written to the PrintWriter
+     */
+    public void printWrapped(PrintWriter pw, int width, int nextLineTabStop, String text)
+    {
+        StringBuffer sb = new StringBuffer(text.length());
+
+        renderWrappedText(sb, width, nextLineTabStop, text);
+        pw.println(sb.toString());
+    }
+
+    // --------------------------------------------------------------- Protected
+
+    /**
+     * Render the specified Options and return the rendered Options
+     * in a StringBuffer.
+     *
+     * @param sb The StringBuffer to place the rendered Options into.
+     * @param width The number of characters to display per line
+     * @param options The command line Options
+     * @param leftPad the number of characters of padding to be prefixed
+     * to each line
+     * @param descPad the number of characters of padding to be prefixed
+     * to each description line
+     *
+     * @return the StringBuffer with the rendered Options contents.
+     */
+    protected StringBuffer renderOptions(StringBuffer sb, int width, Options options, int leftPad, int descPad)
+    {
+        final String lpad = createPadding(leftPad);
+        final String dpad = createPadding(descPad);
+
+        // first create list containing only <lpad>-a,--aaa where 
+        // -a is opt and --aaa is long opt; in parallel look for 
+        // the longest opt string this list will be then used to 
+        // sort options ascending
+        int max = 0;
+        StringBuffer optBuf;
+        List prefixList = new ArrayList();
+
+        List optList = options.helpOptions();
+
+        Collections.sort(optList, getOptionComparator());
+
+        for (Iterator i = optList.iterator(); i.hasNext();)
+        {
+            Option option = (Option) i.next();
+            optBuf = new StringBuffer(8);
+
+            if (option.getOpt() == null)
+            {
+                optBuf.append(lpad).append("   " + defaultLongOptPrefix).append(option.getLongOpt());
+            }
+            else
+            {
+                optBuf.append(lpad).append(defaultOptPrefix).append(option.getOpt());
+
+                if (option.hasLongOpt())
+                {
+                    optBuf.append(',').append(defaultLongOptPrefix).append(option.getLongOpt());
+                }
+            }
+
+            if (option.hasArg())
+            {
+                if (option.hasArgName())
+                {
+                    optBuf.append(" <").append(option.getArgName()).append(">");
+                }
+                else
+                {
+                    optBuf.append(' ');
+                }
+            }
+
+            prefixList.add(optBuf);
+            max = (optBuf.length() > max) ? optBuf.length() : max;
+        }
+
+        int x = 0;
+
+        for (Iterator i = optList.iterator(); i.hasNext();)
+        {
+            Option option = (Option) i.next();
+            optBuf = new StringBuffer(prefixList.get(x++).toString());
+
+            if (optBuf.length() < max)
+            {
+                optBuf.append(createPadding(max - optBuf.length()));
+            }
+
+            optBuf.append(dpad);
+
+            int nextLineTabStop = max + descPad;
+
+            if (option.getDescription() != null)
+            {
+                optBuf.append(option.getDescription());
+            }
+
+            renderWrappedText(sb, width, nextLineTabStop, optBuf.toString());
+
+            if (i.hasNext())
+            {
+                sb.append(defaultNewLine);
+            }
+        }
+
+        return sb;
+    }
+
+    /**
+     * Render the specified text and return the rendered Options
+     * in a StringBuffer.
+     *
+     * @param sb The StringBuffer to place the rendered text into.
+     * @param width The number of characters to display per line
+     * @param nextLineTabStop The position on the next line for the first tab.
+     * @param text The text to be rendered.
+     *
+     * @return the StringBuffer with the rendered Options contents.
+     */
+    protected StringBuffer renderWrappedText(StringBuffer sb, int width, 
+                                             int nextLineTabStop, String text)
+    {
+        int pos = findWrapPos(text, width, 0);
+
+        if (pos == -1)
+        {
+            sb.append(rtrim(text));
+
+            return sb;
+        }
+        sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
+
+        if (nextLineTabStop >= width)
+        {
+            // stops infinite loop happening
+            nextLineTabStop = 1;
+        }
+
+        // all following lines must be padded with nextLineTabStop space 
+        // characters
+        final String padding = createPadding(nextLineTabStop);
+
+        while (true)
+        {
+            text = padding + text.substring(pos).trim();
+            pos = findWrapPos(text, width, 0);
+
+            if (pos == -1)
+            {
+                sb.append(text);
+
+                return sb;
+            }
+            
+            if ( (text.length() > width) && (pos == nextLineTabStop - 1) ) 
+            {
+                pos = width;
+            }
+
+            sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
+        }
+    }
+
+    /**
+     * Finds the next text wrap position after <code>startPos</code> for the
+     * text in <code>text</code> with the column width <code>width</code>.
+     * The wrap point is the last postion before startPos+width having a 
+     * whitespace character (space, \n, \r).
+     *
+     * @param text The text being searched for the wrap position
+     * @param width width of the wrapped text
+     * @param startPos position from which to start the lookup whitespace
+     * character
+     * @return postion on which the text must be wrapped or -1 if the wrap
+     * position is at the end of the text
+     */
+    protected int findWrapPos(String text, int width, int startPos)
+    {
+        int pos = -1;
+
+        // the line ends before the max wrap pos or a new line char found
+        if (((pos = text.indexOf('\n', startPos)) != -1 && pos <= width)
+                || ((pos = text.indexOf('\t', startPos)) != -1 && pos <= width))
+        {
+            return pos + 1;
+        }
+        else if (startPos + width >= text.length())
+        {
+            return -1;
+        }
+
+
+        // look for the last whitespace character before startPos+width
+        pos = startPos + width;
+
+        char c;
+
+        while ((pos >= startPos) && ((c = text.charAt(pos)) != ' ')
+                && (c != '\n') && (c != '\r'))
+        {
+            --pos;
+        }
+
+        // if we found it - just return
+        if (pos > startPos)
+        {
+            return pos;
+        }
+        
+        // must look for the first whitespace chearacter after startPos 
+        // + width
+        pos = startPos + width;
+
+        while ((pos <= text.length()) && ((c = text.charAt(pos)) != ' ')
+               && (c != '\n') && (c != '\r'))
+        {
+            ++pos;
+        }
+
+        return (pos == text.length()) ? (-1) : pos;
+    }
+
+    /**
+     * Return a String of padding of length <code>len</code>.
+     *
+     * @param len The length of the String of padding to create.
+     *
+     * @return The String of padding
+     */
+    protected String createPadding(int len)
+    {
+        StringBuffer sb = new StringBuffer(len);
+
+        for (int i = 0; i < len; ++i)
+        {
+            sb.append(' ');
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Remove the trailing whitespace from the specified String.
+     *
+     * @param s The String to remove the trailing padding from.
+     *
+     * @return The String of without the trailing padding
+     */
+    protected String rtrim(String s)
+    {
+        if ((s == null) || (s.length() == 0))
+        {
+            return s;
+        }
+
+        int pos = s.length();
+
+        while ((pos > 0) && Character.isWhitespace(s.charAt(pos - 1)))
+        {
+            --pos;
+        }
+
+        return s.substring(0, pos);
+    }
+
+    // ------------------------------------------------------ Package protected
+    // ---------------------------------------------------------------- Private
+    // ---------------------------------------------------------- Inner classes
+    /**
+     * This class implements the <code>Comparator</code> interface
+     * for comparing Options.
+     */
+    private static class OptionComparator implements Comparator
+    {
+
+        /**
+         * Compares its two arguments for order. Returns a negative
+         * integer, zero, or a positive integer as the first argument
+         * is less than, equal to, or greater than the second.
+         *
+         * @param o1 The first Option to be compared.
+         * @param o2 The second Option to be compared.
+         * @return a negative integer, zero, or a positive integer as
+         *         the first argument is less than, equal to, or greater than the
+         *         second.
+         */
+        public int compare(Object o1, Object o2)
+        {
+            Option opt1 = (Option) o1;
+            Option opt2 = (Option) o2;
+
+            return opt1.getKey().compareToIgnoreCase(opt2.getKey());
+        }
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/MissingArgumentException.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/MissingArgumentException.java
new file mode 100644
index 0000000..5c80081
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/MissingArgumentException.java
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * Thrown when an option requiring an argument
+ * is not provided with an argument.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class MissingArgumentException extends ParseException
+{
+    /** The option requiring additional arguments */
+    private Option option;
+
+    /**
+     * Construct a new <code>MissingArgumentException</code>
+     * with the specified detail message.
+     *
+     * @param message the detail message
+     */
+    public MissingArgumentException(String message)
+    {
+        super(message);
+    }
+
+    /**
+     * Construct a new <code>MissingArgumentException</code>
+     * with the specified detail message.
+     *
+     * @param option the option requiring an argument
+     * @since 1.2
+     */
+    public MissingArgumentException(Option option)
+    {
+        this("Missing argument for option: " + option.getKey());
+        this.option = option;
+    }
+
+    /**
+     * Return the option requiring an argument that wasn't provided
+     * on the command line.
+     *
+     * @return the related option
+     * @since 1.2
+     */
+    public Option getOption()
+    {
+        return option;
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/MissingOptionException.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/MissingOptionException.java
new file mode 100644
index 0000000..0341d55
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/MissingOptionException.java
@@ -0,0 +1,93 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * Thrown when a required option has not been provided.
+ *
+ * @author John Keyes ( john at integralsource.com )
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class MissingOptionException extends ParseException
+{
+    /** The list of missing options */
+    private List missingOptions;
+
+    /**
+     * Construct a new <code>MissingSelectedException</code>
+     * with the specified detail message.
+     *
+     * @param message the detail message
+     */
+    public MissingOptionException(String message)
+    {
+        super(message);
+    }
+
+    /**
+     * Constructs a new <code>MissingSelectedException</code> with the
+     * specified list of missing options.
+     *
+     * @param missingOptions the list of missing options
+     * @since 1.2
+     */
+    public MissingOptionException(List missingOptions)
+    {
+        this(createMessage(missingOptions));
+        this.missingOptions = missingOptions;
+    }
+
+    /**
+     * Return the list of options (as strings) missing in the command line parsed.
+     *
+     * @return the missing options
+     * @since 1.2
+     */
+    public List getMissingOptions()
+    {
+        return missingOptions;
+    }
+
+    /**
+     * Build the exception message from the specified list of options.
+     *
+     * @param missingOptions
+     * @since 1.2
+     */
+    private static String createMessage(List missingOptions)
+    {
+        StringBuffer buff = new StringBuffer("Missing required option");
+        buff.append(missingOptions.size() == 1 ? "" : "s");
+        buff.append(": ");
+
+        Iterator it = missingOptions.iterator();
+        while (it.hasNext())
+        {
+            buff.append(it.next());
+            if (it.hasNext())
+            {
+                buff.append(", ");
+            }
+        }
+
+        return buff.toString();
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Option.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Option.java
new file mode 100644
index 0000000..8fd69a7
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Option.java
@@ -0,0 +1,674 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/** <p>Describes a single command-line option.  It maintains
+ * information regarding the short-name of the option, the long-name,
+ * if any exists, a flag indicating if an argument is required for
+ * this option, and a self-documenting description of the option.</p>
+ *
+ * <p>An Option is not created independantly, but is create through
+ * an instance of {@link Options}.<p>
+ *
+ * @see org.apache.commons.cli.Options
+ * @see org.apache.commons.cli.CommandLine
+ *
+ * @author bob mcwhirter (bob @ werken.com)
+ * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class Option implements Cloneable, Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** constant that specifies the number of argument values has not been specified */
+    public static final int UNINITIALIZED = -1;
+
+    /** constant that specifies the number of argument values is infinite */
+    public static final int UNLIMITED_VALUES = -2;
+
+    /** the name of the option */
+    private String opt;
+
+    /** the long representation of the option */
+    private String longOpt;
+
+    /** the name of the argument for this option */
+    private String argName = "arg";
+
+    /** description of the option */
+    private String description;
+
+    /** specifies whether this option is required to be present */
+    private boolean required;
+
+    /** specifies whether the argument value of this Option is optional */
+    private boolean optionalArg;
+
+    /** the number of argument values this option can have */
+    private int numberOfArgs = UNINITIALIZED;
+
+    /** the type of this Option */
+    private Object type;
+
+    /** the list of argument values **/
+    private List values = new ArrayList();
+
+    /** the character that is the value separator */
+    private char valuesep;
+
+    /**
+     * Creates an Option using the specified parameters.
+     *
+     * @param opt short representation of the option
+     * @param description describes the function of the option
+     *
+     * @throws IllegalArgumentException if there are any non valid
+     * Option characters in <code>opt</code>.
+     */
+    public Option(String opt, String description) throws IllegalArgumentException
+    {
+        this(opt, null, false, description);
+    }
+
+    /**
+     * Creates an Option using the specified parameters.
+     *
+     * @param opt short representation of the option
+     * @param hasArg specifies whether the Option takes an argument or not
+     * @param description describes the function of the option
+     *
+     * @throws IllegalArgumentException if there are any non valid
+     * Option characters in <code>opt</code>.
+     */
+    public Option(String opt, boolean hasArg, String description) throws IllegalArgumentException
+    {
+        this(opt, null, hasArg, description);
+    }
+
+    /**
+     * Creates an Option using the specified parameters.
+     *
+     * @param opt short representation of the option
+     * @param longOpt the long representation of the option
+     * @param hasArg specifies whether the Option takes an argument or not
+     * @param description describes the function of the option
+     *
+     * @throws IllegalArgumentException if there are any non valid
+     * Option characters in <code>opt</code>.
+     */
+    public Option(String opt, String longOpt, boolean hasArg, String description)
+           throws IllegalArgumentException
+    {
+        // ensure that the option is valid
+        OptionValidator.validateOption(opt);
+
+        this.opt = opt;
+        this.longOpt = longOpt;
+
+        // if hasArg is set then the number of arguments is 1
+        if (hasArg)
+        {
+            this.numberOfArgs = 1;
+        }
+
+        this.description = description;
+    }
+
+    /**
+     * Returns the id of this Option.  This is only set when the
+     * Option shortOpt is a single character.  This is used for switch
+     * statements.
+     *
+     * @return the id of this Option
+     */
+    public int getId()
+    {
+        return getKey().charAt(0);
+    }
+
+    /**
+     * Returns the 'unique' Option identifier.
+     * 
+     * @return the 'unique' Option identifier
+     */
+    String getKey()
+    {
+        // if 'opt' is null, then it is a 'long' option
+        if (opt == null)
+        {
+            return longOpt;
+        }
+
+        return opt;
+    }
+
+    /** 
+     * Retrieve the name of this Option.
+     *
+     * It is this String which can be used with
+     * {@link CommandLine#hasOption(String opt)} and
+     * {@link CommandLine#getOptionValue(String opt)} to check
+     * for existence and argument.
+     *
+     * @return The name of this option
+     */
+    public String getOpt()
+    {
+        return opt;
+    }
+
+    /**
+     * Retrieve the type of this Option.
+     * 
+     * @return The type of this option
+     */
+    public Object getType()
+    {
+        return type;
+    }
+
+    /**
+     * Sets the type of this Option.
+     *
+     * @param type the type of this Option
+     */
+    public void setType(Object type)
+    {
+        this.type = type;
+    }
+
+    /** 
+     * Retrieve the long name of this Option.
+     *
+     * @return Long name of this option, or null, if there is no long name
+     */
+    public String getLongOpt()
+    {
+        return longOpt;
+    }
+
+    /**
+     * Sets the long name of this Option.
+     *
+     * @param longOpt the long name of this Option
+     */
+    public void setLongOpt(String longOpt)
+    {
+        this.longOpt = longOpt;
+    }
+
+    /**
+     * Sets whether this Option can have an optional argument.
+     *
+     * @param optionalArg specifies whether the Option can have
+     * an optional argument.
+     */
+    public void setOptionalArg(boolean optionalArg)
+    {
+        this.optionalArg = optionalArg;
+    }
+
+    /**
+     * @return whether this Option can have an optional argument
+     */
+    public boolean hasOptionalArg()
+    {
+        return optionalArg;
+    }
+
+    /** 
+     * Query to see if this Option has a long name
+     *
+     * @return boolean flag indicating existence of a long name
+     */
+    public boolean hasLongOpt()
+    {
+        return longOpt != null;
+    }
+
+    /** 
+     * Query to see if this Option requires an argument
+     *
+     * @return boolean flag indicating if an argument is required
+     */
+    public boolean hasArg()
+    {
+        return numberOfArgs > 0 || numberOfArgs == UNLIMITED_VALUES;
+    }
+
+    /** 
+     * Retrieve the self-documenting description of this Option
+     *
+     * @return The string description of this option
+     */
+    public String getDescription()
+    {
+        return description;
+    }
+
+    /**
+     * Sets the self-documenting description of this Option
+     *
+     * @param description The description of this option
+     * @since 1.1
+     */
+    public void setDescription(String description)
+    {
+        this.description = description;
+    }
+
+    /** 
+     * Query to see if this Option requires an argument
+     *
+     * @return boolean flag indicating if an argument is required
+     */
+    public boolean isRequired()
+    {
+        return required;
+    }
+
+    /**
+     * Sets whether this Option is mandatory.
+     *
+     * @param required specifies whether this Option is mandatory
+     */
+    public void setRequired(boolean required)
+    {
+        this.required = required;
+    }
+
+    /**
+     * Sets the display name for the argument value.
+     *
+     * @param argName the display name for the argument value.
+     */
+    public void setArgName(String argName)
+    {
+        this.argName = argName;
+    }
+
+    /**
+     * Gets the display name for the argument value.
+     *
+     * @return the display name for the argument value.
+     */
+    public String getArgName()
+    {
+        return argName;
+    }
+
+    /**
+     * Returns whether the display name for the argument value
+     * has been set.
+     *
+     * @return if the display name for the argument value has been
+     * set.
+     */
+    public boolean hasArgName()
+    {
+        return argName != null && argName.length() > 0;
+    }
+
+    /** 
+     * Query to see if this Option can take many values.
+     *
+     * @return boolean flag indicating if multiple values are allowed
+     */
+    public boolean hasArgs()
+    {
+        return numberOfArgs > 1 || numberOfArgs == UNLIMITED_VALUES;
+    }
+
+    /** 
+     * Sets the number of argument values this Option can take.
+     *
+     * @param num the number of argument values
+     */
+    public void setArgs(int num)
+    {
+        this.numberOfArgs = num;
+    }
+
+    /**
+     * Sets the value separator.  For example if the argument value
+     * was a Java property, the value separator would be '='.
+     *
+     * @param sep The value separator.
+     */
+    public void setValueSeparator(char sep)
+    {
+        this.valuesep = sep;
+    }
+
+    /**
+     * Returns the value separator character.
+     *
+     * @return the value separator character.
+     */
+    public char getValueSeparator()
+    {
+        return valuesep;
+    }
+
+    /**
+     * Return whether this Option has specified a value separator.
+     * 
+     * @return whether this Option has specified a value separator.
+     * @since 1.1
+     */
+    public boolean hasValueSeparator()
+    {
+        return valuesep > 0;
+    }
+
+    /** 
+     * Returns the number of argument values this Option can take.
+     *
+     * @return num the number of argument values
+     */
+    public int getArgs()
+    {
+        return numberOfArgs;
+    }
+
+    /**
+     * Adds the specified value to this Option.
+     * 
+     * @param value is a/the value of this Option
+     */
+    void addValueForProcessing(String value)
+    {
+        switch (numberOfArgs)
+        {
+            case UNINITIALIZED:
+                throw new RuntimeException("NO_ARGS_ALLOWED");
+
+            default:
+                processValue(value);
+        }
+    }
+
+    /**
+     * Processes the value.  If this Option has a value separator
+     * the value will have to be parsed into individual tokens.  When
+     * n-1 tokens have been processed and there are more value separators
+     * in the value, parsing is ceased and the remaining characters are
+     * added as a single token.
+     *
+     * @param value The String to be processed.
+     *
+     * @since 1.0.1
+     */
+    private void processValue(String value)
+    {
+        // this Option has a separator character
+        if (hasValueSeparator())
+        {
+            // get the separator character
+            char sep = getValueSeparator();
+
+            // store the index for the value separator
+            int index = value.indexOf(sep);
+
+            // while there are more value separators
+            while (index != -1)
+            {
+                // next value to be added 
+                if (values.size() == (numberOfArgs - 1))
+                {
+                    break;
+                }
+
+                // store
+                add(value.substring(0, index));
+
+                // parse
+                value = value.substring(index + 1);
+
+                // get new index
+                index = value.indexOf(sep);
+            }
+        }
+
+        // store the actual value or the last value that has been parsed
+        add(value);
+    }
+
+    /**
+     * Add the value to this Option.  If the number of arguments
+     * is greater than zero and there is enough space in the list then
+     * add the value.  Otherwise, throw a runtime exception.
+     *
+     * @param value The value to be added to this Option
+     *
+     * @since 1.0.1
+     */
+    private void add(String value)
+    {
+        if ((numberOfArgs > 0) && (values.size() > (numberOfArgs - 1)))
+        {
+            throw new RuntimeException("Cannot add value, list full.");
+        }
+
+        // store value
+        values.add(value);
+    }
+
+    /**
+     * Returns the specified value of this Option or 
+     * <code>null</code> if there is no value.
+     *
+     * @return the value/first value of this Option or 
+     * <code>null</code> if there is no value.
+     */
+    public String getValue()
+    {
+        return hasNoValues() ? null : (String) values.get(0);
+    }
+
+    /**
+     * Returns the specified value of this Option or 
+     * <code>null</code> if there is no value.
+     *
+     * @param index The index of the value to be returned.
+     *
+     * @return the specified value of this Option or 
+     * <code>null</code> if there is no value.
+     *
+     * @throws IndexOutOfBoundsException if index is less than 1
+     * or greater than the number of the values for this Option.
+     */
+    public String getValue(int index) throws IndexOutOfBoundsException
+    {
+        return hasNoValues() ? null : (String) values.get(index);
+    }
+
+    /**
+     * Returns the value/first value of this Option or the 
+     * <code>defaultValue</code> if there is no value.
+     *
+     * @param defaultValue The value to be returned if ther
+     * is no value.
+     *
+     * @return the value/first value of this Option or the 
+     * <code>defaultValue</code> if there are no values.
+     */
+    public String getValue(String defaultValue)
+    {
+        String value = getValue();
+
+        return (value != null) ? value : defaultValue;
+    }
+
+    /**
+     * Return the values of this Option as a String array 
+     * or null if there are no values
+     *
+     * @return the values of this Option as a String array 
+     * or null if there are no values
+     */
+    public String[] getValues()
+    {
+        return hasNoValues() ? null : (String[]) values.toArray(new String[values.size()]);
+    }
+
+    /**
+     * @return the values of this Option as a List
+     * or null if there are no values
+     */
+    public List getValuesList()
+    {
+        return values;
+    }
+
+    /** 
+     * Dump state, suitable for debugging.
+     *
+     * @return Stringified form of this object
+     */
+    public String toString()
+    {
+        StringBuffer buf = new StringBuffer().append("[ option: ");
+
+        buf.append(opt);
+
+        if (longOpt != null)
+        {
+            buf.append(" ").append(longOpt);
+        }
+
+        buf.append(" ");
+
+        if (hasArgs())
+        {
+            buf.append("[ARG...]");
+        }
+        else if (hasArg())
+        {
+            buf.append(" [ARG]");
+        }
+
+        buf.append(" :: ").append(description);
+
+        if (type != null)
+        {
+            buf.append(" :: ").append(type);
+        }
+
+        buf.append(" ]");
+
+        return buf.toString();
+    }
+
+    /**
+     * Returns whether this Option has any values.
+     *
+     * @return whether this Option has any values.
+     */
+    private boolean hasNoValues()
+    {
+        return values.isEmpty();
+    }
+
+    public boolean equals(Object o)
+    {
+        if (this == o)
+        {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass())
+        {
+            return false;
+        }
+
+        Option option = (Option) o;
+
+
+        if (opt != null ? !opt.equals(option.opt) : option.opt != null)
+        {
+            return false;
+        }
+        if (longOpt != null ? !longOpt.equals(option.longOpt) : option.longOpt != null)
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    public int hashCode()
+    {
+        int result;
+        result = (opt != null ? opt.hashCode() : 0);
+        result = 31 * result + (longOpt != null ? longOpt.hashCode() : 0);
+        return result;
+    }
+
+    /**
+     * A rather odd clone method - due to incorrect code in 1.0 it is public 
+     * and in 1.1 rather than throwing a CloneNotSupportedException it throws 
+     * a RuntimeException so as to maintain backwards compat at the API level. 
+     *
+     * After calling this method, it is very likely you will want to call 
+     * clearValues(). 
+     *
+     * @throws RuntimeException
+     */
+    public Object clone()
+    {
+        try
+        {
+            Option option = (Option) super.clone();
+            option.values = new ArrayList(values);
+            return option;
+        }
+        catch (CloneNotSupportedException cnse)
+        {
+            throw new RuntimeException("A CloneNotSupportedException was thrown: " + cnse.getMessage());
+        }
+    }
+
+    /**
+     * Clear the Option values. After a parse is complete, these are left with
+     * data in them and they need clearing if another parse is done.
+     *
+     * See: <a href="https://issues.apache.org/jira/browse/CLI-71">CLI-71</a>
+     */
+    void clearValues()
+    {
+        values.clear();
+    }
+
+    /**
+     * This method is not intended to be used. It was a piece of internal 
+     * API that was made public in 1.0. It currently throws an UnsupportedOperationException. 
+     * @deprecated
+     * @throws UnsupportedOperationException
+     */
+    public boolean addValue(String value)
+    {
+        throw new UnsupportedOperationException("The addValue method is not intended for client use. "
+                + "Subclasses should use the addValueForProcessing method instead. ");
+    }
+
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionBuilder.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionBuilder.java
new file mode 100644
index 0000000..7056769
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionBuilder.java
@@ -0,0 +1,369 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * OptionBuilder allows the user to create Options using descriptive methods.
+ *
+ * <p>Details on the Builder pattern can be found at
+ * <a href="http://c2.com/cgi-bin/wiki?BuilderPattern">
+ * http://c2.com/cgi-bin/wiki?BuilderPattern</a>.</p>
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 754830 $, $Date: 2009-03-16 00:26:44 -0700 (Mon, 16 Mar 2009) $
+ * @since 1.0
+ */
+public final class OptionBuilder
+{
+    /** long option */
+    private static String longopt;
+
+    /** option description */
+    private static String description;
+
+    /** argument name */
+    private static String argName;
+
+    /** is required? */
+    private static boolean required;
+
+    /** the number of arguments */
+    private static int numberOfArgs = Option.UNINITIALIZED;
+
+    /** option type */
+    private static Object type;
+
+    /** option can have an optional argument value */
+    private static boolean optionalArg;
+
+    /** value separator for argument value */
+    private static char valuesep;
+
+    /** option builder instance */
+    private static OptionBuilder instance = new OptionBuilder();
+
+    /**
+     * private constructor to prevent instances being created
+     */
+    private OptionBuilder()
+    {
+        // hide the constructor
+    }
+
+    /**
+     * Resets the member variables to their default values.
+     */
+    private static void reset()
+    {
+        description = null;
+        argName = "arg";
+        longopt = null;
+        type = null;
+        required = false;
+        numberOfArgs = Option.UNINITIALIZED;
+
+
+        // PMM 9/6/02 - these were missing
+        optionalArg = false;
+        valuesep = (char) 0;
+    }
+
+    /**
+     * The next Option created will have the following long option value.
+     *
+     * @param newLongopt the long option value
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withLongOpt(String newLongopt)
+    {
+        OptionBuilder.longopt = newLongopt;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created will require an argument value.
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasArg()
+    {
+        OptionBuilder.numberOfArgs = 1;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created will require an argument value if
+     * <code>hasArg</code> is true.
+     *
+     * @param hasArg if true then the Option has an argument value
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasArg(boolean hasArg)
+    {
+        OptionBuilder.numberOfArgs = hasArg ? 1 : Option.UNINITIALIZED;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created will have the specified argument value name.
+     *
+     * @param name the name for the argument value
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withArgName(String name)
+    {
+        OptionBuilder.argName = name;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created will be required.
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder isRequired()
+    {
+        OptionBuilder.required = true;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created uses <code>sep</code> as a means to
+     * separate argument values.
+     *
+     * <b>Example:</b>
+     * <pre>
+     * Option opt = OptionBuilder.withValueSeparator(':')
+     *                           .create('D');
+     *
+     * CommandLine line = parser.parse(args);
+     * String propertyName = opt.getValue(0);
+     * String propertyValue = opt.getValue(1);
+     * </pre>
+     *
+     * @param sep The value separator to be used for the argument values.
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withValueSeparator(char sep)
+    {
+        OptionBuilder.valuesep = sep;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created uses '<code>=</code>' as a means to
+     * separate argument values.
+     *
+     * <b>Example:</b>
+     * <pre>
+     * Option opt = OptionBuilder.withValueSeparator()
+     *                           .create('D');
+     *
+     * CommandLine line = parser.parse(args);
+     * String propertyName = opt.getValue(0);
+     * String propertyValue = opt.getValue(1);
+     * </pre>
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withValueSeparator()
+    {
+        OptionBuilder.valuesep = '=';
+
+        return instance;
+    }
+
+    /**
+     * The next Option created will be required if <code>required</code>
+     * is true.
+     *
+     * @param newRequired if true then the Option is required
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder isRequired(boolean newRequired)
+    {
+        OptionBuilder.required = newRequired;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created can have unlimited argument values.
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasArgs()
+    {
+        OptionBuilder.numberOfArgs = Option.UNLIMITED_VALUES;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created can have <code>num</code> argument values.
+     *
+     * @param num the number of args that the option can have
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasArgs(int num)
+    {
+        OptionBuilder.numberOfArgs = num;
+
+        return instance;
+    }
+
+    /**
+     * The next Option can have an optional argument.
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasOptionalArg()
+    {
+        OptionBuilder.numberOfArgs = 1;
+        OptionBuilder.optionalArg = true;
+
+        return instance;
+    }
+
+    /**
+     * The next Option can have an unlimited number of optional arguments.
+     *
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasOptionalArgs()
+    {
+        OptionBuilder.numberOfArgs = Option.UNLIMITED_VALUES;
+        OptionBuilder.optionalArg = true;
+
+        return instance;
+    }
+
+    /**
+     * The next Option can have the specified number of optional arguments.
+     *
+     * @param numArgs - the maximum number of optional arguments
+     * the next Option created can have.
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder hasOptionalArgs(int numArgs)
+    {
+        OptionBuilder.numberOfArgs = numArgs;
+        OptionBuilder.optionalArg = true;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created will have a value that will be an instance
+     * of <code>type</code>.
+     *
+     * @param newType the type of the Options argument value
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withType(Object newType)
+    {
+        OptionBuilder.type = newType;
+
+        return instance;
+    }
+
+    /**
+     * The next Option created will have the specified description
+     *
+     * @param newDescription a description of the Option's purpose
+     * @return the OptionBuilder instance
+     */
+    public static OptionBuilder withDescription(String newDescription)
+    {
+        OptionBuilder.description = newDescription;
+
+        return instance;
+    }
+
+    /**
+     * Create an Option using the current settings and with
+     * the specified Option <code>char</code>.
+     *
+     * @param opt the character representation of the Option
+     * @return the Option instance
+     * @throws IllegalArgumentException if <code>opt</code> is not
+     * a valid character.  See Option.
+     */
+    public static Option create(char opt) throws IllegalArgumentException
+    {
+        return create(String.valueOf(opt));
+    }
+
+    /**
+     * Create an Option using the current settings
+     *
+     * @return the Option instance
+     * @throws IllegalArgumentException if <code>longOpt</code> has not been set.
+     */
+    public static Option create() throws IllegalArgumentException
+    {
+        if (longopt == null)
+        {
+            OptionBuilder.reset();
+            throw new IllegalArgumentException("must specify longopt");
+        }
+
+        return create(null);
+    }
+
+    /**
+     * Create an Option using the current settings and with
+     * the specified Option <code>char</code>.
+     *
+     * @param opt the <code>java.lang.String</code> representation
+     * of the Option
+     * @return the Option instance
+     * @throws IllegalArgumentException if <code>opt</code> is not
+     * a valid character.  See Option.
+     */
+    public static Option create(String opt) throws IllegalArgumentException
+    {
+        Option option = null;
+        try {
+            // create the option
+            option = new Option(opt, description);
+
+            // set the option properties
+            option.setLongOpt(longopt);
+            option.setRequired(required);
+            option.setOptionalArg(optionalArg);
+            option.setArgs(numberOfArgs);
+            option.setType(type);
+            option.setValueSeparator(valuesep);
+            option.setArgName(argName);
+        } finally {
+            // reset the OptionBuilder properties
+            OptionBuilder.reset();
+        }
+
+        // return the Option instance
+        return option;
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionGroup.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionGroup.java
new file mode 100644
index 0000000..b209244
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionGroup.java
@@ -0,0 +1,168 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A group of mutually exclusive options.
+ *
+ * @author John Keyes ( john at integralsource.com )
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class OptionGroup implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+    
+    /** hold the options */
+    private Map optionMap = new HashMap();
+
+    /** the name of the selected option */
+    private String selected;
+
+    /** specified whether this group is required */
+    private boolean required;
+
+    /**
+     * Add the specified <code>Option</code> to this group.
+     *
+     * @param option the option to add to this group
+     * @return this option group with the option added
+     */
+    public OptionGroup addOption(Option option)
+    {
+        // key   - option name
+        // value - the option
+        optionMap.put(option.getKey(), option);
+
+        return this;
+    }
+
+    /**
+     * @return the names of the options in this group as a 
+     * <code>Collection</code>
+     */
+    public Collection getNames()
+    {
+        // the key set is the collection of names
+        return optionMap.keySet();
+    }
+
+    /**
+     * @return the options in this group as a <code>Collection</code>
+     */
+    public Collection getOptions()
+    {
+        // the values are the collection of options
+        return optionMap.values();
+    }
+
+    /**
+     * Set the selected option of this group to <code>name</code>.
+     *
+     * @param option the option that is selected
+     * @throws AlreadySelectedException if an option from this group has 
+     * already been selected.
+     */
+    public void setSelected(Option option) throws AlreadySelectedException
+    {
+        // if no option has already been selected or the 
+        // same option is being reselected then set the
+        // selected member variable
+        if (selected == null || selected.equals(option.getOpt()))
+        {
+            selected = option.getOpt();
+        }
+        else
+        {
+            throw new AlreadySelectedException(this, option);
+        }
+    }
+
+    /**
+     * @return the selected option name
+     */
+    public String getSelected()
+    {
+        return selected;
+    }
+
+    /**
+     * @param required specifies if this group is required
+     */
+    public void setRequired(boolean required)
+    {
+        this.required = required;
+    }
+
+    /**
+     * Returns whether this option group is required.
+     *
+     * @return whether this option group is required
+     */
+    public boolean isRequired()
+    {
+        return required;
+    }
+
+    /**
+     * Returns the stringified version of this OptionGroup.
+     * 
+     * @return the stringified representation of this group
+     */
+    public String toString()
+    {
+        StringBuffer buff = new StringBuffer();
+
+        Iterator iter = getOptions().iterator();
+
+        buff.append("[");
+
+        while (iter.hasNext())
+        {
+            Option option = (Option) iter.next();
+
+            if (option.getOpt() != null)
+            {
+                buff.append("-");
+                buff.append(option.getOpt());
+            }
+            else
+            {
+                buff.append("--");
+                buff.append(option.getLongOpt());
+            }
+
+            buff.append(" ");
+            buff.append(option.getDescription());
+
+            if (iter.hasNext())
+            {
+                buff.append(", ");
+            }
+        }
+
+        buff.append("]");
+
+        return buff.toString();
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionValidator.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionValidator.java
new file mode 100644
index 0000000..a83d125
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/OptionValidator.java
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * Validates an Option string.
+ *
+ * @author John Keyes ( john at integralsource.com )
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ * @since 1.1
+ */
+class OptionValidator
+{
+    /**
+     * Validates whether <code>opt</code> is a permissable Option
+     * shortOpt.  The rules that specify if the <code>opt</code>
+     * is valid are:
+     *
+     * <ul>
+     *  <li><code>opt</code> is not NULL</li>
+     *  <li>a single character <code>opt</code> that is either
+     *  ' '(special case), '?', '@' or a letter</li>
+     *  <li>a multi character <code>opt</code> that only contains
+     *  letters.</li>
+     * </ul>
+     *
+     * @param opt The option string to validate
+     * @throws IllegalArgumentException if the Option is not valid.
+     */
+    static void validateOption(String opt) throws IllegalArgumentException
+    {
+        // check that opt is not NULL
+        if (opt == null)
+        {
+            return;
+        }
+
+        // handle the single character opt
+        else if (opt.length() == 1)
+        {
+            char ch = opt.charAt(0);
+
+            if (!isValidOpt(ch))
+            {
+                throw new IllegalArgumentException("illegal option value '" + ch + "'");
+            }
+        }
+
+        // handle the multi character opt
+        else
+        {
+            char[] chars = opt.toCharArray();
+
+            for (int i = 0; i < chars.length; i++)
+            {
+                if (!isValidChar(chars[i]))
+                {
+                    throw new IllegalArgumentException("opt contains illegal character value '" + chars[i] + "'");
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns whether the specified character is a valid Option.
+     *
+     * @param c the option to validate
+     * @return true if <code>c</code> is a letter, ' ', '?' or '@',
+     *         otherwise false.
+     */
+    private static boolean isValidOpt(char c)
+    {
+        return isValidChar(c) || c == ' ' || c == '?' || c == '@';
+    }
+
+    /**
+     * Returns whether the specified character is a valid character.
+     *
+     * @param c the character to validate
+     * @return true if <code>c</code> is a letter.
+     */
+    private static boolean isValidChar(char c)
+    {
+        return Character.isJavaIdentifierPart(c);
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Options.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Options.java
new file mode 100644
index 0000000..79508f3
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Options.java
@@ -0,0 +1,259 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>Main entry-point into the library.</p>
+ *
+ * <p>Options represents a collection of {@link Option} objects, which
+ * describe the possible options for a command-line.<p>
+ *
+ * <p>It may flexibly parse long and short options, with or without
+ * values.  Additionally, it may parse only a portion of a commandline,
+ * allowing for flexible multi-stage parsing.<p>
+ *
+ * @see org.apache.commons.cli.CommandLine
+ *
+ * @author bob mcwhirter (bob @ werken.com)
+ * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class Options implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** a map of the options with the character key */
+    private Map shortOpts = new HashMap();
+
+    /** a map of the options with the long key */
+    private Map longOpts = new HashMap();
+
+    /** a map of the required options */
+    private List requiredOpts = new ArrayList();
+
+    /** a map of the option groups */
+    private Map optionGroups = new HashMap();
+
+    /**
+     * Add the specified option group.
+     *
+     * @param group the OptionGroup that is to be added
+     * @return the resulting Options instance
+     */
+    public Options addOptionGroup(OptionGroup group)
+    {
+        Iterator options = group.getOptions().iterator();
+
+        if (group.isRequired())
+        {
+            requiredOpts.add(group);
+        }
+
+        while (options.hasNext())
+        {
+            Option option = (Option) options.next();
+
+            // an Option cannot be required if it is in an
+            // OptionGroup, either the group is required or
+            // nothing is required
+            option.setRequired(false);
+            addOption(option);
+
+            optionGroups.put(option.getKey(), group);
+        }
+
+        return this;
+    }
+
+    /**
+     * Lists the OptionGroups that are members of this Options instance.
+     *
+     * @return a Collection of OptionGroup instances.
+     */
+    Collection getOptionGroups()
+    {
+        return new HashSet(optionGroups.values());
+    }
+
+    /**
+     * Add an option that only contains a short-name.
+     * It may be specified as requiring an argument.
+     *
+     * @param opt Short single-character name of the option.
+     * @param hasArg flag signally if an argument is required after this option
+     * @param description Self-documenting description
+     * @return the resulting Options instance
+     */
+    public Options addOption(String opt, boolean hasArg, String description)
+    {
+        addOption(opt, null, hasArg, description);
+
+        return this;
+    }
+
+    /**
+     * Add an option that contains a short-name and a long-name.
+     * It may be specified as requiring an argument.
+     *
+     * @param opt Short single-character name of the option.
+     * @param longOpt Long multi-character name of the option.
+     * @param hasArg flag signally if an argument is required after this option
+     * @param description Self-documenting description
+     * @return the resulting Options instance
+     */
+    public Options addOption(String opt, String longOpt, boolean hasArg, String description)
+    {
+        addOption(new Option(opt, longOpt, hasArg, description));
+
+        return this;
+    }
+
+    /**
+     * Adds an option instance
+     *
+     * @param opt the option that is to be added
+     * @return the resulting Options instance
+     */
+    public Options addOption(Option opt)
+    {
+        String key = opt.getKey();
+
+        // add it to the long option list
+        if (opt.hasLongOpt())
+        {
+            longOpts.put(opt.getLongOpt(), opt);
+        }
+
+        // if the option is required add it to the required list
+        if (opt.isRequired())
+        {
+            if (requiredOpts.contains(key))
+            {
+                requiredOpts.remove(requiredOpts.indexOf(key));
+            }
+            requiredOpts.add(key);
+        }
+
+        shortOpts.put(key, opt);
+
+        return this;
+    }
+
+    /**
+     * Retrieve a read-only list of options in this set
+     *
+     * @return read-only Collection of {@link Option} objects in this descriptor
+     */
+    public Collection getOptions()
+    {
+        return Collections.unmodifiableCollection(helpOptions());
+    }
+
+    /**
+     * Returns the Options for use by the HelpFormatter.
+     *
+     * @return the List of Options
+     */
+    List helpOptions()
+    {
+        return new ArrayList(shortOpts.values());
+    }
+
+    /**
+     * Returns the required options.
+     *
+     * @return List of required options
+     */
+    public List getRequiredOptions()
+    {
+        return requiredOpts;
+    }
+
+    /**
+     * Retrieve the {@link Option} matching the long or short name specified.
+     * The leading hyphens in the name are ignored (up to 2).
+     *
+     * @param opt short or long name of the {@link Option}
+     * @return the option represented by opt
+     */
+    public Option getOption(String opt)
+    {
+        opt = Util.stripLeadingHyphens(opt);
+
+        if (shortOpts.containsKey(opt))
+        {
+            return (Option) shortOpts.get(opt);
+        }
+
+        return (Option) longOpts.get(opt);
+    }
+
+    /**
+     * Returns whether the named {@link Option} is a member of this {@link Options}.
+     *
+     * @param opt short or long name of the {@link Option}
+     * @return true if the named {@link Option} is a member
+     * of this {@link Options}
+     */
+    public boolean hasOption(String opt)
+    {
+        opt = Util.stripLeadingHyphens(opt);
+
+        return shortOpts.containsKey(opt) || longOpts.containsKey(opt);
+    }
+
+    /**
+     * Returns the OptionGroup the <code>opt</code> belongs to.
+     * @param opt the option whose OptionGroup is being queried.
+     *
+     * @return the OptionGroup if <code>opt</code> is part
+     * of an OptionGroup, otherwise return null
+     */
+    public OptionGroup getOptionGroup(Option opt)
+    {
+        return (OptionGroup) optionGroups.get(opt.getKey());
+    }
+
+    /**
+     * Dump state, suitable for debugging.
+     *
+     * @return Stringified form of this object
+     */
+    public String toString()
+    {
+        StringBuffer buf = new StringBuffer();
+
+        buf.append("[ Options: [ short ");
+        buf.append(shortOpts.toString());
+        buf.append(" ] [ long ");
+        buf.append(longOpts);
+        buf.append(" ]");
+
+        return buf.toString();
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/ParseException.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/ParseException.java
new file mode 100644
index 0000000..94620e2
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/ParseException.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * Base for Exceptions thrown during parsing of a command-line.
+ *
+ * @author bob mcwhirter (bob @ werken.com)
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class ParseException extends Exception
+{
+    /**
+     * Construct a new <code>ParseException</code>
+     * with the specified detail message.
+     *
+     * @param message the detail message
+     */
+    public ParseException(String message)
+    {
+        super(message);
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Parser.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Parser.java
new file mode 100644
index 0000000..73fd762
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Parser.java
@@ -0,0 +1,399 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Properties;
+
+/**
+ * <code>Parser</code> creates {@link CommandLine}s.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public abstract class Parser implements CommandLineParser
+{
+    /** commandline instance */
+    protected CommandLine cmd;
+
+    /** current Options */
+    private Options options;
+
+    /** list of required options strings */
+    private List requiredOptions;
+
+    protected void setOptions(final Options options)
+    {
+        this.options = options;
+        this.requiredOptions = new ArrayList(options.getRequiredOptions());
+    }
+
+    protected Options getOptions()
+    {
+        return options;
+    }
+
+    protected List getRequiredOptions()
+    {
+        return requiredOptions;
+    }
+
+    /**
+     * Subclasses must implement this method to reduce
+     * the <code>arguments</code> that have been passed to the parse method.
+     *
+     * @param opts The Options to parse the arguments by.
+     * @param arguments The arguments that have to be flattened.
+     * @param stopAtNonOption specifies whether to stop
+     * flattening when a non option has been encountered
+     * @return a String array of the flattened arguments
+     */
+    protected abstract String[] flatten(Options opts, String[] arguments, boolean stopAtNonOption);
+
+    /**
+     * Parses the specified <code>arguments</code> based
+     * on the specifed {@link Options}.
+     *
+     * @param options the <code>Options</code>
+     * @param arguments the <code>arguments</code>
+     * @return the <code>CommandLine</code>
+     * @throws ParseException if an error occurs when parsing the
+     * arguments.
+     */
+    public CommandLine parse(Options options, String[] arguments) throws ParseException
+    {
+        return parse(options, arguments, null, false);
+    }
+
+    /**
+     * Parse the arguments according to the specified options and properties.
+     *
+     * @param options    the specified Options
+     * @param arguments  the command line arguments
+     * @param properties command line option name-value pairs
+     * @return the list of atomic option and value tokens
+     * @throws ParseException if there are any problems encountered
+     *                        while parsing the command line tokens.
+     *
+     * @since 1.1
+     */
+    public CommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException
+    {
+        return parse(options, arguments, properties, false);
+    }
+
+    /**
+     * Parses the specified <code>arguments</code>
+     * based on the specifed {@link Options}.
+     *
+     * @param options         the <code>Options</code>
+     * @param arguments       the <code>arguments</code>
+     * @param stopAtNonOption specifies whether to stop interpreting the
+     *                        arguments when a non option has been encountered
+     *                        and to add them to the CommandLines args list.
+     * @return the <code>CommandLine</code>
+     * @throws ParseException if an error occurs when parsing the arguments.
+     */
+    public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException
+    {
+        return parse(options, arguments, null, stopAtNonOption);
+    }
+
+    /**
+     * Parse the arguments according to the specified options and
+     * properties.
+     *
+     * @param options the specified Options
+     * @param arguments the command line arguments
+     * @param properties command line option name-value pairs
+     * @param stopAtNonOption stop parsing the arguments when the first
+     * non option is encountered.
+     *
+     * @return the list of atomic option and value tokens
+     *
+     * @throws ParseException if there are any problems encountered
+     * while parsing the command line tokens.
+     *
+     * @since 1.1
+     */
+    public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
+            throws ParseException
+    {
+        // clear out the data in options in case it's been used before (CLI-71)
+        for (Iterator it = options.helpOptions().iterator(); it.hasNext();)
+        {
+            Option opt = (Option) it.next();
+            opt.clearValues();
+        }
+
+        // initialise members
+        setOptions(options);
+
+        cmd = new CommandLine();
+
+        boolean eatTheRest = false;
+
+        if (arguments == null)
+        {
+            arguments = new String[0];
+        }
+
+        List tokenList = Arrays.asList(flatten(getOptions(), arguments, stopAtNonOption));
+
+        ListIterator iterator = tokenList.listIterator();
+
+        // process each flattened token
+        while (iterator.hasNext())
+        {
+            String t = (String) iterator.next();
+
+            // the value is the double-dash
+            if ("--".equals(t))
+            {
+                eatTheRest = true;
+            }
+
+            // the value is a single dash
+            else if ("-".equals(t))
+            {
+                if (stopAtNonOption)
+                {
+                    eatTheRest = true;
+                }
+                else
+                {
+                    cmd.addArg(t);
+                }
+            }
+
+            // the value is an option
+            else if (t.startsWith("-"))
+            {
+                if (stopAtNonOption && !getOptions().hasOption(t))
+                {
+                    eatTheRest = true;
+                    cmd.addArg(t);
+                }
+                else
+                {
+                    processOption(t, iterator);
+                }
+            }
+
+            // the value is an argument
+            else
+            {
+                cmd.addArg(t);
+
+                if (stopAtNonOption)
+                {
+                    eatTheRest = true;
+                }
+            }
+
+            // eat the remaining tokens
+            if (eatTheRest)
+            {
+                while (iterator.hasNext())
+                {
+                    String str = (String) iterator.next();
+
+                    // ensure only one double-dash is added
+                    if (!"--".equals(str))
+                    {
+                        cmd.addArg(str);
+                    }
+                }
+            }
+        }
+
+        processProperties(properties);
+        checkRequiredOptions();
+
+        return cmd;
+    }
+
+    /**
+     * Sets the values of Options using the values in <code>properties</code>.
+     *
+     * @param properties The value properties to be processed.
+     */
+    protected void processProperties(Properties properties)
+    {
+        if (properties == null)
+        {
+            return;
+        }
+
+        for (Enumeration e = properties.propertyNames(); e.hasMoreElements();)
+        {
+            String option = e.nextElement().toString();
+
+            if (!cmd.hasOption(option))
+            {
+                Option opt = getOptions().getOption(option);
+
+                // get the value from the properties instance
+                String value = properties.getProperty(option);
+
+                if (opt.hasArg())
+                {
+                    if (opt.getValues() == null || opt.getValues().length == 0)
+                    {
+                        try
+                        {
+                            opt.addValueForProcessing(value);
+                        }
+                        catch (RuntimeException exp)
+                        {
+                            // if we cannot add the value don't worry about it
+                        }
+                    }
+                }
+                else if (!("yes".equalsIgnoreCase(value)
+                        || "true".equalsIgnoreCase(value)
+                        || "1".equalsIgnoreCase(value)))
+                {
+                    // if the value is not yes, true or 1 then don't add the
+                    // option to the CommandLine
+                    break;
+                }
+
+                cmd.addOption(opt);
+            }
+        }
+    }
+
+    /**
+     * Throws a {@link MissingOptionException} if all of the required options
+     * are not present.
+     *
+     * @throws MissingOptionException if any of the required Options
+     * are not present.
+     */
+    protected void checkRequiredOptions() throws MissingOptionException
+    {
+        // if there are required options that have not been processsed
+        if (!getRequiredOptions().isEmpty())
+        {
+            throw new MissingOptionException(getRequiredOptions());
+        }
+    }
+
+    /**
+     * <p>Process the argument values for the specified Option
+     * <code>opt</code> using the values retrieved from the
+     * specified iterator <code>iter</code>.
+     *
+     * @param opt The current Option
+     * @param iter The iterator over the flattened command line
+     * Options.
+     *
+     * @throws ParseException if an argument value is required
+     * and it is has not been found.
+     */
+    public void processArgs(Option opt, ListIterator iter) throws ParseException
+    {
+        // loop until an option is found
+        while (iter.hasNext())
+        {
+            String str = (String) iter.next();
+
+            // found an Option, not an argument
+            if (getOptions().hasOption(str) && str.startsWith("-"))
+            {
+                iter.previous();
+                break;
+            }
+
+            // found a value
+            try
+            {
+                opt.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(str));
+            }
+            catch (RuntimeException exp)
+            {
+                iter.previous();
+                break;
+            }
+        }
+
+        if (opt.getValues() == null && !opt.hasOptionalArg())
+        {
+            throw new MissingArgumentException(opt);
+        }
+    }
+
+    /**
+     * Process the Option specified by <code>arg</code> using the values
+     * retrieved from the specfied iterator <code>iter</code>.
+     *
+     * @param arg The String value representing an Option
+     * @param iter The iterator over the flattened command line arguments.
+     *
+     * @throws ParseException if <code>arg</code> does not represent an Option
+     */
+    protected void processOption(String arg, ListIterator iter) throws ParseException
+    {
+        boolean hasOption = getOptions().hasOption(arg);
+
+        // if there is no option throw an UnrecognisedOptionException
+        if (!hasOption)
+        {
+            throw new UnrecognizedOptionException("Unrecognized option: " + arg, arg);
+        }
+
+        // get the option represented by arg
+        Option opt = (Option) getOptions().getOption(arg).clone();
+
+        // if the option is a required option remove the option from
+        // the requiredOptions list
+        if (opt.isRequired())
+        {
+            getRequiredOptions().remove(opt.getKey());
+        }
+
+        // if the option is in an OptionGroup make that option the selected
+        // option of the group
+        if (getOptions().getOptionGroup(opt) != null)
+        {
+            OptionGroup group = getOptions().getOptionGroup(opt);
+
+            if (group.isRequired())
+            {
+                getRequiredOptions().remove(group);
+            }
+
+            group.setSelected(opt);
+        }
+
+        // if the option takes an argument value
+        if (opt.hasArg())
+        {
+            processArgs(opt, iter);
+        }
+
+        // set the option on the command line
+        cmd.addOption(opt);
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/PatternOptionBuilder.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/PatternOptionBuilder.java
new file mode 100644
index 0000000..34a0662
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/PatternOptionBuilder.java
@@ -0,0 +1,201 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.net.URL;
+import java.util.Date;
+
+/**
+ * <p>
+ * Allows Options to be created from a single String.
+ * The pattern contains various single character flags and via
+ * an optional punctuation character, their expected type.
+ * </p>
+ *
+ * <table border="1">
+ * <tr><td>a</td><td>-a flag</td></tr>
+ * <tr><td>b@</td><td>-b [classname]</td></tr>
+ * <tr><td>c&gt;</td><td>-c [filename]</td></tr>
+ * <tr><td>d+</td><td>-d [classname] (creates object via empty contructor)</td></tr>
+ * <tr><td>e%</td><td>-e [number] (creates Double/Long instance depeding on existing of a '.')</td></tr>
+ * <tr><td>f/</td><td>-f [url]</td></tr>
+ * <tr><td>g:</td><td>-g [string]</td></tr>
+ * </table>
+ *
+ * <p>
+ * For example, the following allows command line flags of '-v -p string-value -f /dir/file'.
+ * The exclamation mark precede a mandatory option.
+ * </p>
+ * <code>Options options = PatternOptionBuilder.parsePattern("vp:!f/");</code>
+ *
+ * <p>
+ * TODO These need to break out to OptionType and also
+ * to be pluggable.
+ * </p>
+ *
+ * @version $Revision: 734339 $, $Date: 2009-01-13 21:56:47 -0800 (Tue, 13 Jan 2009) $
+ */
+public class PatternOptionBuilder
+{
+    /** String class */
+    public static final Class STRING_VALUE = String.class;
+
+    /** Object class */
+    public static final Class OBJECT_VALUE = Object.class;
+
+    /** Number class */
+    public static final Class NUMBER_VALUE = Number.class;
+
+    /** Date class */
+    public static final Class DATE_VALUE = Date.class;
+
+    /** Class class */
+    public static final Class CLASS_VALUE = Class.class;
+
+    /// can we do this one??
+    // is meant to check that the file exists, else it errors.
+    // ie) it's for reading not writing.
+
+    /** FileInputStream class */
+    public static final Class EXISTING_FILE_VALUE = FileInputStream.class;
+
+    /** File class */
+    public static final Class FILE_VALUE = File.class;
+
+    /** File array class */
+    public static final Class FILES_VALUE = File[].class;
+
+    /** URL class */
+    public static final Class URL_VALUE = URL.class;
+
+    /**
+     * Retrieve the class that <code>ch</code> represents.
+     *
+     * @param ch the specified character
+     * @return The class that <code>ch</code> represents
+     */
+    public static Object getValueClass(char ch)
+    {
+        switch (ch)
+        {
+            case '@':
+                return PatternOptionBuilder.OBJECT_VALUE;
+            case ':':
+                return PatternOptionBuilder.STRING_VALUE;
+            case '%':
+                return PatternOptionBuilder.NUMBER_VALUE;
+            case '+':
+                return PatternOptionBuilder.CLASS_VALUE;
+            case '#':
+                return PatternOptionBuilder.DATE_VALUE;
+            case '<':
+                return PatternOptionBuilder.EXISTING_FILE_VALUE;
+            case '>':
+                return PatternOptionBuilder.FILE_VALUE;
+            case '*':
+                return PatternOptionBuilder.FILES_VALUE;
+            case '/':
+                return PatternOptionBuilder.URL_VALUE;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns whether <code>ch</code> is a value code, i.e.
+     * whether it represents a class in a pattern.
+     *
+     * @param ch the specified character
+     * @return true if <code>ch</code> is a value code, otherwise false.
+     */
+    public static boolean isValueCode(char ch)
+    {
+        return ch == '@'
+                || ch == ':'
+                || ch == '%'
+                || ch == '+'
+                || ch == '#'
+                || ch == '<'
+                || ch == '>'
+                || ch == '*'
+                || ch == '/'
+                || ch == '!';
+    }
+
+    /**
+     * Returns the {@link Options} instance represented by <code>pattern</code>.
+     *
+     * @param pattern the pattern string
+     * @return The {@link Options} instance
+     */
+    public static Options parsePattern(String pattern)
+    {
+        char opt = ' ';
+        boolean required = false;
+        Object type = null;
+
+        Options options = new Options();
+
+        for (int i = 0; i < pattern.length(); i++)
+        {
+            char ch = pattern.charAt(i);
+
+            // a value code comes after an option and specifies
+            // details about it
+            if (!isValueCode(ch))
+            {
+                if (opt != ' ')
+                {
+                    OptionBuilder.hasArg(type != null);
+                    OptionBuilder.isRequired(required);
+                    OptionBuilder.withType(type);
+
+                    // we have a previous one to deal with
+                    options.addOption(OptionBuilder.create(opt));
+                    required = false;
+                    type = null;
+                    opt = ' ';
+                }
+
+                opt = ch;
+            }
+            else if (ch == '!')
+            {
+                required = true;
+            }
+            else
+            {
+                type = getValueClass(ch);
+            }
+        }
+
+        if (opt != ' ')
+        {
+            OptionBuilder.hasArg(type != null);
+            OptionBuilder.isRequired(required);
+            OptionBuilder.withType(type);
+
+            // we have a final one to deal with
+            options.addOption(OptionBuilder.create(opt));
+        }
+
+        return options;
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/PosixParser.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/PosixParser.java
new file mode 100644
index 0000000..bc0a846
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/PosixParser.java
@@ -0,0 +1,276 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The class PosixParser provides an implementation of the
+ * {@link Parser#flatten(Options,String[],boolean) flatten} method.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 695760 $, $Date: 2008-09-16 01:05:03 -0700 (Tue, 16 Sep 2008) $
+ */
+public class PosixParser extends Parser
+{
+    /** holder for flattened tokens */
+    private List tokens = new ArrayList();
+
+    /** specifies if bursting should continue */
+    private boolean eatTheRest;
+
+    /** holder for the current option */
+    private Option currentOption;
+
+    /** the command line Options */
+    private Options options;
+
+    /**
+     * Resets the members to their original state i.e. remove
+     * all of <code>tokens</code> entries and set <code>eatTheRest</code>
+     * to false.
+     */
+    private void init()
+    {
+        eatTheRest = false;
+        tokens.clear();
+    }
+
+    /**
+     * <p>An implementation of {@link Parser}'s abstract
+     * {@link Parser#flatten(Options,String[],boolean) flatten} method.</p>
+     *
+     * <p>The following are the rules used by this flatten method.
+     * <ol>
+     *  <li>if <code>stopAtNonOption</code> is <b>true</b> then do not
+     *  burst anymore of <code>arguments</code> entries, just add each
+     *  successive entry without further processing.  Otherwise, ignore
+     *  <code>stopAtNonOption</code>.</li>
+     *  <li>if the current <code>arguments</code> entry is "<b>--</b>"
+     *  just add the entry to the list of processed tokens</li>
+     *  <li>if the current <code>arguments</code> entry is "<b>-</b>"
+     *  just add the entry to the list of processed tokens</li>
+     *  <li>if the current <code>arguments</code> entry is two characters
+     *  in length and the first character is "<b>-</b>" then check if this
+     *  is a valid {@link Option} id.  If it is a valid id, then add the
+     *  entry to the list of processed tokens and set the current {@link Option}
+     *  member.  If it is not a valid id and <code>stopAtNonOption</code>
+     *  is true, then the remaining entries are copied to the list of
+     *  processed tokens.  Otherwise, the current entry is ignored.</li>
+     *  <li>if the current <code>arguments</code> entry is more than two
+     *  characters in length and the first character is "<b>-</b>" then
+     *  we need to burst the entry to determine its constituents.  For more
+     *  information on the bursting algorithm see
+     *  {@link PosixParser#burstToken(String, boolean) burstToken}.</li>
+     *  <li>if the current <code>arguments</code> entry is not handled
+     *  by any of the previous rules, then the entry is added to the list
+     *  of processed tokens.</li>
+     * </ol>
+     * </p>
+     *
+     * @param options The command line {@link Options}
+     * @param arguments The command line arguments to be parsed
+     * @param stopAtNonOption Specifies whether to stop flattening
+     * when an non option is found.
+     * @return The flattened <code>arguments</code> String array.
+     */
+    protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption)
+    {
+        init();
+        this.options = options;
+
+        // an iterator for the command line tokens
+        Iterator iter = Arrays.asList(arguments).iterator();
+
+        // process each command line token
+        while (iter.hasNext())
+        {
+            // get the next command line token
+            String token = (String) iter.next();
+
+            // handle long option --foo or --foo=bar
+            if (token.startsWith("--"))
+            {
+                int pos = token.indexOf('=');
+                String opt = pos == -1 ? token : token.substring(0, pos); // --foo
+
+                if (!options.hasOption(opt))
+                {
+                    processNonOptionToken(token, stopAtNonOption);
+                }
+                else
+                {
+                    currentOption = options.getOption(opt);
+                    
+                    tokens.add(opt);
+                    if (pos != -1)
+                    {
+                        tokens.add(token.substring(pos + 1));
+                    }
+                }
+            }
+
+            // single hyphen
+            else if ("-".equals(token))
+            {
+                tokens.add(token);
+            }
+            else if (token.startsWith("-"))
+            {
+                if (token.length() == 2 || options.hasOption(token))
+                {
+                    processOptionToken(token, stopAtNonOption);
+                }
+                // requires bursting
+                else
+                {
+                    burstToken(token, stopAtNonOption);
+                }
+            }
+            else
+            {
+                processNonOptionToken(token, stopAtNonOption);
+            }
+
+            gobble(iter);
+        }
+
+        return (String[]) tokens.toArray(new String[tokens.size()]);
+    }
+
+    /**
+     * Adds the remaining tokens to the processed tokens list.
+     *
+     * @param iter An iterator over the remaining tokens
+     */
+    private void gobble(Iterator iter)
+    {
+        if (eatTheRest)
+        {
+            while (iter.hasNext())
+            {
+                tokens.add(iter.next());
+            }
+        }
+    }
+
+    /**
+     * Add the special token "<b>--</b>" and the current <code>value</code>
+     * to the processed tokens list. Then add all the remaining
+     * <code>argument</code> values to the processed tokens list.
+     *
+     * @param value The current token
+     */
+    private void processNonOptionToken(String value, boolean stopAtNonOption)
+    {
+        if (stopAtNonOption && (currentOption == null || !currentOption.hasArg()))
+        {
+            eatTheRest = true;
+            tokens.add("--");
+        }
+
+        tokens.add(value);
+    }
+
+    /**
+     * <p>If an {@link Option} exists for <code>token</code> then
+     * add the token to the processed list.</p>
+     *
+     * <p>If an {@link Option} does not exist and <code>stopAtNonOption</code>
+     * is set then add the remaining tokens to the processed tokens list
+     * directly.</p>
+     *
+     * @param token The current option token
+     * @param stopAtNonOption Specifies whether flattening should halt
+     * at the first non option.
+     */
+    private void processOptionToken(String token, boolean stopAtNonOption)
+    {
+        if (stopAtNonOption && !options.hasOption(token))
+        {
+            eatTheRest = true;
+        }
+
+        if (options.hasOption(token))
+        {
+            currentOption = options.getOption(token);
+        }
+
+        tokens.add(token);
+    }
+
+    /**
+     * Breaks <code>token</code> into its constituent parts
+     * using the following algorithm.
+     *
+     * <ul>
+     *  <li>ignore the first character ("<b>-</b>")</li>
+     *  <li>foreach remaining character check if an {@link Option}
+     *  exists with that id.</li>
+     *  <li>if an {@link Option} does exist then add that character
+     *  prepended with "<b>-</b>" to the list of processed tokens.</li>
+     *  <li>if the {@link Option} can have an argument value and there
+     *  are remaining characters in the token then add the remaining
+     *  characters as a token to the list of processed tokens.</li>
+     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
+     *  <code>stopAtNonOption</code> <b>IS</b> set then add the special token
+     *  "<b>--</b>" followed by the remaining characters and also
+     *  the remaining tokens directly to the processed tokens list.</li>
+     *  <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
+     *  <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
+     *  character prepended with "<b>-</b>".</li>
+     * </ul>
+     *
+     * @param token The current token to be <b>burst</b>
+     * @param stopAtNonOption Specifies whether to stop processing
+     * at the first non-Option encountered.
+     */
+    protected void burstToken(String token, boolean stopAtNonOption)
+    {
+        for (int i = 1; i < token.length(); i++)
+        {
+            String ch = String.valueOf(token.charAt(i));
+
+            if (options.hasOption(ch))
+            {
+                tokens.add("-" + ch);
+                currentOption = options.getOption(ch);
+
+                if (currentOption.hasArg() && (token.length() != (i + 1)))
+                {
+                    tokens.add(token.substring(i + 1));
+
+                    break;
+                }
+            }
+            else if (stopAtNonOption)
+            {
+                processNonOptionToken(token.substring(i), true);
+                break;
+            }
+            else
+            {
+                tokens.add(token);
+                break;
+            }
+        }
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/TypeHandler.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/TypeHandler.java
new file mode 100644
index 0000000..8f87762
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/TypeHandler.java
@@ -0,0 +1,246 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+import java.io.File;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import java.util.Date;
+
+/**
+  * This is a temporary implementation. TypeHandler will handle the
+  * pluggableness of OptionTypes and it will direct all of these types
+  * of conversion functionalities to ConvertUtils component in Commons
+  * already. BeanUtils I think.
+  *
+  * @version $Revision: 741425 $, $Date: 2009-02-05 22:10:54 -0800 (Thu, 05 Feb 2009) $
+  */
+public class TypeHandler
+{
+    /**
+     * Returns the <code>Object</code> of type <code>obj</code>
+     * with the value of <code>str</code>.
+     *
+     * @param str the command line value
+     * @param obj the type of argument
+     * @return The instance of <code>obj</code> initialised with
+     * the value of <code>str</code>.
+     */
+    public static Object createValue(String str, Object obj)
+    throws ParseException
+    {
+        return createValue(str, (Class) obj);
+    }
+
+    /**
+     * Returns the <code>Object</code> of type <code>clazz</code>
+     * with the value of <code>str</code>.
+     *
+     * @param str the command line value
+     * @param clazz the type of argument
+     * @return The instance of <code>clazz</code> initialised with
+     * the value of <code>str</code>.
+     */
+    public static Object createValue(String str, Class clazz)
+    throws ParseException
+    {
+        if (PatternOptionBuilder.STRING_VALUE == clazz)
+        {
+            return str;
+        }
+        else if (PatternOptionBuilder.OBJECT_VALUE == clazz)
+        {
+            return createObject(str);
+        }
+        else if (PatternOptionBuilder.NUMBER_VALUE == clazz)
+        {
+            return createNumber(str);
+        }
+        else if (PatternOptionBuilder.DATE_VALUE == clazz)
+        {
+            return createDate(str);
+        }
+        else if (PatternOptionBuilder.CLASS_VALUE == clazz)
+        {
+            return createClass(str);
+        }
+        else if (PatternOptionBuilder.FILE_VALUE == clazz)
+        {
+            return createFile(str);
+        }
+        else if (PatternOptionBuilder.EXISTING_FILE_VALUE == clazz)
+        {
+            return createFile(str);
+        }
+        else if (PatternOptionBuilder.FILES_VALUE == clazz)
+        {
+            return createFiles(str);
+        }
+        else if (PatternOptionBuilder.URL_VALUE == clazz)
+        {
+            return createURL(str);
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    /**
+      * Create an Object from the classname and empty constructor.
+      *
+      * @param classname the argument value
+      * @return the initialised object, or null if it couldn't create
+      * the Object.
+      */
+    public static Object createObject(String classname)
+    throws ParseException
+    {
+        Class cl = null;
+
+        try
+        {
+            cl = Class.forName(classname);
+        }
+        catch (ClassNotFoundException cnfe)
+        {
+            throw new ParseException("Unable to find the class: " + classname);
+        }
+
+        Object instance = null;
+
+        try
+        {
+            instance = cl.newInstance();
+        }
+        catch (Exception e)
+        {
+            throw new ParseException(e.getClass().getName() + "; Unable to create an instance of: " + classname);
+        }
+
+        return instance;
+    }
+
+    /**
+     * Create a number from a String. If a . is present, it creates a
+     * Double, otherwise a Long.
+     *
+     * @param str the value
+     * @return the number represented by <code>str</code>, if <code>str</code>
+     * is not a number, null is returned.
+     */
+    public static Number createNumber(String str)
+    throws ParseException
+    {
+        try
+        {
+            if (str.indexOf('.') != -1)
+            {
+                return Double.valueOf(str);
+            }
+            else
+            {
+                return Long.valueOf(str);
+            }
+        }
+        catch (NumberFormatException e)
+        {
+            throw new ParseException(e.getMessage());
+        }
+    }
+
+    /**
+     * Returns the class whose name is <code>classname</code>.
+     *
+     * @param classname the class name
+     * @return The class if it is found, otherwise return null
+     */
+    public static Class createClass(String classname)
+    throws ParseException
+    {
+        try
+        {
+            return Class.forName(classname);
+        }
+        catch (ClassNotFoundException e)
+        {
+            throw new ParseException("Unable to find the class: " + classname);
+        }
+    }
+
+    /**
+     * Returns the date represented by <code>str</code>.
+     *
+     * @param str the date string
+     * @return The date if <code>str</code> is a valid date string,
+     * otherwise return null.
+     */
+    public static Date createDate(String str)
+    throws ParseException
+    {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    /**
+     * Returns the URL represented by <code>str</code>.
+     *
+     * @param str the URL string
+     * @return The URL is <code>str</code> is well-formed, otherwise
+     * return null.
+     */
+    public static URL createURL(String str)
+    throws ParseException
+    {
+        try
+        {
+            return new URL(str);
+        }
+        catch (MalformedURLException e)
+        {
+            throw new ParseException("Unable to parse the URL: " + str);
+        }
+    }
+
+    /**
+     * Returns the File represented by <code>str</code>.
+     *
+     * @param str the File location
+     * @return The file represented by <code>str</code>.
+     */
+    public static File createFile(String str)
+    throws ParseException
+    {
+        return new File(str);
+    }
+
+    /**
+     * Returns the File[] represented by <code>str</code>.
+     *
+     * @param str the paths to the files
+     * @return The File[] represented by <code>str</code>.
+     */
+    public static File[] createFiles(String str)
+    throws ParseException
+    {
+        // to implement/port:
+        //        return FileW.findFiles(str);
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/UnrecognizedOptionException.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/UnrecognizedOptionException.java
new file mode 100644
index 0000000..8cb49c9
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/UnrecognizedOptionException.java
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * Exception thrown during parsing signalling an unrecognized
+ * option was seen.
+ *
+ * @author bob mcwhiter (bob @ werken.com)
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+public class UnrecognizedOptionException extends ParseException
+{
+    /** The  unrecognized option */
+    private String option;
+
+    /**
+     * Construct a new <code>UnrecognizedArgumentException</code>
+     * with the specified detail message.
+     *
+     * @param message the detail message
+     */
+    public UnrecognizedOptionException(String message)
+    {
+        super(message);
+    }
+
+    /**
+     * Construct a new <code>UnrecognizedArgumentException</code>
+     * with the specified option and detail message.
+     *
+     * @param message the detail message
+     * @param option  the unrecognized option
+     * @since 1.2
+     */
+    public UnrecognizedOptionException(String message, String option)
+    {
+        this(message);
+        this.option = option;
+    }
+
+    /**
+     * Returns the unrecognized option.
+     *
+     * @return the related option
+     * @since 1.2
+     */
+    public String getOption()
+    {
+        return option;
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Util.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Util.java
new file mode 100644
index 0000000..7d18e3b
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/Util.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.felix.sigil.common.runtime.cli;
+
+/**
+ * Contains useful helper methods for classes within this package.
+ *
+ * @author John Keyes (john at integralsource.com)
+ * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
+ */
+class Util
+{
+    /**
+     * Remove the hyphens from the begining of <code>str</code> and
+     * return the new String.
+     *
+     * @param str The string from which the hyphens should be removed.
+     *
+     * @return the new String.
+     */
+    static String stripLeadingHyphens(String str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        if (str.startsWith("--"))
+        {
+            return str.substring(2, str.length());
+        }
+        else if (str.startsWith("-"))
+        {
+            return str.substring(1, str.length());
+        }
+
+        return str;
+    }
+
+    /**
+     * Remove the leading and trailing quotes from <code>str</code>.
+     * E.g. if str is '"one two"', then 'one two' is returned.
+     *
+     * @param str The string from which the leading and trailing quotes
+     * should be removed.
+     *
+     * @return The string without the leading and trailing quotes.
+     */
+    static String stripLeadingAndTrailingQuotes(String str)
+    {
+        if (str.startsWith("\""))
+        {
+            str = str.substring(1, str.length());
+        }
+        if (str.endsWith("\""))
+        {
+            str = str.substring(0, str.length() - 1);
+        }
+        return str;
+    }
+}
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/overview.html b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/overview.html
new file mode 100644
index 0000000..eec9ce7
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/overview.html
@@ -0,0 +1,43 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<body>
+
+    <p>Commons CLI -- version 1.2</p>
+
+    <p>The commons-cli package aides in parsing command-line arguments.</p>
+
+    <p>Allow command-line arguments to be parsed against a descriptor of
+    valid options (long and short), potentially with arguments.</p>
+
+    <p>command-line arguments may be of the typical <code>String[]</code>
+    form, but also may be a <code>java.util.List</code>.  Indexes allow
+    for parsing only a portion of the command-line.  Also, functionality
+    for parsing the command-line in phases is built in, allowing for
+    'cvs-style' command-lines, where some global options are specified
+    before a 'command' argument, and command-specific options are
+    specified after the command argument:
+
+    <code>
+    <pre>
+        myApp -p &lt;port&gt; command -p &lt;printer&gt;
+    </pre>
+    </code>
+
+
+    <p>The homepage for the project is
+    <a href="http://commons.apache.org">Apache Commons/</a>
+</body>
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/package.html b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/package.html
new file mode 100644
index 0000000..20ebdde
--- /dev/null
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/cli/package.html
@@ -0,0 +1,21 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<body>
+
+    Commons CLI 1.2
+
+</body>
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/io/Action.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/io/Action.java
index abf0c94..298fc0f 100644
--- a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/io/Action.java
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/io/Action.java
@@ -23,14 +23,10 @@
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.net.URLConnection;
 
 import org.osgi.framework.BundleException;
 import org.osgi.framework.launch.Framework;
 
-import org.apache.commons.io.input.CountingInputStream;
 import org.apache.felix.sigil.common.runtime.Main;
 
 import static org.apache.felix.sigil.common.runtime.io.Constants.OK;
@@ -143,62 +139,6 @@
         return in.readBoolean();
     }
     
-    protected void writeStream( String location ) throws IOException {
-        URL url = new URL( location );
-        URLConnection conn = url.openConnection();
-        conn.connect();
-        
-        int l = conn.getContentLength();
-        writeInt( l );
-        InputStream uin = conn.getInputStream();
-        byte[] buf = new byte[1024*1024];
-        for (;;) {
-            int r = uin.read( buf );
-            if ( r == -1 ) break;
-            out.write( buf, 0, r );
-        }
-    }
-    
-    protected InputStream readStream() throws IOException {
-        final int length = readInt();
-        return new CountingInputStream(in) {
-            @Override
-            public int read() throws IOException
-            {
-                if ( getCount() < length )
-                    return super.read();
-                else
-                    return -1;
-            }
-
-            @Override
-            public int read( byte[] b, int off, int len ) throws IOException
-            {
-                len = (getCount() + len) > length ? (length - getCount()) : len;
-                if ( len == 0 )
-                    return -1;
-                else 
-                    return super.read( b, off, len );
-            }
-
-            @Override
-            public int read( byte[] b ) throws IOException
-            {
-                return read( b, 0, b.length );
-            }
-
-            @Override
-            public long skip( long len ) throws IOException
-            {
-                len = (getCount() + len) > length ? (length - getCount()) : len;
-                if ( len == 0 )
-                    return -1;
-                else 
-                    return super.skip( len );
-            }  
-        };
-    }
-    
     protected void flush() throws IOException
     {
         out.flush();
diff --git a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/io/UpdateAction.java b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/io/UpdateAction.java
index 99b684a..562fb14 100644
--- a/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/io/UpdateAction.java
+++ b/sigil/common/runtime/src/org/apache/felix/sigil/common/runtime/io/UpdateAction.java
@@ -24,6 +24,7 @@
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.URL;
 
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
@@ -71,8 +72,9 @@
         else
         {
             writeBoolean( true );
-            writeStream( update.location );
+            writeString( update.location );
         }
+        flush();
 
         if ( !checkOk() )
         {
@@ -101,8 +103,21 @@
                 boolean remote = readBoolean();
                 if ( remote )
                 {
-                    InputStream in = readStream();
-                    b.update(in);
+                    String loc = readString();
+                    try {
+                        InputStream in = open( loc );
+                        try {
+                            b.update(in);
+                            writeOk();
+                        }
+                        finally {
+                            in.close();
+                        }
+                    }
+                    catch (IOException e) {
+                        writeError();
+                        writeString(e.getMessage());
+                    }
                 }
                 else
                 {
@@ -117,5 +132,13 @@
             }
         }
 
+        flush();
+    }
+
+
+    private InputStream open( String loc ) throws IOException
+    {
+        URL url = new URL( loc );
+        return url.openStream();
     }
 }