// Copyright (C) 1997 by Arieh Markel <arieh@selectjobs.com>.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
package dods.util;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Enumeration;
/**
* A class for achieving getopts() functionality.
*<P>
* Loosely based on perl5's getopt/Std.pm.
*<P>
* The object is instantiated with a 'flags' String that is the
* composed of the set of switches allowed to be passed, and the
* 'args' String array that has to be parsed opposite the 'flags'
* string.
*<PRE>
* new Getopts("oif:", args) -o, -i are boolean flags,
* -f takes an argument
*</PRE>
* The class processes single-character switches with switch
* clustering.
*<P>
* The list of valid switches is accessible through the 'swList()'
* method, which returns an Enumeration of the switch names.
*<P>
* A local array including the arguments from the 'args' array that
* was passed as an argument but are the actual command line arguments
* is generated and is accessible through the 'argList()' method.
*<P>
* Options switch content fields can be accessible through the
* 'OptSwitch' class.
*<P>
* @author Arieh Markel (arieh@selectjobs.com)
thanks to Mark Skipper (mcs@dmu.ac.uk) for bug fix
* @version 1.1 6/14/98 Updated evaluation following Mark's remarks.
*
* @see OptSwitch
* @see java.util.Enumeration
*
*/
/* $Id: Getopts.java,v 1.3 2004-02-06 15:23:51 donm Exp $ */
public class Getopts extends Object {
// The internal storage of options
//
Hashtable switchtab;
String arglist[];
// getSwitch
/**
* method to return the OptSwitch object associated with the
* 'sw' argument.
*<P>
* @param sw switch whose class is requested
*<P>
*/
public OptSwitch getSwitch(Character sw) {
return (OptSwitch) switchtab.get(sw);
}
/**
* getOption
*
* @param sw Character switch whose option is requested
*/
public String getOption(Character sw) {
return getOption(sw);
}
/**
* getOption
*
* @param sw int value switch whose option is requested
*/
public String getOption(int sw) {
Character opt = new Character((char) sw);
return getOption(opt);
}
// swList
/**
* Method to return an Enumeration of the switches
* that the Getopts object is able to parse (according
* to its initialization).
*<P>
* May be later used to step through the OptSwitch objects.
*/
public Enumeration swList() {
return switchtab.keys();
}
// argList
/**
* Method to return an array of the actual arguments of the
* command line invocation.
*/
public String[] argList() {
return arglist;
}
// Getopts
/**
* Basic class constructor. Gets the flags passed, in a
* notation similar to the one used by the sh, ksh, bash,
* and perl getopts.
*<P>
* String array 'args' is passed and
* is parsed according to the flags.
*<P>
* @param flags a string with the valid switch names
* @param args[] array of strings (usually args)
*<P>
* @exception InvalidSwitch thrown when invalid options are found
*<P>
*/
public Getopts(String flags, String args[])
throws InvalidSwitch
{
String throwstring = new String("Invalid Getopts switch(s): ");
String usage = new String("Usage: Getopts(String flags, String args[])");
switchtab = new Hashtable(1,1);
for (int i = 0; i < flags.length(); i++) {
boolean found;
int cc = flags.charAt(i);
Character c = new Character((char) cc);
char alpha[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
// space characters or punctuation marks are not allowed
// as switch values
//
if (cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r') {
throw new InvalidSwitch(throwstring + "Spaces not allowed\n" +
usage + "\n");
}
found = false;
for (int j = 0; j < alpha.length; j++) {
Character ch = new Character((char) alpha[j]);
char uc = ch.toUpperCase(ch.charValue());
if (alpha[j] == cc || uc == cc) {
found = true;
break;
}
}
// if the character was not found on the set, throw the exception
//
if (! found && cc != ':' ) {
throw new InvalidSwitch(throwstring + "Invalid Character " + c +
"\n" + usage + "\n");
}
// ':' appears when the preceding character accepts a value
//
if (cc == ':') {
if (i > 0) {
int prv = flags.charAt(i-1);
if (prv == ':') {
throw new InvalidSwitch(throwstring +
"Can't have consecutive ':'\n" +
usage + "\n");
}
else {
Character cp = new Character((char) prv);
OptSwitch sw = (OptSwitch) switchtab.get(cp);
sw.SetHasValue(OptSwitch.VAL);
}
}
}
else {
OptSwitch sw = new OptSwitch(cc, OptSwitch.BOOL);
switchtab.put(c, sw);
}
}
//
// Now, step through the arguments in the argument list
// and identify the options and values
//
int i;
for (i = 0; i < args.length; i++) {
char cc = args[i].charAt(0); // first character
if (cc != '-') {
// end of options
break;
}
// more options, iterate them
for (int j = 1; j < args[i].length(); j++) {
cc = args[i].charAt(j);
Character fc = new Character(cc);
OptSwitch cs = (OptSwitch) switchtab.get(fc);
if (cs == null) {
// The supplied switch wasn't recognised.
throw new InvalidSwitch(throwstring + "invalid switch " +
cc + "\n2 Valid switches are: " + flags + "\n" + usage + "\n");
} else if (!cs.acceptVal()) {
// the option is a switch and takes no value
cs.SetVal(true);
} else if (j+1 < args[i].length()) {
// the value may follow immediately after the switch
// (not as a separate token) set value to remainder of string...
cs.SetVal(args[i].substring(j+1));
// ... and move pointer to end, thus consuming the string
j = args[i].length();
} else if (++i >= args.length) {
// there wasn't another token
throw new InvalidSwitch(throwstring +
"missing value from switch " + cc +
"\n1 Valid switches are: " + flags +
"\n" + usage + "\n");
} else if (args[i].charAt(0) == '-') {
// there was no value, next token starts with flags
throw new InvalidSwitch(
throwstring + "missing value from switch " + cc +
"\n0 Valid switches are: " + flags + "\n" +
usage + "\n");
} else {
// the next token is the value
cs.SetVal(args[i]);
// and move j to the end to mark it consumed
j = args[i].length();
}
} // end for j
} // end for i
// Now insert the array of arguments into the object
//
arglist = new String[args.length - i];
System.arraycopy(args, i, arglist, 0, args.length-i);
}
/**
* method for class testing.
*<P>
* Invocation:
*<PRE>
* java Getopts "option set" arg0 arg1 ... argn
*</PRE>
*
* @params args arguments passed
* @exception InvalidSwitch thrown when invalid options are found
*/
public static void main(String args[])
throws InvalidSwitch
{
int i;
String args1[] = new String[args.length-1];
System.arraycopy(args, 1, args1, 0, args.length-1);
for (i = 0; i < args.length; i++) {
System.out.println("args[" + i + "] : " + args[i]);
}
try {
Getopts opts = new Getopts(args[0], args1);
Enumeration names = opts.swList();
i = 0;
while (names.hasMoreElements()) {
OptSwitch cs = opts.getSwitch((Character) names.nextElement());
System.out.println("args[" + i + "] : " +
(char) cs.sw + " " + cs.type + " " +
cs.set + " " + cs.val);
i++;
}
String argp[] = opts.argList();
for (i = 0; i < argp.length; i++) {
System.out.println("argv[" + i + "] : " + argp[i]);
}
}
catch (InvalidSwitch e) {
System.out.print(e);
}
}
}