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