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