diff options
Diffstat (limited to 'web/server/h2o/libh2o/misc/oktavia/src/getopt.jsx')
-rw-r--r-- | web/server/h2o/libh2o/misc/oktavia/src/getopt.jsx | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/misc/oktavia/src/getopt.jsx b/web/server/h2o/libh2o/misc/oktavia/src/getopt.jsx new file mode 100644 index 000000000..56db655a9 --- /dev/null +++ b/web/server/h2o/libh2o/misc/oktavia/src/getopt.jsx @@ -0,0 +1,356 @@ +/* + * getopt.js: node.js implementation of POSIX getopt() (and then some) + * + * Copyright 2011 David Pacheco. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import "console.jsx"; + +class CommandOption +{ + var option : string; + var optarg : Nullable.<string>; + var error : boolean; + + function constructor (option : string) + { + this.option = option; + this.optarg = null; + this.error = false; + } + + function constructor (option : string, optarg : string) + { + this.option = option; + this.optarg = optarg; + this.error = false; + } + + function constructor (option : string, optarg : string, error : boolean) + { + this.option = option; + this.optarg = optarg; + this.error = error; + } +} + + +/* + * The BasicParser is our primary interface to the outside world. The + * documentation for this object and its public methods is contained in + * the included README.md. + */ +class BasicParser +{ + var _argv : string[]; + var _options : Map.<boolean>; + var _aliases : Map.<string>; + var _optind : int; + var _subind : int; + var _silent : boolean; + var _extraoptions : boolean; + + function constructor (optstring : string, argv : string[]) + { + this._argv = argv; + this._options = {} : Map.<boolean>; + this._aliases = {} : Map.<string>; + this._optind = 0; + this._subind = 0; + this._extraoptions = false; + + this._parseOptstr(optstring); + } + + static function _makeError (msg : string) : Error + { + return (new Error('getopt: ' + msg)); + } + + /* + * Parse the option string and update the following fields: + * + * _silent Whether to log errors to stderr. Silent mode is + * indicated by a leading ':' in the option string. + * + * _options Maps valid single-letter-options to booleans indicating + * whether each option is required. + * + * _aliases Maps valid long options to the corresponding + * single-letter short option. + */ + function _parseOptstr (optstr : string) : void + { + var i = 0; + + if (optstr.length > 0 && optstr.slice(0, 1) == ':') + { + this._silent = true; + i++; + } + else + { + this._silent = false; + } + while (i < optstr.length) + { + var chr = optstr.slice(i, i + 1); + var arg = false; + + if (!/^[\w\d]$/.test(chr)) + { + throw new Error('invalid optstring: only alphanumeric ' + + 'characters may be used as options: ' + chr); + } + + if (i + 1 < optstr.length && optstr.slice(i + 1, i + 2) == ':') + { + arg = true; + i++; + } + + this._options[chr] = arg; + + while (i + 1 < optstr.length && optstr.slice(i + 1, i + 2) == '(') + { + i++; + var cp = optstr.indexOf(')', i + 1); + if (cp == -1) + { + throw new Error('invalid optstring: missing ' + + '")" to match "(" at char ' + i as string); + } + var alias = optstr.substring(i + 1, cp); + this._aliases[alias] = chr; + i = cp; + } + i++; + } + } + + function optind () : int + { + return this._optind; + } + + /* + * For documentation on what getopt() does, see README.md. The following + * implementation invariants are maintained by getopt() and its helper methods: + * + * this._optind Refers to the element of _argv that contains + * the next argument to be processed. This may + * exceed _argv, in which case the end of input + * has been reached. + * + * this._subind Refers to the character inside + * this._options[this._optind] which begins + * the next option to be processed. This may never + * exceed the length of _argv[_optind], so + * when incrementing this value we must always + * check if we should instead increment optind and + * reset subind to 0. + * + * That is, when any of these functions is entered, the above indices' values + * are as described above. getopt() itself and getoptArgument() may both be + * called at the end of the input, so they check whether optind exceeds + * argv.length. getoptShort() and getoptLong() are called only when the indices + * already point to a valid short or long option, respectively. + * + * getopt() processes the next option as follows: + * + * o If _optind > _argv.length, then we already parsed all arguments. + * + * o If _subind == 0, then we're looking at the start of an argument: + * + * o Check for special cases like '-', '--', and non-option arguments. + * If present, update the indices and return the appropriate value. + * + * o Check for a long-form option (beginning with '--'). If present, + * delegate to getoptLong() and return the result. + * + * o Otherwise, advance subind past the argument's leading '-' and + * continue as though _subind != 0 (since that's now the case). + * + * o Delegate to getoptShort() and return the result. + */ + function getopt () : Nullable.<CommandOption> + { + if (this._optind >= this._argv.length) + { + /* end of input */ + return null; + } + + var arg = this._argv[this._optind]; + if (this._extraoptions) + { + this._optind++; + return new CommandOption(arg); + } + + if (this._subind == 0) + { + if (arg == '-' || arg == '') + { + return null; + } + + if (arg.charAt(0) != '-') + { + this._extraoptions = true; + this._optind++; + return new CommandOption(arg); + } + + if (arg == '--') + { + this._optind++; + this._subind = 0; + return null; + } + + if (arg.slice(1, 2) == '-') + { + return this._getoptLong(); + } + this._subind++; + } + + return this._getoptShort(); + } + + /* + * Implements getopt() for the case where optind/subind point to a short option. + */ + function _getoptShort () : CommandOption + { + var arg = this._argv[this._optind]; + var chr = arg.slice(this._subind, this._subind + 1); + + if (++this._subind >= arg.length) + { + this._optind++; + this._subind = 0; + } + + if (!(chr in this._options)) + { + return this._errInvalidOption(chr); + } + + if (!this._options[chr]) + { + return new CommandOption(chr); + } + return this._getoptArgument(chr); + } + + /* + * Implements getopt() for the case where optind/subind point to a long option. + */ + function _getoptLong () : CommandOption + { + var arg = this._argv[this._optind]; + var eq = arg.indexOf('='); + var alias = arg.substring(2, eq == -1 ? arg.length : eq); + if (!(alias in this._aliases)) + { + return this._errInvalidOption(alias); + } + + var chr = this._aliases[alias]; + if (!this._options[chr]) + { + if (eq != -1) + { + return this._errExtraArg(alias); + } + this._optind++; /* eat this argument */ + return new CommandOption(chr); + } + + /* + * Advance optind/subind for the argument value and retrieve it. + */ + if (eq == -1) + { + this._optind++; + } + else + { + this._subind = eq + 1; + } + return this._getoptArgument(chr); + } + + /* + * For the given option letter 'chr' that takes an argument, assumes that + * optind/subind point to the argument (or denote the end of input) and return + * the appropriate getopt() return value for this option and argument (or return + * the appropriate error). + */ + function _getoptArgument (chr : string) : CommandOption + { + if (this._optind >= this._argv.length) + { + return this._errMissingArg(chr); + } + + var arg = this._argv[this._optind].substring(this._subind); + this._optind++; + this._subind = 0; + return new CommandOption(chr, arg); + } + + function _errMissingArg (chr : string) : CommandOption + { + if (this._silent) + { + return new CommandOption(':', chr); + } + console.error('option requires an argument -- ' + chr + '\n'); + return new CommandOption('?', chr, true); + } + + function _errInvalidOption (chr : string) : CommandOption + { + if (!this._silent) + { + console.error('illegal option -- ' + chr + '\n'); + } + return new CommandOption('?', chr, true); + } + + /* + * This error is not specified by POSIX, but neither is the notion of specifying + * long option arguments using "=" in the same argv-argument, but it's common + * practice and pretty convenient. + */ + function _errExtraArg (chr : string) : CommandOption + { + if (!this._silent) + { + console.error('option expects no argument -- ' + + chr + '\n'); + } + return new CommandOption('?', chr, true); + } +} |