mirror of
https://github.com/mistergibson/gxg-web-client.git
synced 2026-08-15 03:26:00 -07:00
254 lines
6.5 KiB
Ruby
254 lines
6.5 KiB
Ruby
module Rubinius
|
|
|
|
# A simple command line option parser.
|
|
class Options
|
|
|
|
# A single command line option.
|
|
class Option
|
|
attr_reader :short
|
|
attr_reader :long
|
|
attr_reader :arg
|
|
attr_reader :description
|
|
attr_reader :block
|
|
|
|
def initialize(short, long, arg, description, block)
|
|
@short = short
|
|
@long = long
|
|
@arg = arg
|
|
@description = description
|
|
@block = block
|
|
end
|
|
|
|
private :initialize
|
|
|
|
def arg?
|
|
@arg != nil
|
|
end
|
|
|
|
def optional?
|
|
@arg[0] == ?[ and @arg[-1] == ?]
|
|
end
|
|
|
|
def match?(opt)
|
|
opt == @short or opt == @long
|
|
end
|
|
end
|
|
|
|
# Raised if incorrect or incomplete formats are passed to #on.
|
|
class OptionError < Exception; end
|
|
|
|
# Raised if an unrecognized option is encountered.
|
|
class ParseError < Exception; end
|
|
|
|
attr_accessor :config
|
|
attr_accessor :banner
|
|
attr_accessor :width
|
|
attr_accessor :options
|
|
|
|
def initialize(banner="", width=30, config=nil)
|
|
@parse = true
|
|
@banner = banner
|
|
@config = config
|
|
@width = width
|
|
@options = []
|
|
@doc = []
|
|
@extra = []
|
|
@align = true
|
|
@on_extra = lambda { |x|
|
|
raise ParseError, "Unrecognized option: #{x}" if x[0] == ?-
|
|
@extra << x
|
|
}
|
|
|
|
yield self if block_given?
|
|
end
|
|
|
|
private :initialize
|
|
|
|
# Documentation for options is left aligned. For example,
|
|
#
|
|
# -a ARG Some description
|
|
# --big Another bit of info
|
|
# -c, --class Yet more info
|
|
#
|
|
# See +option_align+.
|
|
def left_align
|
|
@align = nil
|
|
end
|
|
|
|
# Documentation for options is aligned by option type. For example,
|
|
#
|
|
# -a ARG Some description
|
|
# --big Another bit of info
|
|
# -c, --class Yet more info
|
|
#
|
|
# See +left_align+
|
|
def option_align
|
|
@align = true
|
|
end
|
|
|
|
# Registers an option. Acceptable formats for arguments are:
|
|
#
|
|
# on "-a", "description"
|
|
# on "-a", "--abdc", "description"
|
|
# on "-a", "ARG", "description"
|
|
# on "--abdc", "ARG", "description"
|
|
# on "-a", "--abdc", "ARG", "description"
|
|
# on "-a", "[ARG]", "description"
|
|
#
|
|
# The [ARG] form specifies an optional argument. The argument
|
|
# must be attached to the option. In the case of a short option,
|
|
# the form "-aARG" must be used. For a long option, the form
|
|
# '--opt=ARG' must be used.
|
|
#
|
|
# If an block is passed, it will be invoked when the option is
|
|
# matched. Not passing a block is permitted, but nonsensical.
|
|
def on(*args, &block)
|
|
raise OptionError, "option and description are required" if args.size < 2
|
|
|
|
description = args.pop
|
|
short, long, argument = nil
|
|
args.each do |arg|
|
|
if arg[0] == ?-
|
|
if arg[1] == ?-
|
|
long = arg
|
|
else
|
|
short = arg
|
|
end
|
|
else
|
|
argument = arg
|
|
end
|
|
end
|
|
|
|
add short, long, argument, description, block
|
|
end
|
|
|
|
# Adds documentation text for an option and adds an +Option+
|
|
# instance to the list of registered options.
|
|
def add(short, long, arg, description, block)
|
|
pad = @align ? " " : ""
|
|
s = short ? short.dup : pad
|
|
s << (short ? ", " : pad) if long
|
|
doc " #{s}#{long} #{arg}".ljust(@width-1) + " #{description}"
|
|
@options << Option.new(short, long, arg, description, block)
|
|
end
|
|
|
|
# Searches all registered options to find a match for +opt+. Returns
|
|
# +nil+ if no registered options match.
|
|
def match?(opt)
|
|
@options.find { |o| o.match? opt }
|
|
end
|
|
|
|
# Processes an option. Calles the #on_extra block (or default) for
|
|
# unrecognized options. For registered options, possibly fetches an
|
|
# argument and invokes the option's block if it is not nil.
|
|
def process(argv, entry, opt, arg)
|
|
unless option = match?(opt)
|
|
@on_extra[entry]
|
|
else
|
|
if option.arg?
|
|
if arg.nil?
|
|
unless option.optional?
|
|
arg = argv.shift
|
|
end
|
|
end
|
|
|
|
unless arg or option.optional?
|
|
raise ParseError, "No argument provided for #{opt}"
|
|
end
|
|
|
|
option.block[arg] if option.block
|
|
else
|
|
option.block[] if option.block
|
|
end
|
|
end
|
|
option
|
|
end
|
|
|
|
# Splits a string at +n+ characters into the +opt+ and the +rest+.
|
|
# The +arg+ is set to +nil+ if +rest+ is an empty string.
|
|
def split(str, n)
|
|
opt = str[0, n]
|
|
rest = str[n, str.size] || ""
|
|
arg = rest == "" ? nil : rest
|
|
return opt, arg, rest
|
|
end
|
|
|
|
# Parses an array of command line entries, calling blocks for
|
|
# registered options.
|
|
def parse(argv=ARGV)
|
|
argv = Array(argv)
|
|
|
|
while @parse and entry = argv.shift
|
|
# collect everything that is not an option
|
|
if entry[0] != ?-
|
|
@on_extra[entry]
|
|
next
|
|
end
|
|
|
|
# this is a long option
|
|
if entry[1] == ?-
|
|
opt, arg = entry.split "="
|
|
process argv, entry, opt, arg
|
|
next
|
|
end
|
|
|
|
# disambiguate short option group from short option with argument
|
|
opt, arg, rest = split entry, 2
|
|
|
|
# process first option
|
|
option = process argv, entry, opt, arg
|
|
next unless option and not option.arg?
|
|
|
|
# process the rest of the options
|
|
while rest.size > 0
|
|
opt, arg, rest = split rest, 1
|
|
opt = "-" + opt
|
|
option = process argv, opt, opt, arg
|
|
break if option.arg?
|
|
end
|
|
end
|
|
|
|
@extra
|
|
rescue ParseError => e
|
|
puts self
|
|
puts e
|
|
exit 1
|
|
end
|
|
|
|
def start_parsing
|
|
@parse = true
|
|
end
|
|
|
|
def stop_parsing
|
|
@parse = false
|
|
end
|
|
|
|
# Adds a string of documentation text inline in the text generated
|
|
# from the options. See #on and #add.
|
|
def doc(str)
|
|
@doc << str
|
|
end
|
|
|
|
# Convenience method for providing -v, --version options.
|
|
def version(version, &block)
|
|
show = block || lambda { puts "#{File.basename $0} #{version}"; exit }
|
|
on "-v", "--version", "Show version", &show
|
|
end
|
|
|
|
# Convenience method for providing -h, --help options.
|
|
def help(&block)
|
|
help = block || lambda { puts self; exit 1 }
|
|
on "-h", "--help", "Show this message", &help
|
|
end
|
|
|
|
# Stores a block that will be called with unrecognized options
|
|
def on_extra(&block)
|
|
@on_extra = block
|
|
end
|
|
|
|
# Returns a string representation of the options and doc strings.
|
|
def to_s
|
|
@banner + "\n\n" + @doc.join("\n") + "\n"
|
|
end
|
|
end
|
|
end
|