diff options
author | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-16 18:49:26 +0900 |
---|---|---|
committer | Jari Vetoniemi <jari.vetoniemi@indooratlas.com> | 2020-03-30 00:39:06 +0900 |
commit | fcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch) | |
tree | 64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/getoptlong.rb |
Fresh start
Diffstat (limited to 'jni/ruby/lib/getoptlong.rb')
-rw-r--r-- | jni/ruby/lib/getoptlong.rb | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/jni/ruby/lib/getoptlong.rb b/jni/ruby/lib/getoptlong.rb new file mode 100644 index 0000000..cf635f0 --- /dev/null +++ b/jni/ruby/lib/getoptlong.rb @@ -0,0 +1,612 @@ +# +# GetoptLong for Ruby +# +# Copyright (C) 1998, 1999, 2000 Motoyuki Kasahara. +# +# You may redistribute and/or modify this library under the same license +# terms as Ruby. +# +# See GetoptLong for documentation. +# +# Additional documents and the latest version of `getoptlong.rb' can be +# found at http://www.sra.co.jp/people/m-kasahr/ruby/getoptlong/ + +# The GetoptLong class allows you to parse command line options similarly to +# the GNU getopt_long() C library call. Note, however, that GetoptLong is a +# pure Ruby implementation. +# +# GetoptLong allows for POSIX-style options like <tt>--file</tt> as well +# as single letter options like <tt>-f</tt> +# +# The empty option <tt>--</tt> (two minus symbols) is used to end option +# processing. This can be particularly important if options have optional +# arguments. +# +# Here is a simple example of usage: +# +# require 'getoptlong' +# +# opts = GetoptLong.new( +# [ '--help', '-h', GetoptLong::NO_ARGUMENT ], +# [ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ], +# [ '--name', GetoptLong::OPTIONAL_ARGUMENT ] +# ) +# +# dir = nil +# name = nil +# repetitions = 1 +# opts.each do |opt, arg| +# case opt +# when '--help' +# puts <<-EOF +# hello [OPTION] ... DIR +# +# -h, --help: +# show help +# +# --repeat x, -n x: +# repeat x times +# +# --name [name]: +# greet user by name, if name not supplied default is John +# +# DIR: The directory in which to issue the greeting. +# EOF +# when '--repeat' +# repetitions = arg.to_i +# when '--name' +# if arg == '' +# name = 'John' +# else +# name = arg +# end +# end +# end +# +# if ARGV.length != 1 +# puts "Missing dir argument (try --help)" +# exit 0 +# end +# +# dir = ARGV.shift +# +# Dir.chdir(dir) +# for i in (1..repetitions) +# print "Hello" +# if name +# print ", #{name}" +# end +# puts +# end +# +# Example command line: +# +# hello -n 6 --name -- /tmp +# +class GetoptLong + # + # Orderings. + # + ORDERINGS = [REQUIRE_ORDER = 0, PERMUTE = 1, RETURN_IN_ORDER = 2] + + # + # Argument flags. + # + ARGUMENT_FLAGS = [NO_ARGUMENT = 0, REQUIRED_ARGUMENT = 1, + OPTIONAL_ARGUMENT = 2] + + # + # Status codes. + # + STATUS_YET, STATUS_STARTED, STATUS_TERMINATED = 0, 1, 2 + + # + # Error types. + # + class Error < StandardError; end + class AmbiguousOption < Error; end + class NeedlessArgument < Error; end + class MissingArgument < Error; end + class InvalidOption < Error; end + + # + # Set up option processing. + # + # The options to support are passed to new() as an array of arrays. + # Each sub-array contains any number of String option names which carry + # the same meaning, and one of the following flags: + # + # GetoptLong::NO_ARGUMENT :: Option does not take an argument. + # + # GetoptLong::REQUIRED_ARGUMENT :: Option always takes an argument. + # + # GetoptLong::OPTIONAL_ARGUMENT :: Option may or may not take an argument. + # + # The first option name is considered to be the preferred (canonical) name. + # Other than that, the elements of each sub-array can be in any order. + # + def initialize(*arguments) + # + # Current ordering. + # + if ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = PERMUTE + end + + # + # Hash table of option names. + # Keys of the table are option names, and their values are canonical + # names of the options. + # + @canonical_names = Hash.new + + # + # Hash table of argument flags. + # Keys of the table are option names, and their values are argument + # flags of the options. + # + @argument_flags = Hash.new + + # + # Whether error messages are output to $stderr. + # + @quiet = FALSE + + # + # Status code. + # + @status = STATUS_YET + + # + # Error code. + # + @error = nil + + # + # Error message. + # + @error_message = nil + + # + # Rest of catenated short options. + # + @rest_singles = '' + + # + # List of non-option-arguments. + # Append them to ARGV when option processing is terminated. + # + @non_option_arguments = Array.new + + if 0 < arguments.length + set_options(*arguments) + end + end + + # + # Set the handling of the ordering of options and arguments. + # A RuntimeError is raised if option processing has already started. + # + # The supplied value must be a member of GetoptLong::ORDERINGS. It alters + # the processing of options as follows: + # + # <b>REQUIRE_ORDER</b> : + # + # Options are required to occur before non-options. + # + # Processing of options ends as soon as a word is encountered that has not + # been preceded by an appropriate option flag. + # + # For example, if -a and -b are options which do not take arguments, + # parsing command line arguments of '-a one -b two' would result in + # 'one', '-b', 'two' being left in ARGV, and only ('-a', '') being + # processed as an option/arg pair. + # + # This is the default ordering, if the environment variable + # POSIXLY_CORRECT is set. (This is for compatibility with GNU getopt_long.) + # + # <b>PERMUTE</b> : + # + # Options can occur anywhere in the command line parsed. This is the + # default behavior. + # + # Every sequence of words which can be interpreted as an option (with or + # without argument) is treated as an option; non-option words are skipped. + # + # For example, if -a does not require an argument and -b optionally takes + # an argument, parsing '-a one -b two three' would result in ('-a','') and + # ('-b', 'two') being processed as option/arg pairs, and 'one','three' + # being left in ARGV. + # + # If the ordering is set to PERMUTE but the environment variable + # POSIXLY_CORRECT is set, REQUIRE_ORDER is used instead. This is for + # compatibility with GNU getopt_long. + # + # <b>RETURN_IN_ORDER</b> : + # + # All words on the command line are processed as options. Words not + # preceded by a short or long option flag are passed as arguments + # with an option of '' (empty string). + # + # For example, if -a requires an argument but -b does not, a command line + # of '-a one -b two three' would result in option/arg pairs of ('-a', 'one') + # ('-b', ''), ('', 'two'), ('', 'three') being processed. + # + def ordering=(ordering) + # + # The method is failed if option processing has already started. + # + if @status != STATUS_YET + set_error(ArgumentError, "argument error") + raise RuntimeError, + "invoke ordering=, but option processing has already started" + end + + # + # Check ordering. + # + if !ORDERINGS.include?(ordering) + raise ArgumentError, "invalid ordering `#{ordering}'" + end + if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = ordering + end + end + + # + # Return ordering. + # + attr_reader :ordering + + # + # Set options. Takes the same argument as GetoptLong.new. + # + # Raises a RuntimeError if option processing has already started. + # + def set_options(*arguments) + # + # The method is failed if option processing has already started. + # + if @status != STATUS_YET + raise RuntimeError, + "invoke set_options, but option processing has already started" + end + + # + # Clear tables of option names and argument flags. + # + @canonical_names.clear + @argument_flags.clear + + arguments.each do |arg| + if !arg.is_a?(Array) + raise ArgumentError, "the option list contains non-Array argument" + end + + # + # Find an argument flag and it set to `argument_flag'. + # + argument_flag = nil + arg.each do |i| + if ARGUMENT_FLAGS.include?(i) + if argument_flag != nil + raise ArgumentError, "too many argument-flags" + end + argument_flag = i + end + end + + raise ArgumentError, "no argument-flag" if argument_flag == nil + + canonical_name = nil + arg.each do |i| + # + # Check an option name. + # + next if i == argument_flag + begin + if !i.is_a?(String) || i !~ /^-([^-]|-.+)$/ + raise ArgumentError, "an invalid option `#{i}'" + end + if (@canonical_names.include?(i)) + raise ArgumentError, "option redefined `#{i}'" + end + rescue + @canonical_names.clear + @argument_flags.clear + raise + end + + # + # Register the option (`i') to the `@canonical_names' and + # `@canonical_names' Hashes. + # + if canonical_name == nil + canonical_name = i + end + @canonical_names[i] = canonical_name + @argument_flags[i] = argument_flag + end + raise ArgumentError, "no option name" if canonical_name == nil + end + return self + end + + # + # Set/Unset `quiet' mode. + # + attr_writer :quiet + + # + # Return the flag of `quiet' mode. + # + attr_reader :quiet + + # + # `quiet?' is an alias of `quiet'. + # + alias quiet? quiet + + # + # Explicitly terminate option processing. + # + def terminate + return nil if @status == STATUS_TERMINATED + raise RuntimeError, "an error has occurred" if @error != nil + + @status = STATUS_TERMINATED + @non_option_arguments.reverse_each do |argument| + ARGV.unshift(argument) + end + + @canonical_names = nil + @argument_flags = nil + @rest_singles = nil + @non_option_arguments = nil + + return self + end + + # + # Returns true if option processing has terminated, false otherwise. + # + def terminated? + return @status == STATUS_TERMINATED + end + + # + # Set an error (a protected method). + # + def set_error(type, message) + $stderr.print("#{$0}: #{message}\n") if !@quiet + + @error = type + @error_message = message + @canonical_names = nil + @argument_flags = nil + @rest_singles = nil + @non_option_arguments = nil + + raise type, message + end + protected :set_error + + # + # Examine whether an option processing is failed. + # + attr_reader :error + + # + # `error?' is an alias of `error'. + # + alias error? error + + # Return the appropriate error message in POSIX-defined format. + # If no error has occurred, returns nil. + # + def error_message + return @error_message + end + + # + # Get next option name and its argument, as an Array of two elements. + # + # The option name is always converted to the first (preferred) + # name given in the original options to GetoptLong.new. + # + # Example: ['--option', 'value'] + # + # Returns nil if the processing is complete (as determined by + # STATUS_TERMINATED). + # + def get + option_name, option_argument = nil, '' + + # + # Check status. + # + return nil if @error != nil + case @status + when STATUS_YET + @status = STATUS_STARTED + when STATUS_TERMINATED + return nil + end + + # + # Get next option argument. + # + if 0 < @rest_singles.length + argument = '-' + @rest_singles + elsif (ARGV.length == 0) + terminate + return nil + elsif @ordering == PERMUTE + while 0 < ARGV.length && ARGV[0] !~ /^-./ + @non_option_arguments.push(ARGV.shift) + end + if ARGV.length == 0 + terminate + return nil + end + argument = ARGV.shift + elsif @ordering == REQUIRE_ORDER + if (ARGV[0] !~ /^-./) + terminate + return nil + end + argument = ARGV.shift + else + argument = ARGV.shift + end + + # + # Check the special argument `--'. + # `--' indicates the end of the option list. + # + if argument == '--' && @rest_singles.length == 0 + terminate + return nil + end + + # + # Check for long and short options. + # + if argument =~ /^(--[^=]+)/ && @rest_singles.length == 0 + # + # This is a long style option, which start with `--'. + # + pattern = $1 + if @canonical_names.include?(pattern) + option_name = pattern + else + # + # The option `option_name' is not registered in `@canonical_names'. + # It may be an abbreviated. + # + matches = [] + @canonical_names.each_key do |key| + if key.index(pattern) == 0 + option_name = key + matches << key + end + end + if 2 <= matches.length + set_error(AmbiguousOption, "option `#{argument}' is ambiguous between #{matches.join(', ')}") + elsif matches.length == 0 + set_error(InvalidOption, "unrecognized option `#{argument}'") + end + end + + # + # Check an argument to the option. + # + if @argument_flags[option_name] == REQUIRED_ARGUMENT + if argument =~ /=(.*)$/ + option_argument = $1 + elsif 0 < ARGV.length + option_argument = ARGV.shift + else + set_error(MissingArgument, + "option `#{argument}' requires an argument") + end + elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT + if argument =~ /=(.*)$/ + option_argument = $1 + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + option_argument = ARGV.shift + else + option_argument = '' + end + elsif argument =~ /=(.*)$/ + set_error(NeedlessArgument, + "option `#{option_name}' doesn't allow an argument") + end + + elsif argument =~ /^(-(.))(.*)/ + # + # This is a short style option, which start with `-' (not `--'). + # Short options may be catenated (e.g. `-l -g' is equivalent to + # `-lg'). + # + option_name, ch, @rest_singles = $1, $2, $3 + + if @canonical_names.include?(option_name) + # + # The option `option_name' is found in `@canonical_names'. + # Check its argument. + # + if @argument_flags[option_name] == REQUIRED_ARGUMENT + if 0 < @rest_singles.length + option_argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length + option_argument = ARGV.shift + else + # 1003.2 specifies the format of this message. + set_error(MissingArgument, "option requires an argument -- #{ch}") + end + elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT + if 0 < @rest_singles.length + option_argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + option_argument = ARGV.shift + else + option_argument = '' + end + end + else + # + # This is an invalid option. + # 1003.2 specifies the format of this message. + # + if ENV.include?('POSIXLY_CORRECT') + set_error(InvalidOption, "invalid option -- #{ch}") + else + set_error(InvalidOption, "invalid option -- #{ch}") + end + end + else + # + # This is a non-option argument. + # Only RETURN_IN_ORDER falled into here. + # + return '', argument + end + + return @canonical_names[option_name], option_argument + end + + # + # `get_option' is an alias of `get'. + # + alias get_option get + + # Iterator version of `get'. + # + # The block is called repeatedly with two arguments: + # The first is the option name. + # The second is the argument which followed it (if any). + # Example: ('--opt', 'value') + # + # The option name is always converted to the first (preferred) + # name given in the original options to GetoptLong.new. + # + def each + loop do + option_name, option_argument = get_option + break if option_name == nil + yield option_name, option_argument + end + end + + # + # `each_option' is an alias of `each'. + # + alias each_option each +end |