1178476Sjb/*
2178476Sjb * CDDL HEADER START
3178476Sjb *
4178476Sjb * The contents of this file are subject to the terms of the
5178476Sjb * Common Development and Distribution License (the "License").
6178476Sjb * You may not use this file except in compliance with the License.
7178476Sjb *
8178476Sjb * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9178476Sjb * or http://www.opensolaris.org/os/licensing.
10178476Sjb * See the License for the specific language governing permissions
11178476Sjb * and limitations under the License.
12178476Sjb *
13178476Sjb * When distributing Covered Code, include this CDDL HEADER in each
14178476Sjb * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15178476Sjb * If applicable, add the following below this CDDL HEADER, with the
16178476Sjb * fields enclosed by brackets "[]" replaced with your own identifying
17178476Sjb * information: Portions Copyright [yyyy] [name of copyright owner]
18178476Sjb *
19178476Sjb * CDDL HEADER END
20178476Sjb */
21178476Sjb
22178476Sjb/*
23178476Sjb * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24178476Sjb * Use is subject to license terms.
25178476Sjb *
26178476Sjb * ident	"%Z%%M%	%I%	%E% SMI"
27178476Sjb */
28178476Sjb
29178476Sjb/* Copyright (c) 1988 AT&T */
30178476Sjb/* All Rights Reserved */
31178476Sjb
32178476Sjbimport java.io.StringWriter;
33178476Sjbimport java.io.PrintWriter;
34178476Sjb
35178476Sjb/**
36178476Sjb * A Java port of Solaris {@code lib/libc/port/gen/getopt.c}, which is a
37178476Sjb * port of System V UNIX getopt.  See <b>getopt(3C)</b> and SUS/XPG
38178476Sjb * getopt() for function definition and requirements. Unlike that
39178476Sjb * definition, this implementation moves non-options to the end of the
40178476Sjb * argv array rather than quitting at the first non-option.
41178476Sjb */
42178476Sjbpublic class Getopt {
43178476Sjb    static final int EOF = -1;
44178476Sjb
45178476Sjb    private String progname;
46178476Sjb    private String[] args;
47178476Sjb    private int argc;
48178476Sjb    private String optstring;
49178476Sjb    private int optind = 0; // args index
50178476Sjb    private int optopt = 0;
51178476Sjb    private String optarg = null;
52178476Sjb    private boolean opterr = true;
53178476Sjb
54178476Sjb    /*
55178476Sjb     * _sp is required to keep state between successive calls to
56178476Sjb     * getopt() while extracting aggregated short-options (ie: -abcd).
57178476Sjb     */
58178476Sjb    private int _sp = 1;
59178476Sjb
60178476Sjb    /**
61178476Sjb     * Creates a {Code Getopt} instance to parse the given command-line
62178476Sjb     * arguments. Modifies the given args array by swapping the
63178476Sjb     * positions of non-options and options so that non-options appear
64178476Sjb     * at the end of the array.
65178476Sjb     */
66178476Sjb    public Getopt(String programName, String[] args,
67178476Sjb	    String optionString)
68178476Sjb    {
69178476Sjb	progname = programName;
70178476Sjb	// No defensive copy; Getopt is expected to modify the given
71178476Sjb	// args array
72178476Sjb	this.args = args;
73178476Sjb	argc = this.args.length;
74178476Sjb	optstring = optionString;
75178476Sjb	validate();
76178476Sjb    }
77178476Sjb
78178476Sjb    private void
79178476Sjb    validate()
80178476Sjb    {
81178476Sjb	if (progname == null) {
82178476Sjb	    throw new NullPointerException("program name is null");
83178476Sjb	}
84178476Sjb	int i = 0;
85178476Sjb	for (String s : args) {
86178476Sjb	    if (s == null) {
87178476Sjb		throw new NullPointerException("null arg at index " + i);
88178476Sjb	    }
89178476Sjb	    ++i;
90178476Sjb	}
91178476Sjb	if (optstring == null) {
92178476Sjb	    throw new NullPointerException("option string is null");
93178476Sjb	}
94178476Sjb    }
95178476Sjb
96178476Sjb    private static class StringRef {
97178476Sjb	private String s;
98178476Sjb
99178476Sjb	public String
100178476Sjb	get()
101178476Sjb	{
102178476Sjb	    return s;
103178476Sjb	}
104178476Sjb
105178476Sjb	public StringRef
106178476Sjb	set(String value)
107178476Sjb	{
108178476Sjb	    s = value;
109178476Sjb	    return this;
110178476Sjb	}
111178476Sjb    }
112178476Sjb
113178476Sjb    /*
114178476Sjb     * Generalized error processing method. If the optstr parameter is
115178476Sjb     * null, the character c is converted to a string and displayed
116178476Sjb     * instead.
117178476Sjb     */
118178476Sjb    void
119178476Sjb    err(String format, char c, String optstr)
120178476Sjb    {
121178476Sjb	if (opterr && optstring.charAt(0) != ':') {
122178476Sjb	    StringWriter w = new StringWriter();
123178476Sjb	    PrintWriter p = new PrintWriter(w);
124178476Sjb	    p.printf(format, progname, (optstr == null ?
125178476Sjb		    Character.toString(c) : optstr.substring(2)));
126178476Sjb	    System.err.println(w.toString());
127178476Sjb	}
128178476Sjb    }
129178476Sjb
130178476Sjb    /*
131178476Sjb     * Determine if the specified character (c) is present in the string
132178476Sjb     * (optstring) as a regular, single character option. If the option
133178476Sjb     * is found, return an index into optstring where the short-option
134178476Sjb     * character is found, otherwise return -1. The characters ':' and
135178476Sjb     * '(' are not allowed.
136178476Sjb     */
137178476Sjb    static int
138178476Sjb    parseshort(String optstring, char c)
139178476Sjb    {
140178476Sjb	if (c == ':' || c == '(') {
141178476Sjb	    return -1;
142178476Sjb	}
143178476Sjb
144178476Sjb	int ch;
145178476Sjb	int len = optstring.length();
146178476Sjb	for (int i = 0; i < len; ++i) {
147178476Sjb	    ch = optstring.charAt(i);
148178476Sjb	    if (ch == c) {
149178476Sjb		return i;
150178476Sjb	    }
151178476Sjb
152178476Sjb	    while (i < len && ch == '(') {
153178476Sjb		for (++i; i < len && (ch = optstring.charAt(i)) != ')'; ++i);
154178476Sjb	    }
155178476Sjb	}
156178476Sjb
157178476Sjb	return -1;
158178476Sjb    }
159178476Sjb
160178476Sjb    /**
161178476Sjb     * Determine if the specified string (opt) is present in the string
162178476Sjb     * (optstring) as a long-option contained within parenthesis. If the
163178476Sjb     * long-option specifies option-argument, return a reference to it
164178476Sjb     * in longoptarg.  Otherwise set the longoptarg reference to null.
165178476Sjb     * If the option is found, return an index into optstring at the
166178476Sjb     * position of the short-option character associated with the
167178476Sjb     * long-option; otherwise return -1.
168178476Sjb     *
169178476Sjb     * @param optstring	the entire optstring passed to the {@code
170178476Sjb     * Getopt} constructor
171178476Sjb     * @param opt the long option read from the command line
172178476Sjb     * @param longoptarg the value of the option is returned in this
173178476Sjb     * parameter, if an option exists. Possible return values in
174178476Sjb     * longoptarg are:
175178476Sjb     * <ul>
176178476Sjb     * <li><b>NULL:</b> No argument was found</li>
177178476Sjb     * <li><b>empty string (""):</b> Argument was explicitly left empty
178178476Sjb     * by the user (e.g., --option= )</li>
179178476Sjb     * <li><b>valid string:</b> Argument found on the command line</li>
180178476Sjb     * </ul>
181178476Sjb     * @return index to equivalent short-option in optstring, or -1 if
182178476Sjb     * option not found in optstring.
183178476Sjb     */
184178476Sjb    static int
185178476Sjb    parselong(String optstring, String opt, StringRef longoptarg)
186178476Sjb    {
187178476Sjb	int cp; // index into optstring, beginning of one option spec
188178476Sjb	int ip; // index into optstring, traverses every char
189178476Sjb	char ic; // optstring char
190178476Sjb	int il; // optstring length
191178476Sjb	int op;	// index into opt
192178476Sjb	char oc; // opt char
193178476Sjb	int ol; // opt length
194178476Sjb	boolean	match; // true if opt is matching part of optstring
195178476Sjb
196178476Sjb	longoptarg.set(null);
197178476Sjb	cp = ip = 0;
198178476Sjb	il = optstring.length();
199178476Sjb	ol = opt.length();
200178476Sjb	do {
201178476Sjb	    ic = optstring.charAt(ip);
202178476Sjb	    if (ic != '(' && ++ip == il)
203178476Sjb		break;
204178476Sjb	    ic = optstring.charAt(ip);
205178476Sjb	    if (ic == ':' && ++ip == il)
206178476Sjb		break;
207178476Sjb	    ic = optstring.charAt(ip);
208178476Sjb	    while (ic == '(') {
209178476Sjb		if (++ip == il)
210178476Sjb		    break;
211178476Sjb		op = 0;
212178476Sjb		match = true;
213178476Sjb		while (ip < il && (ic = optstring.charAt(ip)) != ')' &&
214178476Sjb			op < ol) {
215178476Sjb		    oc = opt.charAt(op++);
216178476Sjb		    match = (ic == oc && match);
217178476Sjb		    ++ip;
218178476Sjb		}
219178476Sjb
220178476Sjb		if (match && ip < il && ic == ')' && (op >= ol ||
221178476Sjb			opt.charAt(op) == '=')) {
222178476Sjb		    if (op < ol && opt.charAt(op) == '=') {
223178476Sjb			/* may be an empty string - OK */
224178476Sjb			longoptarg.set(opt.substring(op + 1));
225178476Sjb		    } else {
226178476Sjb			longoptarg.set(null);
227178476Sjb		    }
228178476Sjb		    return cp;
229178476Sjb		}
230178476Sjb		if (ip < il && ic == ')' && ++ip == il)
231178476Sjb		    break;
232178476Sjb		ic = optstring.charAt(ip);
233178476Sjb	    }
234178476Sjb	    cp = ip;
235178476Sjb	    /*
236178476Sjb	     * Handle double-colon in optstring ("a::(longa)") The old
237178476Sjb	     * getopt() accepts it and treats it as a required argument.
238178476Sjb	     */
239178476Sjb	    while ((cp > 0) && (cp < il) && (optstring.charAt(cp) == ':')) {
240178476Sjb		--cp;
241178476Sjb	    }
242178476Sjb	} while (cp < il);
243178476Sjb	return -1;
244178476Sjb    }
245178476Sjb
246178476Sjb    /**
247178476Sjb     * Get the current option value.
248178476Sjb     */
249178476Sjb    public String
250178476Sjb    getOptarg()
251178476Sjb    {
252178476Sjb	return optarg;
253178476Sjb    }
254178476Sjb
255178476Sjb    /**
256178476Sjb     * Get the index of the next option to be parsed.
257178476Sjb     */
258178476Sjb    public int
259178476Sjb    getOptind()
260178476Sjb    {
261178476Sjb	return optind;
262178476Sjb    }
263178476Sjb
264178476Sjb    /**
265178476Sjb     * Gets the command-line arguments.
266178476Sjb     */
267178476Sjb    public String[]
268178476Sjb    getArgv()
269178476Sjb    {
270178476Sjb	// No defensive copy: Getopt is expected to modify the given
271178476Sjb	// args array.
272178476Sjb	return args;
273178476Sjb    }
274178476Sjb
275178476Sjb    /**
276178476Sjb     * Gets the aggregated short option that just failed. Since long
277178476Sjb     * options can't be aggregated, a failed long option can be obtained
278178476Sjb     * by {@code getArgv()[getOptind() - 1]}.
279178476Sjb     */
280178476Sjb    public int
281178476Sjb    getOptopt()
282178476Sjb    {
283178476Sjb	return optopt;
284178476Sjb    }
285178476Sjb
286178476Sjb    /**
287178476Sjb     * Set to {@code false} to suppress diagnostic messages to stderr.
288178476Sjb     */
289178476Sjb    public void
290178476Sjb    setOpterr(boolean err)
291178476Sjb    {
292178476Sjb	opterr = err;
293178476Sjb    }
294178476Sjb
295178476Sjb    /**
296178476Sjb     * Gets the next option character, or -1 if there are no more
297178476Sjb     * options. If getopt() encounters a short-option character or a
298178476Sjb     * long-option string not described in the {@code optionString}
299178476Sjb     * argument to the constructor, it returns the question-mark (?)
300178476Sjb     * character. If it detects a missing option-argument, it also
301178476Sjb     * returns the question-mark (?) character, unless the first
302178476Sjb     * character of the {@code optionString} argument was a colon (:),
303178476Sjb     * in which case getopt() returns the colon (:) character.
304178476Sjb     * <p>
305178476Sjb     * This implementation swaps the positions of options and
306178476Sjb     * non-options in the given argv array.
307178476Sjb     */
308178476Sjb    public int
309178476Sjb    getopt()
310178476Sjb    {
311178476Sjb	char c;
312178476Sjb	int cp;
313178476Sjb	boolean longopt;
314178476Sjb	StringRef longoptarg = new StringRef();
315178476Sjb
316178476Sjb	/*
317178476Sjb	 * Has the end of the options been encountered?  The following
318178476Sjb	 * implements the SUS requirements:
319178476Sjb	 *
320178476Sjb	 * If, when getopt() is called:
321178476Sjb	 *	- the first character of argv[optind] is not '-'
322178476Sjb	 *	- argv[optind] is the string "-"
323178476Sjb	 * getopt() returns -1 without changing optind if
324178476Sjb	 *	- argv[optind] is the string "--"
325178476Sjb	 * getopt() returns -1 after incrementing optind
326178476Sjb	 */
327178476Sjb	if (_sp == 1) {
328178476Sjb	    boolean nonOption;
329178476Sjb	    do {
330178476Sjb		nonOption = false;
331178476Sjb		if (optind >= argc || args[optind].equals("-")) {
332178476Sjb		    return EOF;
333178476Sjb		} else if (args[optind].equals("--")) {
334178476Sjb		    ++optind;
335178476Sjb		    return EOF;
336178476Sjb		} else if (args[optind].charAt(0) != '-') {
337178476Sjb		    // non-option: here we deviate from the SUS requirements
338178476Sjb		    // by not quitting, and instead move non-options to the
339178476Sjb		    // end of the args array
340178476Sjb		    nonOption = true;
341178476Sjb		    String tmp = args[optind];
342178476Sjb		    if (optind + 1 < args.length) {
343178476Sjb			System.arraycopy(args, optind + 1, args, optind,
344178476Sjb				args.length - (optind + 1));
345178476Sjb			args[args.length - 1] = tmp;
346178476Sjb		    }
347178476Sjb		    --argc;
348178476Sjb		}
349178476Sjb	    } while (nonOption);
350178476Sjb	}
351178476Sjb
352178476Sjb	/*
353178476Sjb	 * Getting this far indicates that an option has been encountered.
354178476Sjb	 * Note that the syntax of optstring applies special meanings to
355178476Sjb	 * the characters ':' and '(', so they are not permissible as
356178476Sjb	 * option letters. A special meaning is also applied to the ')'
357178476Sjb	 * character, but its meaning can be determined from context.
358178476Sjb	 * Note that the specification only requires that the alnum
359178476Sjb	 * characters be accepted.
360178476Sjb	 *
361178476Sjb	 * If the second character of the argument is a '-' this must be
362178476Sjb	 * a long-option, otherwise it must be a short option.  Scan for
363178476Sjb	 * the option in optstring by the appropriate algorithm. Either
364178476Sjb	 * scan will return an index to the short-option character in
365178476Sjb	 * optstring if the option is found and -1 otherwise.
366178476Sjb	 *
367178476Sjb	 * For an unrecognized long-option, optopt will equal 0, but
368178476Sjb	 * since long-options can't aggregate the failing option can be
369178476Sjb	 * identified by argv[optind-1].
370178476Sjb	 */
371178476Sjb	optopt = c = args[optind].charAt(_sp);
372178476Sjb	optarg = null;
373178476Sjb	longopt = (_sp == 1 && c == '-');
374178476Sjb	if (!(longopt
375178476Sjb		? ((cp = parselong(optstring, args[optind].substring(2),
376178476Sjb		longoptarg)) != -1)
377178476Sjb		: ((cp = parseshort(optstring, c)) != -1))) {
378178476Sjb	    err("%s: illegal option -- %s", c,
379178476Sjb		    (longopt ? args[optind] : null));
380178476Sjb	    /*
381178476Sjb	     * Note: When the long option is unrecognized, optopt will
382178476Sjb	     * be '-' here, which matches the specification.
383178476Sjb	     */
384178476Sjb	    if (args[optind].length() == ++_sp || longopt) {
385178476Sjb		++optind;
386178476Sjb		_sp = 1;
387178476Sjb	    }
388178476Sjb	    return '?';
389178476Sjb	}
390178476Sjb	optopt = c = optstring.charAt(cp);
391178476Sjb
392178476Sjb	/*
393178476Sjb	 * A valid option has been identified.  If it should have an
394178476Sjb	 * option-argument, process that now.  SUS defines the setting
395178476Sjb	 * of optarg as follows:
396178476Sjb	 *
397178476Sjb	 *   1.	If the option was the last character in an element of
398178476Sjb	 *   argv, then optarg contains the next element of argv, and
399178476Sjb	 *   optind is incremented by 2. If the resulting value of
400178476Sjb	 *   optind is not less than argc, this indicates a missing
401178476Sjb	 *   option-argument, and getopt() returns an error indication.
402178476Sjb	 *
403178476Sjb	 *   2.	Otherwise, optarg points to the string following the
404178476Sjb	 *   option character in that element of argv, and optind is
405178476Sjb	 *   incremented by 1.
406178476Sjb	 *
407178476Sjb	 * The second clause allows -abcd (where b requires an
408178476Sjb	 * option-argument) to be interpreted as "-a -b cd".
409178476Sjb	 *
410178476Sjb	 * Note that the option-argument can legally be an empty string,
411178476Sjb	 * such as:
412178476Sjb	 * 	command --option= operand
413178476Sjb	 * which explicitly sets the value of --option to nil
414178476Sjb	 */
415178476Sjb	if (cp + 1 < optstring.length() && optstring.charAt(cp + 1) == ':') {
416178476Sjb	    // The option takes an argument
417178476Sjb	    if (!longopt && ((_sp + 1) < args[optind].length())) {
418178476Sjb		optarg = args[optind++].substring(_sp + 1);
419178476Sjb	    } else if (longopt && (longoptarg.get() != null)) {
420178476Sjb		/*
421178476Sjb		 * The option argument was explicitly set to the empty
422178476Sjb		 * string on the command line (--option=)
423178476Sjb		 */
424178476Sjb		optind++;
425178476Sjb		optarg = longoptarg.get();
426178476Sjb	    } else if (++optind >= argc) {
427178476Sjb		err("%s: option requires an argument -- %s", c,
428178476Sjb			(longopt ? args[optind - 1] : null));
429178476Sjb		_sp = 1;
430178476Sjb		optarg = null;
431178476Sjb		return (optstring.charAt(0) == ':' ? ':' : '?');
432178476Sjb	    } else
433178476Sjb		optarg = args[optind++];
434178476Sjb		_sp = 1;
435178476Sjb	    } else {
436178476Sjb		// The option does NOT take an argument
437178476Sjb		if (longopt && (longoptarg.get() != null)) {
438178476Sjb		// User supplied an arg to an option that takes none
439178476Sjb		err("%s: option doesn't take an argument -- %s", (char)0,
440178476Sjb			(longopt ? args[optind] : null));
441178476Sjb		optarg = longoptarg.set(null).get();
442178476Sjb		c = '?';
443178476Sjb	    }
444178476Sjb
445178476Sjb	    if (longopt || args[optind].length() == ++_sp) {
446178476Sjb		_sp = 1;
447178476Sjb		++optind;
448178476Sjb	    }
449178476Sjb	    optarg = null;
450178476Sjb	}
451178476Sjb	return (c);
452178476Sjb    }
453178476Sjb}
454