xref: /illumos-gate/usr/src/cmd/dtrace/test/cmd/jdtrace/Getopt.java (revision 73427c57f824c3ec3b396181b163f37d50c5b3b1)
1*73427c57Sahl /*
2*73427c57Sahl  * CDDL HEADER START
3*73427c57Sahl  *
4*73427c57Sahl  * The contents of this file are subject to the terms of the
5*73427c57Sahl  * Common Development and Distribution License (the "License").
6*73427c57Sahl  * You may not use this file except in compliance with the License.
7*73427c57Sahl  *
8*73427c57Sahl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*73427c57Sahl  * or http://www.opensolaris.org/os/licensing.
10*73427c57Sahl  * See the License for the specific language governing permissions
11*73427c57Sahl  * and limitations under the License.
12*73427c57Sahl  *
13*73427c57Sahl  * When distributing Covered Code, include this CDDL HEADER in each
14*73427c57Sahl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*73427c57Sahl  * If applicable, add the following below this CDDL HEADER, with the
16*73427c57Sahl  * fields enclosed by brackets "[]" replaced with your own identifying
17*73427c57Sahl  * information: Portions Copyright [yyyy] [name of copyright owner]
18*73427c57Sahl  *
19*73427c57Sahl  * CDDL HEADER END
20*73427c57Sahl  */
21*73427c57Sahl 
22*73427c57Sahl /*
23*73427c57Sahl  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*73427c57Sahl  * Use is subject to license terms.
25*73427c57Sahl  *
26*73427c57Sahl  * ident	"%Z%%M%	%I%	%E% SMI"
27*73427c57Sahl  */
28*73427c57Sahl 
29*73427c57Sahl /* Copyright (c) 1988 AT&T */
30*73427c57Sahl /* All Rights Reserved */
31*73427c57Sahl 
32*73427c57Sahl import java.io.StringWriter;
33*73427c57Sahl import java.io.PrintWriter;
34*73427c57Sahl 
35*73427c57Sahl /**
36*73427c57Sahl  * A Java port of Solaris {@code lib/libc/port/gen/getopt.c}, which is a
37*73427c57Sahl  * port of System V UNIX getopt.  See <b>getopt(3C)</b> and SUS/XPG
38*73427c57Sahl  * getopt() for function definition and requirements. Unlike that
39*73427c57Sahl  * definition, this implementation moves non-options to the end of the
40*73427c57Sahl  * argv array rather than quitting at the first non-option.
41*73427c57Sahl  */
42*73427c57Sahl public class Getopt {
43*73427c57Sahl     static final int EOF = -1;
44*73427c57Sahl 
45*73427c57Sahl     private String progname;
46*73427c57Sahl     private String[] args;
47*73427c57Sahl     private int argc;
48*73427c57Sahl     private String optstring;
49*73427c57Sahl     private int optind = 0; // args index
50*73427c57Sahl     private int optopt = 0;
51*73427c57Sahl     private String optarg = null;
52*73427c57Sahl     private boolean opterr = true;
53*73427c57Sahl 
54*73427c57Sahl     /*
55*73427c57Sahl      * _sp is required to keep state between successive calls to
56*73427c57Sahl      * getopt() while extracting aggregated short-options (ie: -abcd).
57*73427c57Sahl      */
58*73427c57Sahl     private int _sp = 1;
59*73427c57Sahl 
60*73427c57Sahl     /**
61*73427c57Sahl      * Creates a {Code Getopt} instance to parse the given command-line
62*73427c57Sahl      * arguments. Modifies the given args array by swapping the
63*73427c57Sahl      * positions of non-options and options so that non-options appear
64*73427c57Sahl      * at the end of the array.
65*73427c57Sahl      */
Getopt(String programName, String[] args, String optionString)66*73427c57Sahl     public Getopt(String programName, String[] args,
67*73427c57Sahl 	    String optionString)
68*73427c57Sahl     {
69*73427c57Sahl 	progname = programName;
70*73427c57Sahl 	// No defensive copy; Getopt is expected to modify the given
71*73427c57Sahl 	// args array
72*73427c57Sahl 	this.args = args;
73*73427c57Sahl 	argc = this.args.length;
74*73427c57Sahl 	optstring = optionString;
75*73427c57Sahl 	validate();
76*73427c57Sahl     }
77*73427c57Sahl 
78*73427c57Sahl     private void
validate()79*73427c57Sahl     validate()
80*73427c57Sahl     {
81*73427c57Sahl 	if (progname == null) {
82*73427c57Sahl 	    throw new NullPointerException("program name is null");
83*73427c57Sahl 	}
84*73427c57Sahl 	int i = 0;
85*73427c57Sahl 	for (String s : args) {
86*73427c57Sahl 	    if (s == null) {
87*73427c57Sahl 		throw new NullPointerException("null arg at index " + i);
88*73427c57Sahl 	    }
89*73427c57Sahl 	    ++i;
90*73427c57Sahl 	}
91*73427c57Sahl 	if (optstring == null) {
92*73427c57Sahl 	    throw new NullPointerException("option string is null");
93*73427c57Sahl 	}
94*73427c57Sahl     }
95*73427c57Sahl 
96*73427c57Sahl     private static class StringRef {
97*73427c57Sahl 	private String s;
98*73427c57Sahl 
99*73427c57Sahl 	public String
get()100*73427c57Sahl 	get()
101*73427c57Sahl 	{
102*73427c57Sahl 	    return s;
103*73427c57Sahl 	}
104*73427c57Sahl 
105*73427c57Sahl 	public StringRef
set(String value)106*73427c57Sahl 	set(String value)
107*73427c57Sahl 	{
108*73427c57Sahl 	    s = value;
109*73427c57Sahl 	    return this;
110*73427c57Sahl 	}
111*73427c57Sahl     }
112*73427c57Sahl 
113*73427c57Sahl     /*
114*73427c57Sahl      * Generalized error processing method. If the optstr parameter is
115*73427c57Sahl      * null, the character c is converted to a string and displayed
116*73427c57Sahl      * instead.
117*73427c57Sahl      */
118*73427c57Sahl     void
err(String format, char c, String optstr)119*73427c57Sahl     err(String format, char c, String optstr)
120*73427c57Sahl     {
121*73427c57Sahl 	if (opterr && optstring.charAt(0) != ':') {
122*73427c57Sahl 	    StringWriter w = new StringWriter();
123*73427c57Sahl 	    PrintWriter p = new PrintWriter(w);
124*73427c57Sahl 	    p.printf(format, progname, (optstr == null ?
125*73427c57Sahl 		    Character.toString(c) : optstr.substring(2)));
126*73427c57Sahl 	    System.err.println(w.toString());
127*73427c57Sahl 	}
128*73427c57Sahl     }
129*73427c57Sahl 
130*73427c57Sahl     /*
131*73427c57Sahl      * Determine if the specified character (c) is present in the string
132*73427c57Sahl      * (optstring) as a regular, single character option. If the option
133*73427c57Sahl      * is found, return an index into optstring where the short-option
134*73427c57Sahl      * character is found, otherwise return -1. The characters ':' and
135*73427c57Sahl      * '(' are not allowed.
136*73427c57Sahl      */
137*73427c57Sahl     static int
parseshort(String optstring, char c)138*73427c57Sahl     parseshort(String optstring, char c)
139*73427c57Sahl     {
140*73427c57Sahl 	if (c == ':' || c == '(') {
141*73427c57Sahl 	    return -1;
142*73427c57Sahl 	}
143*73427c57Sahl 
144*73427c57Sahl 	int ch;
145*73427c57Sahl 	int len = optstring.length();
146*73427c57Sahl 	for (int i = 0; i < len; ++i) {
147*73427c57Sahl 	    ch = optstring.charAt(i);
148*73427c57Sahl 	    if (ch == c) {
149*73427c57Sahl 		return i;
150*73427c57Sahl 	    }
151*73427c57Sahl 
152*73427c57Sahl 	    while (i < len && ch == '(') {
153*73427c57Sahl 		for (++i; i < len && (ch = optstring.charAt(i)) != ')'; ++i);
154*73427c57Sahl 	    }
155*73427c57Sahl 	}
156*73427c57Sahl 
157*73427c57Sahl 	return -1;
158*73427c57Sahl     }
159*73427c57Sahl 
160*73427c57Sahl     /**
161*73427c57Sahl      * Determine if the specified string (opt) is present in the string
162*73427c57Sahl      * (optstring) as a long-option contained within parenthesis. If the
163*73427c57Sahl      * long-option specifies option-argument, return a reference to it
164*73427c57Sahl      * in longoptarg.  Otherwise set the longoptarg reference to null.
165*73427c57Sahl      * If the option is found, return an index into optstring at the
166*73427c57Sahl      * position of the short-option character associated with the
167*73427c57Sahl      * long-option; otherwise return -1.
168*73427c57Sahl      *
169*73427c57Sahl      * @param optstring	the entire optstring passed to the {@code
170*73427c57Sahl      * Getopt} constructor
171*73427c57Sahl      * @param opt the long option read from the command line
172*73427c57Sahl      * @param longoptarg the value of the option is returned in this
173*73427c57Sahl      * parameter, if an option exists. Possible return values in
174*73427c57Sahl      * longoptarg are:
175*73427c57Sahl      * <ul>
176*73427c57Sahl      * <li><b>NULL:</b> No argument was found</li>
177*73427c57Sahl      * <li><b>empty string (""):</b> Argument was explicitly left empty
178*73427c57Sahl      * by the user (e.g., --option= )</li>
179*73427c57Sahl      * <li><b>valid string:</b> Argument found on the command line</li>
180*73427c57Sahl      * </ul>
181*73427c57Sahl      * @return index to equivalent short-option in optstring, or -1 if
182*73427c57Sahl      * option not found in optstring.
183*73427c57Sahl      */
184*73427c57Sahl     static int
parselong(String optstring, String opt, StringRef longoptarg)185*73427c57Sahl     parselong(String optstring, String opt, StringRef longoptarg)
186*73427c57Sahl     {
187*73427c57Sahl 	int cp; // index into optstring, beginning of one option spec
188*73427c57Sahl 	int ip; // index into optstring, traverses every char
189*73427c57Sahl 	char ic; // optstring char
190*73427c57Sahl 	int il; // optstring length
191*73427c57Sahl 	int op;	// index into opt
192*73427c57Sahl 	char oc; // opt char
193*73427c57Sahl 	int ol; // opt length
194*73427c57Sahl 	boolean	match; // true if opt is matching part of optstring
195*73427c57Sahl 
196*73427c57Sahl 	longoptarg.set(null);
197*73427c57Sahl 	cp = ip = 0;
198*73427c57Sahl 	il = optstring.length();
199*73427c57Sahl 	ol = opt.length();
200*73427c57Sahl 	do {
201*73427c57Sahl 	    ic = optstring.charAt(ip);
202*73427c57Sahl 	    if (ic != '(' && ++ip == il)
203*73427c57Sahl 		break;
204*73427c57Sahl 	    ic = optstring.charAt(ip);
205*73427c57Sahl 	    if (ic == ':' && ++ip == il)
206*73427c57Sahl 		break;
207*73427c57Sahl 	    ic = optstring.charAt(ip);
208*73427c57Sahl 	    while (ic == '(') {
209*73427c57Sahl 		if (++ip == il)
210*73427c57Sahl 		    break;
211*73427c57Sahl 		op = 0;
212*73427c57Sahl 		match = true;
213*73427c57Sahl 		while (ip < il && (ic = optstring.charAt(ip)) != ')' &&
214*73427c57Sahl 			op < ol) {
215*73427c57Sahl 		    oc = opt.charAt(op++);
216*73427c57Sahl 		    match = (ic == oc && match);
217*73427c57Sahl 		    ++ip;
218*73427c57Sahl 		}
219*73427c57Sahl 
220*73427c57Sahl 		if (match && ip < il && ic == ')' && (op >= ol ||
221*73427c57Sahl 			opt.charAt(op) == '=')) {
222*73427c57Sahl 		    if (op < ol && opt.charAt(op) == '=') {
223*73427c57Sahl 			/* may be an empty string - OK */
224*73427c57Sahl 			longoptarg.set(opt.substring(op + 1));
225*73427c57Sahl 		    } else {
226*73427c57Sahl 			longoptarg.set(null);
227*73427c57Sahl 		    }
228*73427c57Sahl 		    return cp;
229*73427c57Sahl 		}
230*73427c57Sahl 		if (ip < il && ic == ')' && ++ip == il)
231*73427c57Sahl 		    break;
232*73427c57Sahl 		ic = optstring.charAt(ip);
233*73427c57Sahl 	    }
234*73427c57Sahl 	    cp = ip;
235*73427c57Sahl 	    /*
236*73427c57Sahl 	     * Handle double-colon in optstring ("a::(longa)") The old
237*73427c57Sahl 	     * getopt() accepts it and treats it as a required argument.
238*73427c57Sahl 	     */
239*73427c57Sahl 	    while ((cp > 0) && (cp < il) && (optstring.charAt(cp) == ':')) {
240*73427c57Sahl 		--cp;
241*73427c57Sahl 	    }
242*73427c57Sahl 	} while (cp < il);
243*73427c57Sahl 	return -1;
244*73427c57Sahl     }
245*73427c57Sahl 
246*73427c57Sahl     /**
247*73427c57Sahl      * Get the current option value.
248*73427c57Sahl      */
249*73427c57Sahl     public String
getOptarg()250*73427c57Sahl     getOptarg()
251*73427c57Sahl     {
252*73427c57Sahl 	return optarg;
253*73427c57Sahl     }
254*73427c57Sahl 
255*73427c57Sahl     /**
256*73427c57Sahl      * Get the index of the next option to be parsed.
257*73427c57Sahl      */
258*73427c57Sahl     public int
getOptind()259*73427c57Sahl     getOptind()
260*73427c57Sahl     {
261*73427c57Sahl 	return optind;
262*73427c57Sahl     }
263*73427c57Sahl 
264*73427c57Sahl     /**
265*73427c57Sahl      * Gets the command-line arguments.
266*73427c57Sahl      */
267*73427c57Sahl     public String[]
getArgv()268*73427c57Sahl     getArgv()
269*73427c57Sahl     {
270*73427c57Sahl 	// No defensive copy: Getopt is expected to modify the given
271*73427c57Sahl 	// args array.
272*73427c57Sahl 	return args;
273*73427c57Sahl     }
274*73427c57Sahl 
275*73427c57Sahl     /**
276*73427c57Sahl      * Gets the aggregated short option that just failed. Since long
277*73427c57Sahl      * options can't be aggregated, a failed long option can be obtained
278*73427c57Sahl      * by {@code getArgv()[getOptind() - 1]}.
279*73427c57Sahl      */
280*73427c57Sahl     public int
getOptopt()281*73427c57Sahl     getOptopt()
282*73427c57Sahl     {
283*73427c57Sahl 	return optopt;
284*73427c57Sahl     }
285*73427c57Sahl 
286*73427c57Sahl     /**
287*73427c57Sahl      * Set to {@code false} to suppress diagnostic messages to stderr.
288*73427c57Sahl      */
289*73427c57Sahl     public void
setOpterr(boolean err)290*73427c57Sahl     setOpterr(boolean err)
291*73427c57Sahl     {
292*73427c57Sahl 	opterr = err;
293*73427c57Sahl     }
294*73427c57Sahl 
295*73427c57Sahl     /**
296*73427c57Sahl      * Gets the next option character, or -1 if there are no more
297*73427c57Sahl      * options. If getopt() encounters a short-option character or a
298*73427c57Sahl      * long-option string not described in the {@code optionString}
299*73427c57Sahl      * argument to the constructor, it returns the question-mark (?)
300*73427c57Sahl      * character. If it detects a missing option-argument, it also
301*73427c57Sahl      * returns the question-mark (?) character, unless the first
302*73427c57Sahl      * character of the {@code optionString} argument was a colon (:),
303*73427c57Sahl      * in which case getopt() returns the colon (:) character.
304*73427c57Sahl      * <p>
305*73427c57Sahl      * This implementation swaps the positions of options and
306*73427c57Sahl      * non-options in the given argv array.
307*73427c57Sahl      */
308*73427c57Sahl     public int
getopt()309*73427c57Sahl     getopt()
310*73427c57Sahl     {
311*73427c57Sahl 	char c;
312*73427c57Sahl 	int cp;
313*73427c57Sahl 	boolean longopt;
314*73427c57Sahl 	StringRef longoptarg = new StringRef();
315*73427c57Sahl 
316*73427c57Sahl 	/*
317*73427c57Sahl 	 * Has the end of the options been encountered?  The following
318*73427c57Sahl 	 * implements the SUS requirements:
319*73427c57Sahl 	 *
320*73427c57Sahl 	 * If, when getopt() is called:
321*73427c57Sahl 	 *	- the first character of argv[optind] is not '-'
322*73427c57Sahl 	 *	- argv[optind] is the string "-"
323*73427c57Sahl 	 * getopt() returns -1 without changing optind if
324*73427c57Sahl 	 *	- argv[optind] is the string "--"
325*73427c57Sahl 	 * getopt() returns -1 after incrementing optind
326*73427c57Sahl 	 */
327*73427c57Sahl 	if (_sp == 1) {
328*73427c57Sahl 	    boolean nonOption;
329*73427c57Sahl 	    do {
330*73427c57Sahl 		nonOption = false;
331*73427c57Sahl 		if (optind >= argc || args[optind].equals("-")) {
332*73427c57Sahl 		    return EOF;
333*73427c57Sahl 		} else if (args[optind].equals("--")) {
334*73427c57Sahl 		    ++optind;
335*73427c57Sahl 		    return EOF;
336*73427c57Sahl 		} else if (args[optind].charAt(0) != '-') {
337*73427c57Sahl 		    // non-option: here we deviate from the SUS requirements
338*73427c57Sahl 		    // by not quitting, and instead move non-options to the
339*73427c57Sahl 		    // end of the args array
340*73427c57Sahl 		    nonOption = true;
341*73427c57Sahl 		    String tmp = args[optind];
342*73427c57Sahl 		    if (optind + 1 < args.length) {
343*73427c57Sahl 			System.arraycopy(args, optind + 1, args, optind,
344*73427c57Sahl 				args.length - (optind + 1));
345*73427c57Sahl 			args[args.length - 1] = tmp;
346*73427c57Sahl 		    }
347*73427c57Sahl 		    --argc;
348*73427c57Sahl 		}
349*73427c57Sahl 	    } while (nonOption);
350*73427c57Sahl 	}
351*73427c57Sahl 
352*73427c57Sahl 	/*
353*73427c57Sahl 	 * Getting this far indicates that an option has been encountered.
354*73427c57Sahl 	 * Note that the syntax of optstring applies special meanings to
355*73427c57Sahl 	 * the characters ':' and '(', so they are not permissible as
356*73427c57Sahl 	 * option letters. A special meaning is also applied to the ')'
357*73427c57Sahl 	 * character, but its meaning can be determined from context.
358*73427c57Sahl 	 * Note that the specification only requires that the alnum
359*73427c57Sahl 	 * characters be accepted.
360*73427c57Sahl 	 *
361*73427c57Sahl 	 * If the second character of the argument is a '-' this must be
362*73427c57Sahl 	 * a long-option, otherwise it must be a short option.  Scan for
363*73427c57Sahl 	 * the option in optstring by the appropriate algorithm. Either
364*73427c57Sahl 	 * scan will return an index to the short-option character in
365*73427c57Sahl 	 * optstring if the option is found and -1 otherwise.
366*73427c57Sahl 	 *
367*73427c57Sahl 	 * For an unrecognized long-option, optopt will equal 0, but
368*73427c57Sahl 	 * since long-options can't aggregate the failing option can be
369*73427c57Sahl 	 * identified by argv[optind-1].
370*73427c57Sahl 	 */
371*73427c57Sahl 	optopt = c = args[optind].charAt(_sp);
372*73427c57Sahl 	optarg = null;
373*73427c57Sahl 	longopt = (_sp == 1 && c == '-');
374*73427c57Sahl 	if (!(longopt
375*73427c57Sahl 		? ((cp = parselong(optstring, args[optind].substring(2),
376*73427c57Sahl 		longoptarg)) != -1)
377*73427c57Sahl 		: ((cp = parseshort(optstring, c)) != -1))) {
378*73427c57Sahl 	    err("%s: illegal option -- %s", c,
379*73427c57Sahl 		    (longopt ? args[optind] : null));
380*73427c57Sahl 	    /*
381*73427c57Sahl 	     * Note: When the long option is unrecognized, optopt will
382*73427c57Sahl 	     * be '-' here, which matches the specification.
383*73427c57Sahl 	     */
384*73427c57Sahl 	    if (args[optind].length() == ++_sp || longopt) {
385*73427c57Sahl 		++optind;
386*73427c57Sahl 		_sp = 1;
387*73427c57Sahl 	    }
388*73427c57Sahl 	    return '?';
389*73427c57Sahl 	}
390*73427c57Sahl 	optopt = c = optstring.charAt(cp);
391*73427c57Sahl 
392*73427c57Sahl 	/*
393*73427c57Sahl 	 * A valid option has been identified.  If it should have an
394*73427c57Sahl 	 * option-argument, process that now.  SUS defines the setting
395*73427c57Sahl 	 * of optarg as follows:
396*73427c57Sahl 	 *
397*73427c57Sahl 	 *   1.	If the option was the last character in an element of
398*73427c57Sahl 	 *   argv, then optarg contains the next element of argv, and
399*73427c57Sahl 	 *   optind is incremented by 2. If the resulting value of
400*73427c57Sahl 	 *   optind is not less than argc, this indicates a missing
401*73427c57Sahl 	 *   option-argument, and getopt() returns an error indication.
402*73427c57Sahl 	 *
403*73427c57Sahl 	 *   2.	Otherwise, optarg points to the string following the
404*73427c57Sahl 	 *   option character in that element of argv, and optind is
405*73427c57Sahl 	 *   incremented by 1.
406*73427c57Sahl 	 *
407*73427c57Sahl 	 * The second clause allows -abcd (where b requires an
408*73427c57Sahl 	 * option-argument) to be interpreted as "-a -b cd".
409*73427c57Sahl 	 *
410*73427c57Sahl 	 * Note that the option-argument can legally be an empty string,
411*73427c57Sahl 	 * such as:
412*73427c57Sahl 	 * 	command --option= operand
413*73427c57Sahl 	 * which explicitly sets the value of --option to nil
414*73427c57Sahl 	 */
415*73427c57Sahl 	if (cp + 1 < optstring.length() && optstring.charAt(cp + 1) == ':') {
416*73427c57Sahl 	    // The option takes an argument
417*73427c57Sahl 	    if (!longopt && ((_sp + 1) < args[optind].length())) {
418*73427c57Sahl 		optarg = args[optind++].substring(_sp + 1);
419*73427c57Sahl 	    } else if (longopt && (longoptarg.get() != null)) {
420*73427c57Sahl 		/*
421*73427c57Sahl 		 * The option argument was explicitly set to the empty
422*73427c57Sahl 		 * string on the command line (--option=)
423*73427c57Sahl 		 */
424*73427c57Sahl 		optind++;
425*73427c57Sahl 		optarg = longoptarg.get();
426*73427c57Sahl 	    } else if (++optind >= argc) {
427*73427c57Sahl 		err("%s: option requires an argument -- %s", c,
428*73427c57Sahl 			(longopt ? args[optind - 1] : null));
429*73427c57Sahl 		_sp = 1;
430*73427c57Sahl 		optarg = null;
431*73427c57Sahl 		return (optstring.charAt(0) == ':' ? ':' : '?');
432*73427c57Sahl 	    } else
433*73427c57Sahl 		optarg = args[optind++];
434*73427c57Sahl 		_sp = 1;
435*73427c57Sahl 	    } else {
436*73427c57Sahl 		// The option does NOT take an argument
437*73427c57Sahl 		if (longopt && (longoptarg.get() != null)) {
438*73427c57Sahl 		// User supplied an arg to an option that takes none
439*73427c57Sahl 		err("%s: option doesn't take an argument -- %s", (char)0,
440*73427c57Sahl 			(longopt ? args[optind] : null));
441*73427c57Sahl 		optarg = longoptarg.set(null).get();
442*73427c57Sahl 		c = '?';
443*73427c57Sahl 	    }
444*73427c57Sahl 
445*73427c57Sahl 	    if (longopt || args[optind].length() == ++_sp) {
446*73427c57Sahl 		_sp = 1;
447*73427c57Sahl 		++optind;
448*73427c57Sahl 	    }
449*73427c57Sahl 	    optarg = null;
450*73427c57Sahl 	}
451*73427c57Sahl 	return (c);
452*73427c57Sahl     }
453*73427c57Sahl }
454