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