From 62e648e148b3cb9f96dcce808c55c02b7ccb4486 Mon Sep 17 00:00:00 2001 From: matz Date: Wed, 20 Jan 1999 04:59:39 +0000 Subject: ruby 1.3 cycle git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/RUBY@372 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/getoptlong.rb | 473 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 473 insertions(+) create mode 100644 lib/getoptlong.rb (limited to 'lib/getoptlong.rb') diff --git a/lib/getoptlong.rb b/lib/getoptlong.rb new file mode 100644 index 0000000000..98a73ae3db --- /dev/null +++ b/lib/getoptlong.rb @@ -0,0 +1,473 @@ +# -*- Ruby -*- +# Copyright (C) 1998 Motoyuki Kasahara +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# + +# +# Documents and latest version of `getoptlong.rb' are found at: +# http://www.sra.co.jp/people/m-kasahr/ruby/getoptlong/ +# + +# +# Parse command line options just like GNU getopt_long(). +# +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..2 + + # + # Error types. + # + class AmbigousOption < StandardError; end + class NeedlessArgument < StandardError; end + class MissingArgument < StandardError; end + class InvalidOption < StandardError; end + + # + # Initializer. + # + def initialize(*arguments) + # + # Current ordering. + # + if ENV.include?('POSIXLY_CORRECT') + @ordering = REQUIRE_ORDER + else + @ordering = PERMUTE + end + + # + # Hash table of option names. + # Keyes of the table are option names, and their values are canonical + # names of the options. + # + @canonical_names = Hash.new + + # + # Hash table of argument flags. + # Keyes 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_flag = FALSE + + # + # Status code. + # + @status = STATUS_YET + + # + # Error code. + # + @error = nil + + # + # Error message. + # + @error_message = nil + + # + # Rest of catinated 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 ordering. + # + 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 + # + 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| + # + # Each argument must be an Array. + # + 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 `quit' mode. + # + attr_writer :quiet + + # + # Return the flag of `quiet' mode. + # + attr_reader :quiet + + # + # `quiet?' is an alias of `quiet'. + # + alias quiet? quiet + + # + # Termintate option processing. + # + def terminate + return if @status == STATUS_TERMINATED + raise RuntimeError, "an error has occured" 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 + + # + # Examine whether option processing is termintated or not. + # + def terminated? + return @status == STATUS_TERMINATED + end + + # + # Set an error (protected). + # + def set_error(type, message) + $stderr.print("#{$0}: #{message}\n") if !@quiet_flag + + @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 an error message. + # + def error_message + return @error_message + end + + # + # Get next option name and its argument as an array. + # + def get + name, argument = nil, '' + + # + # Check status. + # + return if @error != nil + case @status + when STATUS_YET + @status = STATUS_STARTED + when STATUS_TERMINATED + return + end + + # + # Get next option argument. + # + if 0 < @rest_singles.length + $_ = '-' + @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 + end + $_ = ARGV.shift + elsif @ordering == REQUIRE_ORDER + if (ARGV[0] !~ /^-./) + terminate + return nil + end + $_ = ARGV.shift + else + $_ = ARGV.shift + end + + # + # Check the special argument `--'. + # `--' indicates the end of the option list. + # + if $_ == '--' && @rest_singles.length == 0 + terminate + return nil + end + + # + # Check for long and short options. + # + if /^(--[^=]+)/ && @rest_singles.length == 0 + # + # This is a long style option, which start with `--'. + # + pattern = $1 + if @canonical_names.include?(pattern) + name = pattern + else + # + # The option `name' is not registered in `@canonical_names'. + # It may be an abbreviated. + # + match_count = 0 + @canonical_names.each_key do |key| + if key.index(pattern) == 0 + name = key + match_count += 1 + end + end + if 2 <= match_count + set_error(AmbigousOption, "option `#{$_}' is ambiguous") + elsif match_count == 0 + set_error(InvalidOption, "unrecognized option `#{$_}'") + end + end + + # + # Check an argument to the option. + # + if @argument_flags[name] == REQUIRED_ARGUMENT + if /=(.*)$/ + argument = $1 + elsif 0 < ARGV.length + argument = ARGV.shift + else + set_error(MissingArgument, "option `#{$_}' requires an argument") + end + elsif @argument_flags[name] == OPTIONAL_ARGUMENT + if /=(.*)$/ + argument = $1 + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + argument = ARGV.shift + else + argument = '' + end + elsif /=(.*)$/ + set_error(NeedlessArgument, + "option `#{name}' doesn't allow an argument") + end + + elsif /^(-(.))(.*)/ + # + # This is a short style option, which start with `-' (not `--'). + # Short options may be catinated (e.g. `-l -g' is equivalent to + # `-lg'). + # + name, ch, @rest_singles = $1, $2, $3 + + if @canonical_names.include?(name) + # + # The option `name' is found in `@canonical_names'. + # Check its argument. + # + if @argument_flags[name] == REQUIRED_ARGUMENT + if 0 < @rest_singles.length + argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length + argument = ARGV.shift + else + # 1003.2 specifies the format of this message. + set_error(MissingArgument, "option requires an argument -- #{ch}") + end + elsif @argument_flags[name] == OPTIONAL_ARGUMENT + if 0 < @rest_singles.length + argument = @rest_singles + @rest_singles = '' + elsif 0 < ARGV.length && ARGV[0] !~ /^-./ + argument = ARGV.shift + else + 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, "illegal 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 '', $_ + end + + return @canonical_names[name], argument + end + + # + # `get_option' is an alias of `get'. + # + alias get_option get + + # + # Iterator version of `get'. + # + def each + loop do + name, argument = get_option + break if name == nil + yield name, argument + end + end + + # + # `each_option' is an alias of `each'. + # + alias each_option each +end -- cgit v1.2.3