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