You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by mt...@apache.org on 2011/10/26 13:36:20 UTC
svn commit: r1189137 - in /commons/sandbox/runtime/trunk/src/main:
java/org/apache/commons/runtime/util/Getopt.java
java/org/apache/commons/runtime/util/LongOption.java
test/org/apache/commons/runtime/TestGetopt.java
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