package com.darwinsys.lang;
import com.darwinsys.util.Debug;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
/** A class to implement UNIX-style (single-character) command line argument
* parsing. Originally patterned after (but not using code from) the UNIX
* getopt(3) program, this has been redesigned to be more Java-friendly.
* As a result, there are two ways of using it.
* <ol><li>Original model:
* <pre>
GetOpt go = new GetOpt("hno:");
boolean numeric_option = false;
String outFileName = "(standard output)";
char c;
while ((c = go.getopt(args)) != GetOpt.DONE) {
switch(c) {
case 'h':
doHelp(0);
break;
case 'n':
numeric_option = true;
break;
case 'o':
outFileName = go.optarg();
break;
default:
System.err.println("Unknown option character " + c);
doHelp(1);
}
}
System.out.print("Options: ");
System.out.print("Numeric: " + numeric_option + ' ');
System.out.print("Output: " + outFileName + "; ");
System.out.print("Inputs: ");
if (go.getOptInd() == args.length) {
doFile("(standard input)");
} else for (int i = go.getOptInd(); i < args.length; i++) {
doFile(args[i]);
}
* </pre></li>
* <li>Newer model, which allows long-named options:
* <pre>
boolean numeric_option = false;
boolean errs = false;
String outputFileName = null;
GetOptDesc options[] = {
new GetOptDesc('n', "numeric", false),
new GetOptDesc('o', "output-file", true),
};
GetOpt parser = new GetOpt(options);
Map optionsFound = parser.parseArguments(argv);
Iterator it = optionsFound.keySet().iterator();
while (it.hasNext()) {
String key = (String)it.next();
char c = key.charAt(0);
switch (c) {
case 'n':
numeric_option = true;
break;
case 'o':
outputFileName = (String)optionsFound.get(key);
break;
case '?':
errs = true;
break;
default:
throw new IllegalStateException(
"Unexpected option character: " + c);
}
}
if (errs) {
System.err.println("Usage: GetOptDemo [-n][-o file][file...]");
}
System.out.print("Options: ");
System.out.print("Numeric: " + numeric_option + ' ');
System.out.print("Output: " + outputFileName + "; ");
System.out.print("Input files: ");
List files = parser.getFilenameList();
while (files.hasNext()) {
System.out.print(files.next());
System.out.print(' ');
}
System.out.println();
}
* </pre></li>
* </ol>
* <p>
* This is <em>not</em> threadsafe; it is expected to be used only from main().
* <p>
* For another way of dealing with command lines, see the
* <a href="http://jakarta.apache.org/commons/cli/">Jakarta Commons
* Command Line Interface</a>.
* @author Ian F. Darwin, http://www.darwinsys.com/
* @version $Id: GetOpt.java,v 1.23 2004/06/01 02:51:36 ian Exp $
*/
public class GetOpt {
/** The List of File Names found after args */
protected List fileNameArguments;
/** The set of characters to look for */
protected final GetOptDesc[] options;
/** Where we are in the options */
protected int optind = 0;
/** Public constant for "no more options" */
public static final int DONE = 0;
/** Internal flag - whether we are done all the options */
protected boolean done = false;
/** The current option argument. */
protected String optarg;
/** Retrieve the current option argument; UNIX variant spelling. */
public String optarg() {
return optarg;
}
/** Retrieve the current option argument; Java variant spelling. */
public String optArg() {
return optarg;
}
/* Construct a GetOpt parser, given the option specifications
* in an array of GetOptDesc objects. This is the preferred constructor.
*/
public GetOpt(final GetOptDesc[] opt) {
this.options = opt;
}
/* Construct a GetOpt parser, storing the set of option characters.
* This is a legacy constructor for backwards compatibility.
*/
public GetOpt(final String patt) {
if (patt == null) {
throw new IllegalArgumentException("Pattern may not be null");
}
// Pass One: just count the letters
int n = 0;
for (int i = 0; i<patt.length(); i++) {
if (patt.charAt(i) != ':')
++n;
}
if (n == 0) {
throw new IllegalArgumentException(
"No option letters found in " + patt);
}
// Pass Two: construct an array of GetOptDesc opjects.
options = new GetOptDesc[n];
for (int i = 0, ix = 0; i<patt.length(); i++) {
final char c = patt.charAt(i);
boolean argTakesValue = false;
if (i < patt.length() - 1 && patt.charAt(i+1) == ':') {
argTakesValue = true;
++i;
}
options[ix] = new GetOptDesc(c, null, argTakesValue);
Debug.println("getopt",
"CONSTR: options[" + ix + "] = " + c + ", " + argTakesValue);
++ix;
}
}
/** Reset this GetOpt parser */
public void rewind() {
fileNameArguments = null;
done = false;
optind = 0;
}
/** Array used to convert a char to a String */
private static char[] strConvArray = { 0 };
/**
* Modern way of using GetOpt: call this once and get all options.
* <p>
* This parses the options, returns a Map whose keys are the found options.
* Normally followed by a call to getFilenameList().
* @return a Map whose keys are Strings of length 1 (containing the char
* from the option that was matched) and whose value is a String
* containing the value, or null for a non-option argument.
*/
public Map parseArguments(String[] argv) {
Map optionsAndValues = new HashMap();
fileNameArguments = new ArrayList();
for (int i = 0; i < argv.length; i++) {
Debug.println("getopt", "parseArg: i=" + i + ": arg " + argv[i]);
char c = getopt(argv);
if (c != DONE) {
strConvArray[0] = c;
optionsAndValues.put(new String(strConvArray), optarg);
// If this arg takes an option, we must skip it here.
if (optarg != null)
++i;
} else {
fileNameArguments.add(argv[i]);
}
}
return optionsAndValues;
}
/** Get the list of filename-like arguments after options */
public List getFilenameList() {
if (fileNameArguments == null) {
throw new IllegalArgumentException(
"Illegal call to getFilenameList() before parseOptions()");
}
return fileNameArguments;
}
/** The true heart of getopt, whether used old way or new way:
* returns one argument; call repeatedly until it returns DONE.
*/
public char getopt(String argv[]) {
Debug.println("getopt",
"optind=" + optind + ", argv.length="+argv.length);
if (optind >= (argv.length)-1) {
done = true;
}
// If we are (now) finished, bail.
if (done) {
return DONE;
}
// XXX TODO - two-pass, 1st check long args, 2nd check for
// char, may be multi char as in "-no outfile" == "-n -o outfile".
// Pick off the next command line argument, check if it starts "-".
// If so look it up in the list.
String thisArg = argv[optind++];
if (thisArg.startsWith("-")) {
optarg = null;
for (int i=0; i<options.length; i++) {
if ( options[i].argLetter == thisArg.charAt(1) ||
(options[i].argName != null &&
options[i].argName == thisArg.substring(1))) { // found it
// If it needs an option argument, get it.
if (options[i].takesArgument) {
if (optind < argv.length) {
optarg = argv[optind];
++optind;
} else {
throw new IllegalArgumentException(
"Option " + options[i].argLetter +
" needs value but found end of arg list");
}
}
return options[i].argLetter;
}
}
// Began with "-" but not matched, so must be error.
return '?';
} else {
// Found non-argument non-option word in argv: end of options.
done = true;
return DONE;
}
}
/** Return optind, the index into args of the last option we looked at */
public int getOptInd() {
return optind;
}
}
|