Return-Path: X-Original-To: apmail-commons-commits-archive@minotaur.apache.org Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B7D8B7E10 for ; Wed, 26 Oct 2011 11:36:43 +0000 (UTC) Received: (qmail 88250 invoked by uid 500); 26 Oct 2011 11:36:43 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 88182 invoked by uid 500); 26 Oct 2011 11:36:43 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 88175 invoked by uid 99); 26 Oct 2011 11:36:43 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Oct 2011 11:36:43 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Oct 2011 11:36:41 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 170C523889DA for ; Wed, 26 Oct 2011 11:36:21 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: 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 Date: Wed, 26 Oct 2011 11:36:20 -0000 To: commits@commons.apache.org From: mturk@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20111026113621.170C523889DA@eris.apache.org> 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