/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * ident "%Z%%M% %I% %E% SMI" */ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ import java.io.StringWriter; import java.io.PrintWriter; /** * A Java port of Solaris {@code lib/libc/port/gen/getopt.c}, which is a * port of System V UNIX getopt. See getopt(3C) and SUS/XPG * getopt() for function definition and requirements. Unlike that * definition, this implementation moves non-options to the end of the * argv array rather than quitting at the first non-option. */ public class Getopt { static final int EOF = -1; private String progname; private String[] args; private int argc; private String optstring; private int optind = 0; // args index private int optopt = 0; private String optarg = null; private boolean opterr = true; /* * _sp is required to keep state between successive calls to * getopt() while extracting aggregated short-options (ie: -abcd). */ private int _sp = 1; /** * Creates a {Code Getopt} instance to parse the given command-line * arguments. Modifies the given args array by swapping the * positions of non-options and options so that non-options appear * at the end of the array. */ public Getopt(String programName, String[] args, String optionString) { progname = programName; // No defensive copy; Getopt is expected to modify the given // args array this.args = args; argc = this.args.length; optstring = optionString; validate(); } private void validate() { if (progname == null) { throw new NullPointerException("program name is null"); } int i = 0; for (String s : args) { if (s == null) { throw new NullPointerException("null arg at index " + i); } ++i; } if (optstring == null) { throw new NullPointerException("option string is null"); } } private static class StringRef { private String s; public String get() { return s; } public StringRef set(String value) { s = value; return this; } } /* * Generalized error processing method. If the optstr parameter is * null, the character c is converted to a string and displayed * instead. */ void err(String format, char c, String optstr) { if (opterr && optstring.charAt(0) != ':') { StringWriter w = new StringWriter(); PrintWriter p = new PrintWriter(w); p.printf(format, progname, (optstr == null ? Character.toString(c) : optstr.substring(2))); System.err.println(w.toString()); } } /* * Determine if the specified character (c) is present in the string * (optstring) as a regular, single character option. If the option * is found, return an index into optstring where the short-option * character is found, otherwise return -1. The characters ':' and * '(' are not allowed. */ static int parseshort(String optstring, char c) { if (c == ':' || c == '(') { return -1; } int ch; int len = optstring.length(); for (int i = 0; i < len; ++i) { ch = optstring.charAt(i); if (ch == c) { return i; } while (i < len && ch == '(') { for (++i; i < len && (ch = optstring.charAt(i)) != ')'; ++i); } } return -1; } /** * Determine if the specified string (opt) is present in the string * (optstring) as a long-option contained within parenthesis. If the * long-option specifies option-argument, return a reference to it * in longoptarg. Otherwise set the longoptarg reference to null. * If the option is found, return an index into optstring at the * position of the short-option character associated with the * long-option; otherwise return -1. * * @param optstring the entire optstring passed to the {@code * Getopt} constructor * @param opt the long option read from the command line * @param longoptarg the value of the option is returned in this * parameter, if an option exists. Possible return values in * longoptarg are: * * @return index to equivalent short-option in optstring, or -1 if * option not found in optstring. */ static int parselong(String optstring, String opt, StringRef longoptarg) { int cp; // index into optstring, beginning of one option spec int ip; // index into optstring, traverses every char char ic; // optstring char int il; // optstring length int op; // index into opt char oc; // opt char int ol; // opt length boolean match; // true if opt is matching part of optstring longoptarg.set(null); cp = ip = 0; il = optstring.length(); ol = opt.length(); do { ic = optstring.charAt(ip); if (ic != '(' && ++ip == il) break; ic = optstring.charAt(ip); if (ic == ':' && ++ip == il) break; ic = optstring.charAt(ip); while (ic == '(') { if (++ip == il) break; op = 0; match = true; while (ip < il && (ic = optstring.charAt(ip)) != ')' && op < ol) { oc = opt.charAt(op++); match = (ic == oc && match); ++ip; } if (match && ip < il && ic == ')' && (op >= ol || opt.charAt(op) == '=')) { if (op < ol && opt.charAt(op) == '=') { /* may be an empty string - OK */ longoptarg.set(opt.substring(op + 1)); } else { longoptarg.set(null); } return cp; } if (ip < il && ic == ')' && ++ip == il) break; ic = optstring.charAt(ip); } cp = ip; /* * Handle double-colon in optstring ("a::(longa)") The old * getopt() accepts it and treats it as a required argument. */ while ((cp > 0) && (cp < il) && (optstring.charAt(cp) == ':')) { --cp; } } while (cp < il); return -1; } /** * Get the current option value. */ public String getOptarg() { return optarg; } /** * Get the index of the next option to be parsed. */ public int getOptind() { return optind; } /** * Gets the command-line arguments. */ public String[] getArgv() { // No defensive copy: Getopt is expected to modify the given // args array. return args; } /** * Gets the aggregated short option that just failed. Since long * options can't be aggregated, a failed long option can be obtained * by {@code getArgv()[getOptind() - 1]}. */ public int getOptopt() { return optopt; } /** * Set to {@code false} to suppress diagnostic messages to stderr. */ public void setOpterr(boolean err) { opterr = err; } /** * Gets the next option character, or -1 if there are no more * options. If getopt() encounters a short-option character or a * long-option string not described in the {@code optionString} * argument to the constructor, it returns the question-mark (?) * character. If it detects a missing option-argument, it also * returns the question-mark (?) character, unless the first * character of the {@code optionString} argument was a colon (:), * in which case getopt() returns the colon (:) character. *

* This implementation swaps the positions of options and * non-options in the given argv array. */ public int getopt() { char c; int cp; boolean longopt; StringRef longoptarg = new StringRef(); /* * Has the end of the options been encountered? The following * implements the SUS requirements: * * If, when getopt() is called: * - the first character of argv[optind] is not '-' * - argv[optind] is the string "-" * getopt() returns -1 without changing optind if * - argv[optind] is the string "--" * getopt() returns -1 after incrementing optind */ if (_sp == 1) { boolean nonOption; do { nonOption = false; if (optind >= argc || args[optind].equals("-")) { return EOF; } else if (args[optind].equals("--")) { ++optind; return EOF; } else if (args[optind].charAt(0) != '-') { // non-option: here we deviate from the SUS requirements // by not quitting, and instead move non-options to the // end of the args array nonOption = true; String tmp = args[optind]; if (optind + 1 < args.length) { System.arraycopy(args, optind + 1, args, optind, args.length - (optind + 1)); args[args.length - 1] = tmp; } --argc; } } while (nonOption); } /* * Getting this far indicates that an option has been encountered. * Note that the syntax of optstring applies special meanings to * the characters ':' and '(', so they are not permissible as * option letters. A special meaning is also applied to the ')' * character, but its meaning can be determined from context. * Note that the specification only requires that the alnum * characters be accepted. * * If the second character of the argument is a '-' this must be * a long-option, otherwise it must be a short option. Scan for * the option in optstring by the appropriate algorithm. Either * scan will return an index to the short-option character in * optstring if the option is found and -1 otherwise. * * For an unrecognized long-option, optopt will equal 0, but * since long-options can't aggregate the failing option can be * identified by argv[optind-1]. */ optopt = c = args[optind].charAt(_sp); optarg = null; longopt = (_sp == 1 && c == '-'); if (!(longopt ? ((cp = parselong(optstring, args[optind].substring(2), longoptarg)) != -1) : ((cp = parseshort(optstring, c)) != -1))) { err("%s: illegal option -- %s", c, (longopt ? args[optind] : null)); /* * Note: When the long option is unrecognized, optopt will * be '-' here, which matches the specification. */ if (args[optind].length() == ++_sp || longopt) { ++optind; _sp = 1; } return '?'; } optopt = c = optstring.charAt(cp); /* * A valid option has been identified. If it should have an * option-argument, process that now. SUS defines the setting * of optarg as follows: * * 1. If the option was the last character in an element of * argv, then optarg contains the next element of argv, and * optind is incremented by 2. If the resulting value of * optind is not less than argc, this indicates a missing * option-argument, and getopt() returns an error indication. * * 2. Otherwise, optarg points to the string following the * option character in that element of argv, and optind is * incremented by 1. * * The second clause allows -abcd (where b requires an * option-argument) to be interpreted as "-a -b cd". * * Note that the option-argument can legally be an empty string, * such as: * command --option= operand * which explicitly sets the value of --option to nil */ if (cp + 1 < optstring.length() && optstring.charAt(cp + 1) == ':') { // The option takes an argument if (!longopt && ((_sp + 1) < args[optind].length())) { optarg = args[optind++].substring(_sp + 1); } else if (longopt && (longoptarg.get() != null)) { /* * The option argument was explicitly set to the empty * string on the command line (--option=) */ optind++; optarg = longoptarg.get(); } else if (++optind >= argc) { err("%s: option requires an argument -- %s", c, (longopt ? args[optind - 1] : null)); _sp = 1; optarg = null; return (optstring.charAt(0) == ':' ? ':' : '?'); } else optarg = args[optind++]; _sp = 1; } else { // The option does NOT take an argument if (longopt && (longoptarg.get() != null)) { // User supplied an arg to an option that takes none err("%s: option doesn't take an argument -- %s", (char)0, (longopt ? args[optind] : null)); optarg = longoptarg.set(null).get(); c = '?'; } if (longopt || args[optind].length() == ++_sp) { _sp = 1; ++optind; } optarg = null; } return (c); } }