Author: mturk
Date: Wed Oct 26 11:36:20 2011
New Revision: 1189137

URL: http://svn.apache.org/viewvc?rev=1189137&view=rev
Log:
Add posix compliant getopt parser. Borrowed from OpenBSD getopt

Added:
    
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Getopt.java
   (with props)
    
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/LongOption.java
   (with props)
    
commons/sandbox/runtime/trunk/src/main/test/org/apache/commons/runtime/TestGetopt.java
   (with props)

Added: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Getopt.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Getopt.java?rev=1189137&view=auto
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Getopt.java
 (added)
+++ 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Getopt.java
 Wed Oct 26 11:36:20 2011
@@ -0,0 +1,527 @@
+/* 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.commons.runtime.util;
+
+/**
+ * Getopt class.
+ * @author Mladen Turk
+ * @since Runtime 1.0
+ */
+public class Getopt
+{
+
+    private static final char[] EMSG = { 0 };
+    private char[][]            argv;
+    private int                 argc;
+    private String[]            args;
+
+    private int                 optind;
+    private int                 optopt = '?';
+    
+    private char[]              optarg;
+    private char[]              place;
+
+    private int                 nonopt_start = -1; // first non option 
argument (for permute)
+    private int                 nonopt_end   = -1; // first option after non 
options (for permute)
+    private boolean             interleave;
+    private boolean             reset;
+    private int                 flags;
+
+    public  static int  GETOPT_ALLARGS  =  0x01; /* treat non-options as args 
to option "-1" */
+    public  static int  GETOPT_LONG     =  0x02; /* operate as 
getopt_long_only */
+    public  static int  GETOPT_LONGONLY =  0x04; /* operate as 
getopt_long_only */
+    public  static int  GETOPT_POSIXLY  =  0x08; /* Used instead 
POSIXLY_CORRECT env var */
+    private static int  GETOPT_PERMUTE  =  0x10;
+
+    private Getopt()
+    {
+        // No instance
+    }
+
+    public Getopt(String[] args)
+    {
+        argc   = args.length;
+        argv   = new char[argc + 1][];
+        optarg = null;
+        place  = EMSG;
+        for (int i = 0; i < args.length; i++)
+            argv[i] = wcsdup(args[i]);
+        this.args = args;
+    }
+
+    public Getopt(String[] args, int flags)
+    {
+        this(args);
+        this.flags  = flags;
+    }
+
+    public void reset()
+    {
+        reset = true;
+    }
+
+    public String optarg()
+    {
+        return wcstostr(optarg);
+    }
+
+    public String[] args()
+    {
+        if (optind < argc) {
+            int n = 0;
+            String[] a = new String[argc - optind];
+            for (int i = optind; i < argc; i++)
+                a[n++] = wcstostr(argv[i]);
+            return a;
+        }
+        return null;
+    }
+
+    /*
+     * Compute the greatest common divisor of a and b.
+     */
+    private int gcd(int a, int b)
+    {
+        int c;
+
+        c = a % b;
+        while (c != 0) {
+            a = b;
+            b = c;
+            c = a % b;
+        }
+        return b;
+    }
+
+    private static int wcslen(char[] s, int off)
+    {
+        for (int i = off; i < s.length; i++) {
+            if (s[i] == '\0')
+                return i - off;
+        }
+        return 0;
+    }
+
+    private static int wcslen(char[] s)
+    {
+        return wcslen(s, 0);
+    }
+
+    /**
+     * Create C string from Java string.
+     */
+    private static char[] wcsdup(String s)
+        throws NullPointerException
+    {
+        if (s == null)
+            throw new NullPointerException();
+        int length = s.length();
+        if (length == 0)
+            return new char[1];
+        char[] c = new char[length + 1];
+        s.getChars(0, length, c, 0);
+        return c;
+    }
+
+    private static char[] wcsdup(String s, int srcBegin)
+        throws NullPointerException,
+               IndexOutOfBoundsException
+    {
+        if (s == null)
+            throw new NullPointerException();
+        int srcEnd = s.length();
+        if (srcBegin < 0 || srcBegin > srcEnd)
+            throw new IndexOutOfBoundsException();
+        if (srcEnd == 0)
+            return new char[1];
+        char[] c = new char[srcEnd - srcBegin + 1];
+        s.getChars(srcBegin, srcEnd, c, 0);
+        return c;
+    }
+
+    /**
+     * Convert C string to Java string.
+     */
+    private static String wcstostr(char[] wcs, int off)
+    {
+        if (wcs == null)
+            return null;
+        int len = wcslen(wcs, off);
+        if (len == 0)
+            return "";
+        else
+            return new String(wcs, off, len);
+    }
+
+    private static String wcstostr(char[] wcs)
+    {
+        if (wcs == null)
+            return null;
+        int len = wcslen(wcs);
+        if (len == 0)
+            return "";
+        else
+            return new String(wcs, 0, len);
+    }
+
+    private static char[] wcsdup(char[] s, int off)
+    {
+        int len  = wcslen(s, off);
+        char[] d = new char[len + 1];
+        if (len > 0)
+            System.arraycopy(s, off, d, 0, len);
+        return d;
+    }
+
+    private static char[] wcsdup(char[] s)
+    {
+        return wcsdup(s, 0);
+    }
+
+    private static char[] wcschr(char[] s, int c)
+    {
+        if (s[0] == c)
+            return s;
+        for (int i = 0; i < s.length; i++) {
+            if (s[i] == c)
+                return wcsdup(s, i);
+            if (s[i] == '\0')
+                break;
+        }
+        return null;
+    }
+
+    private static char[] wcschr(String s, int c)
+    {
+        for (int i = 0; i < s.length(); i++) {
+            if (s.charAt(i) == c)
+                return wcsdup(s, i);
+        }
+        return null;
+    }
+
+    private static int wcsidx(char[] s, int c)
+    {
+        for (int i = 0; i < s.length; i++) {
+            if (s[i] == c)
+                return i;
+            if (s[i] == '\0')
+                break;
+        }
+        return -1;
+    }
+
+    // Wrapper for *++str
+    private static int wcsnext(char[] c)
+    {
+        System.arraycopy(c, 1, c, 0, c.length - 1);
+        return c[0];
+    }
+
+    private static int wcsncmp(char[] s1, char[] s2, int nch)
+    {
+        int s1c = 0;
+        int s2c = 0;
+
+        if (nch == 0)
+            return 0;
+        do {
+            if (s1[s1c] != s2[s2c++]) {
+                // Result is not important
+                return s1[s1c] - s2[s2c - 1];
+            }
+            if (s1[s1c++] == '\0')
+                break;
+        } while (--nch != 0);
+        return 0;
+    }
+
+    /*
+     * Exchange the block from os->nonopt_start to os->nonopt_end with the 
block
+     * from os->nonopt_end to opt_end (keeping the same order of arguments
+     * in each block).
+     */
+    private void permute_args()
+    {
+        /*
+         * compute lengths of blocks and number and size of cycles
+         */
+        int nnonopts = nonopt_end - nonopt_start;
+        int nopts    = optind - nonopt_end;
+        int ncycle   = gcd(nnonopts, nopts);
+        int cyclelen = (optind - nonopt_start) / ncycle;
+
+        for (int i = 0; i < ncycle; i++) {
+            int cstart = nonopt_end + i;
+            int pos = cstart;
+            for (int j = 0; j < cyclelen; j++) {
+                if (pos >= nonopt_end)
+                    pos -= nnonopts;
+                else
+                    pos += nopts;
+                char[] swap  = argv[pos];
+                argv[pos]    = argv[cstart];
+                argv[cstart] = swap;
+            }
+        }
+    }
+
+    private int parse_long_options(LongOption[] longOptions, boolean short_too)
+    {
+        int i, current_argv_len;
+        int match = -1;
+        int has_equal;
+        char[] current_argv = place;
+
+        optind++;
+        if ((has_equal = wcsidx(current_argv, '=')) != -1)
+            current_argv_len = has_equal;
+        else
+            current_argv_len = wcslen(current_argv);
+        for (i = 0; i < longOptions.length; i++) {
+            char[] name = wcsdup(longOptions[i].name());
+            if (wcsncmp(current_argv, name, current_argv_len) != 0)
+                continue;
+            if (wcslen(name) == current_argv_len) {
+                // exact match
+                match = i;
+                break;
+            }
+            /*
+            * If this is a known short option, don't allow
+            * a partial match of a single character.
+            */
+            if (short_too && current_argv_len == 1)
+                continue;
+
+            if (match == -1)    // partial match
+                match = i;
+            else {
+                String s = new String(current_argv, 0, current_argv_len);
+                // ambiguous abbreviation
+                throw new IllegalArgumentException("ambiguous option -- " + s);
+            }
+        }
+        if (match != -1) {      // option found
+            if (longOptions[match].noArgument() && has_equal != -1) {
+                String s = new String(current_argv, 0, current_argv_len);
+                throw new IllegalArgumentException("option doesn't take an 
argument -- " + s);
+            }
+            if (longOptions[match].isRequired() || 
longOptions[match].isOptional()) {
+                if (has_equal != -1) {
+                    optarg = wcsdup(current_argv, has_equal + 1);
+                }
+                else if (longOptions[match].isRequired()) {
+                    // optional argument doesn't use next os->argv
+                    optarg = argv[optind++];
+                }
+            }
+            if (longOptions[match].isRequired() && optarg == null) {
+                String s = new String(current_argv, 0, current_argv_len);
+                throw new IllegalArgumentException("option requires an 
argument -- " + s);
+            }
+        }
+        else {
+            // unknown option
+            if (short_too) {
+                --optind;
+                return -1;
+            }
+            String s = new String(current_argv, 0, current_argv_len);
+            throw new IllegalArgumentException("unknown option -- " + s);
+        }
+        return longOptions[match].value();
+    }
+
+    /**
+     * Get option.
+     */
+    public int getopt(String options)
+        throws IllegalArgumentException
+    {
+        return getopt(options, null);
+    }
+
+    /**
+     * Get long option.
+     */
+    public int getopt(String options, LongOption[] longOptions)
+        throws IllegalArgumentException
+    {
+        int optchar = -1;
+        optarg = null;
+
+        if (options == null)
+            return -1; // Nothing to do
+        if (longOptions != null && flags == 0)
+            flags |= GETOPT_LONG;
+        if ((flags & GETOPT_POSIXLY) != 0)
+            flags &= ~GETOPT_PERMUTE;
+        if (options.charAt(0) == '+') {
+            flags &= ~GETOPT_PERMUTE;
+            options = options.substring(1);
+        }
+        if (options.charAt(0) == '-') {
+            flags |= GETOPT_ALLARGS;
+            options = options.substring(1);
+        }
+        if (reset) {
+            nonopt_start = -1;
+            nonopt_end   = -1;
+        }
+        // start:
+        for (;;) {
+            if (reset || place[0] == '\0') {     // update scanning pointer
+                reset = false;
+                if (optind >= argc) {           // end of argument vector
+                    place= EMSG;
+                    if (nonopt_end != -1) {
+                        // do permutation, if we have to
+                        permute_args();
+                        optind -= nonopt_end - nonopt_start;
+                    }
+                    else if (nonopt_start != -1) {
+                        // If we skipped non-options, set optind
+                        // to the first of them.
+                        optind = nonopt_start;
+                    }
+                    nonopt_start = nonopt_end = -1;
+                    return -1;
+                }
+                place = argv[optind];
+                if (place[0] != '-' || place[1] == '\0' && 
options.indexOf('-') == -1) {
+                    place = EMSG;    // found non-option
+                    if ((flags & GETOPT_ALLARGS) != 0) {
+                        // GNU extension:
+                        // return non-option as argument to option 1
+                        optarg = argv[optind++];
+                        return 1;
+                    }
+                    if ((flags & GETOPT_PERMUTE) == 0) {
+                        // If no permutation wanted, stop parsing
+                        // at first non-option.
+                        return -1;
+                    }
+                    // do permutation
+                    if (nonopt_start == -1)
+                        nonopt_start = optind;
+                    else if (nonopt_end != -1) {
+                        permute_args();
+                        nonopt_start = optind - (nonopt_end - nonopt_start);
+                        nonopt_end = -1;
+                    }
+                    optind++;
+                    // process next argument
+                    continue;
+                }
+                if (nonopt_start != -1 && nonopt_end == -1)
+                    nonopt_end = optind;
+                // If we have "-" do nothing, if "--" we are done.
+                if (place[1] != '\0')
+                    place = wcsdup(place, 1);
+                if (place[0] == '-' && place[1] == '\0') {
+                    optind++;
+                    place = EMSG;
+                    // We found an option (--), so if we skipped
+                    // non-options, we have to permute.
+                    if (nonopt_end != -1) {
+                        permute_args();
+                        optind -= nonopt_end - nonopt_start;
+                    }
+                    nonopt_start = nonopt_end = -1;
+                    return -1;
+                }
+            }
+            break;
+        }
+        // Check long options if:
+        //  1) we were passed some
+        //  2) the arg is not just "-"
+        //  3) either the arg starts with -- we are getopt_long_only()
+        if (longOptions != null && place != argv[optind] && (place[0] == '-' 
|| (flags & GETOPT_LONGONLY) != 0)) {
+            boolean short_too = false;
+            if (place[0] == '-') {
+                wcsnext(place);  // --foo long option
+            }
+            else if (place[0] != ':' && wcschr(options, place[0]) != null) {
+                short_too = true;      // could be short option too
+            }
+            optchar = parse_long_options(longOptions, short_too);
+            if (optchar != -1) {
+                place = EMSG;
+                return optchar;
+            }
+        }
+
+        optchar = place[0];
+        place   = wcsdup(place, 1);
+        char[] oli = wcschr(options, optchar);
+        if (optchar == ':' || (optchar == '-' && place[0] != '\0') || oli == 
null) {
+            /*
+             * If the user specified "-" and  '-' isn't listed in
+             * options, return -1 (non-option) as per POSIX.
+             * Otherwise, it is an unknown option character (or ':').
+             */
+            if (optchar == '-' && place[0] == '\0')
+                return -1;
+            if (place[0] == '\0')
+                ++optind;
+            throw new IllegalArgumentException("unknown option -- " + 
String.valueOf((char)optchar));
+        }
+        if (longOptions != null && optchar == 'W' && oli[1] == ';') {
+            // -W long-option
+            if (place[0] != '\0') {
+                // no space, NOTHING
+                ;
+            }
+            else if (++optind >= argc) {   // no arg
+                place = EMSG;
+                throw new IllegalArgumentException("option requires an 
argument -- " + String.valueOf((char)optchar));
+            }
+            else {
+                // white space
+                place = argv[optind];
+            }
+            optchar = parse_long_options(longOptions, false);
+            place   = EMSG;
+            return optchar;
+        }
+        
+        if (wcsnext(oli) != ':') {  // doesn't take argument
+            if (place[0] == '\0')
+                ++optind;
+        }
+        else {                // takes (optional) argument
+            optarg = null;
+            if (place[0] != '\0') {
+                /* no white space */
+                optarg = place;
+            }
+            else if (oli[1] != ':') {   // arg not optional
+                if (++optind >= argc) {    // no arg
+                    throw new IllegalArgumentException("option requires an 
argument -- " + String.valueOf((char)optchar));
+                }
+                else {
+                    optarg = argv[optind];
+                }
+            }
+            place = EMSG;
+            ++optind;
+        }        
+        return optchar;
+    }
+}

Propchange: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/Getopt.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/LongOption.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/LongOption.java?rev=1189137&view=auto
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/LongOption.java
 (added)
+++ 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/LongOption.java
 Wed Oct 26 11:36:20 2011
@@ -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.commons.runtime.util;
+
+/**
+ * LongOption class.
+ * @author Mladen Turk
+ * @since Runtime 1.0
+ */
+public class LongOption
+{
+
+    private String              name;
+    private String              description;
+    private int                 required;
+    private int                 value;
+
+    /**
+     * Option has no argument.
+     */
+    public static int           NO_ARGUMENT       = 0;
+    /**
+     * Option requires argument.
+     */
+    public static int           REQUIRED_ARGUMENT = 1;
+    /**
+     * Option has optional argument.
+     */
+    public static int           OPTIONAL_ARGUMENT = 2;
+
+    private LongOption()
+    {
+        // No instance
+    }
+
+    /**
+     * Create long option instance.
+     */
+    public LongOption(String name, int required, int value, String description)
+    {
+        this.name        = name;
+        this.description = description;
+        this.required    = required;
+        this.value       = value;
+    }
+
+    /**
+     * Get option name.
+     */
+    public String name()
+    {
+        return name;
+    }
+
+    /**
+     * Get option value.
+     */
+    public int    value()
+    {
+        return value;
+    }
+
+    /**
+     * Check if option requires argument.
+     */
+    public boolean isRequired()
+    {
+        return (required & REQUIRED_ARGUMENT) != 0;
+    }
+
+    /**
+     * Check if option argument is optional.
+     */
+    public boolean isOptional()
+    {
+        return (required & OPTIONAL_ARGUMENT) != 0;
+    }
+
+    /**
+     * Check if option does not have argument.
+     */
+    public boolean noArgument()
+    {
+        return required == 0;
+    }
+    
+}

Propchange: 
commons/sandbox/runtime/trunk/src/main/java/org/apache/commons/runtime/util/LongOption.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
commons/sandbox/runtime/trunk/src/main/test/org/apache/commons/runtime/TestGetopt.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/runtime/trunk/src/main/test/org/apache/commons/runtime/TestGetopt.java?rev=1189137&view=auto
==============================================================================
--- 
commons/sandbox/runtime/trunk/src/main/test/org/apache/commons/runtime/TestGetopt.java
 (added)
+++ 
commons/sandbox/runtime/trunk/src/main/test/org/apache/commons/runtime/TestGetopt.java
 Wed Oct 26 11:36:20 2011
@@ -0,0 +1,108 @@
+/* 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.commons.runtime.util;
+
+import java.io.IOException;
+import java.io.File;
+import org.testng.annotations.*;
+import org.testng.Assert;
+import org.apache.commons.runtime.util.Utils;
+import org.apache.commons.runtime.Errno;
+import org.apache.commons.runtime.Status;
+import org.apache.commons.runtime.TimeoutException;
+import org.apache.commons.runtime.SystemException;
+
+@Test(groups = { "core" })
+public class TestGetopt extends Assert
+{
+
+    private static String[] noOpts = { "one", "two", "end" };
+    private static String[] avOpts = {
+        "-b",
+        "-dcafoo",
+        "--",
+        "--bar"
+    };
+
+    private static String[] acOpts = {
+        "-b",
+        "-dcafoo",
+        "--help",
+        "--all=foo",
+        "--",
+        "--bar",
+    };    
+
+    private static LongOption[] longOpts = {
+        new LongOption("all",  LongOption.REQUIRED_ARGUMENT, 'a', null),
+        new LongOption("bar",  LongOption.NO_ARGUMENT,       'b', null),
+        new LongOption("cxx",  LongOption.NO_ARGUMENT,       'c', null),
+        new LongOption("help", LongOption.NO_ARGUMENT,       '0', null)
+    };
+
+    public void checkAvOptions()
+    {
+        int rc;
+        int oc = 0;
+        Getopt opts = new Getopt(avOpts);
+        while ((rc = opts.getopt("a:bcd")) != -1) {
+            switch (rc) {
+                case 'a' :
+                    assertEquals(opts.optarg(), "foo");
+                    oc++;
+                break;
+                case 'b' :
+                case 'c' :
+                case 'd' :
+                    oc++;
+                break;
+                default:
+                    oc = rc;
+                break;
+            }
+        }
+        assertEquals(oc, 4);
+    }
+
+    public void checkAcOptions()
+    {
+        int rc;
+        int oc = 0;
+        Getopt opts = new Getopt(acOpts, Getopt.GETOPT_LONGONLY);
+        while ((rc = opts.getopt("a:bcd", longOpts)) != -1) {
+            switch (rc) {
+                case 'a' :
+                    assertEquals(opts.optarg(), "foo");
+                    oc++;
+                break;
+                case 'b' :
+                case 'c' :
+                case 'd' :
+                case '0' :
+                    oc++;
+                break;
+                default:
+                    oc = rc;
+                break;
+            }
+        }
+        assertEquals(oc, 6);
+        assertEquals(opts.args().length, 1);
+        assertEquals(opts.args()[0], "--bar");
+    }
+
+}

Propchange: 
commons/sandbox/runtime/trunk/src/main/test/org/apache/commons/runtime/TestGetopt.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to