bump to 1.15
[trollop:mainline.git] / lib / trollop.rb
1 ## lib/trollop.rb -- trollop command-line processing library
2 ## Author::    William Morgan (mailto: wmorgan-trollop@masanjin.net)
3 ## Copyright:: Copyright 2007 William Morgan
4 ## License::   GNU GPL version 2
5
6 require 'date'
7
8 module Trollop
9
10 VERSION = "1.15"
11
12 ## Thrown by Parser in the event of a commandline error. Not needed if
13 ## you're using the Trollop::options entry.
14 class CommandlineError < StandardError; end
15   
16 ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
17 ## automatically by Trollop#options.
18 class HelpNeeded < StandardError; end
19
20 ## Thrown by Parser if the user passes in '-h' or '--version'. Handled
21 ## automatically by Trollop#options.
22 class VersionNeeded < StandardError; end
23
24 ## Regex for floating point numbers
25 FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))$/
26
27 ## Regex for parameters
28 PARAM_RE = /^-(-|\.$|[^\d\.])/
29
30 ## The commandline parser. In typical usage, the methods in this class
31 ## will be handled internally by Trollop::options. In this case, only the
32 ## #opt, #banner and #version, #depends, and #conflicts methods will
33 ## typically be called.
34 ##
35 ## If it's necessary to instantiate this class (for more complicated
36 ## argument-parsing situations), be sure to call #parse to actually
37 ## produce the output hash.
38 class Parser
39
40   ## The set of values that indicate a flag option when passed as the
41   ## +:type+ parameter of #opt.
42   FLAG_TYPES = [:flag, :bool, :boolean]
43
44   ## The set of values that indicate a single-parameter (normal) option when
45   ## passed as the +:type+ parameter of #opt.
46   ##
47   ## A value of +io+ corresponds to a readable IO resource, including
48   ## a filename, URI, or the strings 'stdin' or '-'.
49   SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float, :io, :date]
50
51   ## The set of values that indicate a multiple-parameter option (i.e., that
52   ## takes multiple space-separated values on the commandline) when passed as
53   ## the +:type+ parameter of #opt.
54   MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats, :ios, :dates]
55
56   ## The complete set of legal values for the +:type+ parameter of #opt.
57   TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES
58
59   INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
60
61   ## The values from the commandline that were not interpreted by #parse.
62   attr_reader :leftovers
63
64   ## The complete configuration hashes for each option. (Mainly useful
65   ## for testing.)
66   attr_reader :specs
67
68   ## Initializes the parser, and instance-evaluates any block given.
69   def initialize *a, &b
70     @version = nil
71     @leftovers = []
72     @specs = {}
73     @long = {}
74     @short = {}
75     @order = []
76     @constraints = []
77     @stop_words = []
78     @stop_on_unknown = false
79
80     #instance_eval(&b) if b # can't take arguments
81     cloaker(&b).bind(self).call(*a) if b
82   end
83
84   ## Define an option. +name+ is the option name, a unique identifier
85   ## for the option that you will use internally, which should be a
86   ## symbol or a string. +desc+ is a string description which will be
87   ## displayed in help messages.
88   ##
89   ## Takes the following optional arguments:
90   ##
91   ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
92   ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+.
93   ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
94   ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Trollop::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
95   ## [+:required+] If set to +true+, the argument must be provided on the commandline.
96   ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)
97   ##
98   ## Note that there are two types of argument multiplicity: an argument
99   ## can take multiple values, e.g. "--arg 1 2 3". An argument can also
100   ## be allowed to occur multiple times, e.g. "--arg 1 --arg 2".
101   ##
102   ## Arguments that take multiple values should have a +:type+ parameter
103   ## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+
104   ## value of an array of the correct type (e.g. [String]). The
105   ## value of this argument will be an array of the parameters on the
106   ## commandline.
107   ##
108   ## Arguments that can occur multiple times should be marked with
109   ## +:multi+ => +true+. The value of this argument will also be an array.
110   ## In contrast with regular non-multi options, if not specified on
111   ## the commandline, the default value will be [], not nil.
112   ##
113   ## These two attributes can be combined (e.g. +:type+ => +:strings+,
114   ## +:multi+ => +true+), in which case the value of the argument will be
115   ## an array of arrays.
116   ##
117   ## There's one ambiguous case to be aware of: when +:multi+: is true and a
118   ## +:default+ is set to an array (of something), it's ambiguous whether this
119   ## is a multi-value argument as well as a multi-occurrence argument.
120   ## In thise case, Trollop assumes that it's not a multi-value argument.
121   ## If you want a multi-value, multi-occurrence argument with a default
122   ## value, you must specify +:type+ as well.
123
124   def opt name, desc="", opts={}
125     raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name
126
127     ## fill in :type
128     opts[:type] = # normalize
129       case opts[:type]
130       when :boolean, :bool; :flag
131       when :integer; :int
132       when :integers; :ints
133       when :double; :float
134       when :doubles; :floats
135       when Class
136         case opts[:type].name
137         when 'TrueClass', 'FalseClass'; :flag
138         when 'String'; :string
139         when 'Integer'; :int
140         when 'Float'; :float
141         when 'IO'; :io
142         when 'Date'; :date
143         else
144           raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
145         end
146       when nil; nil
147       else
148         raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
149         opts[:type]
150       end
151
152     ## for options with :multi => true, an array default doesn't imply
153     ## a multi-valued argument. for that you have to specify a :type
154     ## as well. (this is how we disambiguate an ambiguous situation;
155     ## see the docs for Parser#opt for details.)
156     disambiguated_default =
157       if opts[:multi] && opts[:default].is_a?(Array) && !opts[:type]
158         opts[:default].first
159       else
160         opts[:default]
161       end
162
163     type_from_default =
164       case disambiguated_default
165       when Integer; :int
166       when Numeric; :float
167       when TrueClass, FalseClass; :flag
168       when String; :string
169       when IO; :io
170       when Date; :date
171       when Array
172         if opts[:default].empty?
173           raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
174         end
175         case opts[:default][0]    # the first element determines the types
176         when Integer; :ints
177         when Numeric; :floats
178         when String; :strings
179         when IO; :ios
180         when Date; :dates
181         else
182           raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
183         end
184       when nil; nil
185       else
186         raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
187       end
188
189     raise ArgumentError, ":type specification and default type don't match (default type is #{type_from_default})" if opts[:type] && type_from_default && opts[:type] != type_from_default
190
191     opts[:type] = opts[:type] || type_from_default || :flag
192
193     ## fill in :long
194     opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
195     opts[:long] =
196       case opts[:long]
197       when /^--([^-].*)$/
198         $1
199       when /^[^-]/
200         opts[:long]
201       else
202         raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
203       end
204     raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]
205
206     ## fill in :short
207     opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
208     opts[:short] = case opts[:short]
209       when /^-(.)$/; $1
210       when nil, :none, /^.$/; opts[:short]
211       else raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
212     end
213
214     if opts[:short]
215       raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
216       raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ INVALID_SHORT_ARG_REGEX
217     end
218
219     ## fill in :default for flags
220     opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
221
222     ## autobox :default for :multi (multi-occurrence) arguments
223     opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].is_a?(Array)
224
225     ## fill in :multi
226     opts[:multi] ||= false
227
228     opts[:desc] ||= desc
229     @long[opts[:long]] = name
230     @short[opts[:short]] = name if opts[:short] && opts[:short] != :none
231     @specs[name] = opts
232     @order << [:opt, name]
233   end
234
235   ## Sets the version string. If set, the user can request the version
236   ## on the commandline. Should probably be of the form "<program name>
237   ## <version number>".
238   def version s=nil; @version = s if s; @version end
239
240   ## Adds text to the help display. Can be interspersed with calls to
241   ## #opt to build a multi-section help page.
242   def banner s; @order << [:text, s] end
243   alias :text :banner
244
245   ## Marks two (or more!) options as requiring each other. Only handles
246   ## undirected (i.e., mutual) dependencies. Directed dependencies are
247   ## better modeled with Trollop::die.
248   def depends *syms
249     syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
250     @constraints << [:depends, syms]
251   end
252   
253   ## Marks two (or more!) options as conflicting.
254   def conflicts *syms
255     syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
256     @constraints << [:conflicts, syms]
257   end
258
259   ## Defines a set of words which cause parsing to terminate when
260   ## encountered, such that any options to the left of the word are
261   ## parsed as usual, and options to the right of the word are left
262   ## intact.
263   ##
264   ## A typical use case would be for subcommand support, where these
265   ## would be set to the list of subcommands. A subsequent Trollop
266   ## invocation would then be used to parse subcommand options, after
267   ## shifting the subcommand off of ARGV.
268   def stop_on *words
269     @stop_words = [*words].flatten
270   end
271
272   ## Similar to #stop_on, but stops on any unknown word when encountered
273   ## (unless it is a parameter for an argument). This is useful for
274   ## cases where you don't know the set of subcommands ahead of time,
275   ## i.e., without first parsing the global options.
276   def stop_on_unknown
277     @stop_on_unknown = true
278   end
279
280   ## Parses the commandline. Typically called by Trollop::options.
281   def parse cmdline=ARGV
282     vals = {}
283     required = {}
284
285     opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
286     opt :help, "Show this message" unless @specs[:help] || @long["help"]
287
288     @specs.each do |sym, opts|
289       required[sym] = true if opts[:required]
290       vals[sym] = opts[:default]
291       vals[sym] = [] if opts[:multi] && !opts[:default] # multi arguments default to [], not nil
292     end
293
294     resolve_default_short_options
295
296     ## resolve symbols
297     given_args = {}
298     @leftovers = each_arg cmdline do |arg, params|
299       sym = case arg
300       when /^-([^-])$/
301         @short[$1]
302       when /^--([^-]\S*)$/
303         @long[$1]
304       else
305         raise CommandlineError, "invalid argument syntax: '#{arg}'"
306       end
307       raise CommandlineError, "unknown argument '#{arg}'" unless sym
308
309       if given_args.include?(sym) && !@specs[sym][:multi]
310         raise CommandlineError, "option '#{arg}' specified multiple times"
311       end
312
313       given_args[sym] ||= {}
314
315       given_args[sym][:arg] = arg
316       given_args[sym][:params] ||= []
317
318       # The block returns the number of parameters taken.
319       num_params_taken = 0
320
321       unless params.nil?
322         if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
323           given_args[sym][:params] << params[0, 1]  # take the first parameter
324           num_params_taken = 1
325         elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
326           given_args[sym][:params] << params        # take all the parameters
327           num_params_taken = params.size
328         end
329       end
330
331       num_params_taken
332     end
333
334     ## check for version and help args
335     raise VersionNeeded if given_args.include? :version
336     raise HelpNeeded if given_args.include? :help
337
338     ## check constraint satisfaction
339     @constraints.each do |type, syms|
340       constraint_sym = syms.find { |sym| given_args[sym] }
341       next unless constraint_sym
342
343       case type
344       when :depends
345         syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} requires --#{@specs[sym][:long]}" unless given_args.include? sym }
346       when :conflicts
347         syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} conflicts with --#{@specs[sym][:long]}" if given_args.include?(sym) && (sym != constraint_sym) }
348       end
349     end
350
351     required.each do |sym, val|
352       raise CommandlineError, "option '#{sym}' must be specified" unless given_args.include? sym
353     end
354
355     ## parse parameters
356     given_args.each do |sym, given_data|
357       arg = given_data[:arg]
358       params = given_data[:params]
359
360       opts = @specs[sym]
361       raise CommandlineError, "option '#{arg}' needs a parameter" if params.empty? && opts[:type] != :flag
362
363       vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
364
365       case opts[:type]
366       when :flag
367         vals[sym] = !opts[:default]
368       when :int, :ints
369         vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
370       when :float, :floats
371         vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
372       when :string, :strings
373         vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
374       when :io, :ios
375         vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }
376       when :date, :dates
377         vals[sym] = params.map { |pg| pg.map { |p| parse_date_parameter p, arg } }
378       end
379
380       if SINGLE_ARG_TYPES.include?(opts[:type])
381         unless opts[:multi]       # single parameter
382           vals[sym] = vals[sym][0][0]
383         else                      # multiple options, each with a single parameter
384           vals[sym] = vals[sym].map { |p| p[0] }
385         end
386       elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]
387         vals[sym] = vals[sym][0]  # single option, with multiple parameters
388       end
389       # else: multiple options, with multiple parameters
390     end
391
392     ## allow openstruct-style accessors
393     class << vals
394       def method_missing(m, *args)
395         self[m] || self[m.to_s]
396       end
397     end
398     vals
399   end
400
401   def parse_date_parameter param, arg #:nodoc:
402     begin
403       begin
404         time = Chronic.parse(param)
405       rescue NameError
406         # chronic is not available
407       end
408       time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
409     rescue ArgumentError => e
410       raise CommandlineError, "option '#{arg}' needs a date"
411     end
412   end
413
414   ## Print the help message to +stream+.
415   def educate stream=$stdout
416     width # just calculate it now; otherwise we have to be careful not to
417           # call this unless the cursor's at the beginning of a line.
418
419     left = {}
420     @specs.each do |name, spec| 
421       left[name] = "--#{spec[:long]}" +
422         (spec[:short] && spec[:short] != :none ? ", -#{spec[:short]}" : "") +
423         case spec[:type]
424         when :flag; ""
425         when :int; " <i>"
426         when :ints; " <i+>"
427         when :string; " <s>"
428         when :strings; " <s+>"
429         when :float; " <f>"
430         when :floats; " <f+>"
431         when :io; " <filename/uri>"
432         when :ios; " <filename/uri+>"
433         when :date; " <date>"
434         when :dates; " <date+>"
435         end
436     end
437
438     leftcol_width = left.values.map { |s| s.length }.max || 0
439     rightcol_start = leftcol_width + 6 # spaces
440
441     unless @order.size > 0 && @order.first.first == :text
442       stream.puts "#@version\n" if @version
443       stream.puts "Options:"
444     end
445
446     @order.each do |what, opt|
447       if what == :text
448         stream.puts wrap(opt)
449         next
450       end
451
452       spec = @specs[opt]
453       stream.printf "  %#{leftcol_width}s:   ", left[opt]
454       desc = spec[:desc] + begin
455         default_s = case spec[:default]
456         when $stdout; "<stdout>"
457         when $stdin; "<stdin>"
458         when $stderr; "<stderr>"
459         when Array
460           spec[:default].join(", ")
461         else
462           spec[:default].to_s
463         end
464
465         if spec[:default]
466           if spec[:desc] =~ /\.$/
467             " (Default: #{default_s})"
468           else
469             " (default: #{default_s})"
470           end
471         else
472           ""
473         end
474       end
475       stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
476     end
477   end
478
479   def width #:nodoc:
480     @width ||= if $stdout.tty?
481       begin
482         require 'curses'
483         Curses::init_screen
484         x = Curses::cols
485         Curses::close_screen
486         x
487       rescue Exception
488         80
489       end
490     else
491       80
492     end
493   end
494
495   def wrap str, opts={} # :nodoc:
496     if str == ""
497       [""]
498     else
499       str.split("\n").map { |s| wrap_line s, opts }.flatten
500     end
501   end
502
503 private
504
505   ## yield successive arg, parameter pairs
506   def each_arg args
507     remains = []
508     i = 0
509
510     until i >= args.length
511       if @stop_words.member? args[i]
512         remains += args[i .. -1]
513         return remains
514       end
515       case args[i]
516       when /^--$/ # arg terminator
517         remains += args[(i + 1) .. -1]
518         return remains
519       when /^--(\S+?)=(.*)$/ # long argument with equals
520         yield "--#{$1}", [$2]
521         i += 1
522       when /^--(\S+)$/ # long argument
523         params = collect_argument_parameters(args, i + 1)
524         unless params.empty?
525           num_params_taken = yield args[i], params
526           unless num_params_taken
527             if @stop_on_unknown
528               remains += args[i + 1 .. -1]
529               return remains
530             else
531               remains += params
532             end
533           end
534           i += 1 + num_params_taken
535         else # long argument no parameter
536           yield args[i], nil
537           i += 1
538         end
539       when /^-(\S+)$/ # one or more short arguments
540         shortargs = $1.split(//)
541         shortargs.each_with_index do |a, j|
542           if j == (shortargs.length - 1)
543             params = collect_argument_parameters(args, i + 1)
544             unless params.empty?
545               num_params_taken = yield "-#{a}", params
546               unless num_params_taken
547                 if @stop_on_unknown
548                   remains += args[i + 1 .. -1]
549                   return remains
550                 else
551                   remains += params
552                 end
553               end
554               i += 1 + num_params_taken
555             else # argument no parameter
556               yield "-#{a}", nil
557               i += 1
558             end
559           else
560             yield "-#{a}", nil
561           end
562         end
563       else
564         if @stop_on_unknown
565           remains += args[i .. -1]
566           return remains
567         else
568           remains << args[i]
569           i += 1
570         end
571       end
572     end
573
574     remains
575   end
576
577   def parse_integer_parameter param, arg
578     raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
579     param.to_i
580   end
581
582   def parse_float_parameter param, arg
583     raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
584     param.to_f
585   end
586
587   def parse_io_parameter param, arg
588     case param
589     when /^(stdin|-)$/i; $stdin
590     else
591       require 'open-uri'
592       begin
593         open param
594       rescue SystemCallError => e
595         raise CommandlineError, "file or url for option '#{arg}' cannot be opened: #{e.message}"
596       end
597     end
598   end
599
600   def collect_argument_parameters args, start_at
601     params = []
602     pos = start_at
603     while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
604       params << args[pos]
605       pos += 1
606     end
607     params
608   end
609
610   def resolve_default_short_options
611     @order.each do |type, name|
612       next unless type == :opt
613       opts = @specs[name]
614       next if opts[:short]
615
616       c = opts[:long].split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
617       if c # found a character to use
618         opts[:short] = c
619         @short[c] = name
620       end
621     end
622   end
623
624   def wrap_line str, opts={}
625     prefix = opts[:prefix] || 0
626     width = opts[:width] || (self.width - 1)
627     start = 0
628     ret = []
629     until start > str.length
630       nextt = 
631         if start + width >= str.length
632           str.length
633         else
634           x = str.rindex(/\s/, start + width)
635           x = str.index(/\s/, start) if x && x < start
636           x || str.length
637         end
638       ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
639       start = nextt + 1
640     end
641     ret
642   end
643
644   ## instance_eval but with ability to handle block arguments
645   ## thanks to why: http://redhanded.hobix.com/inspect/aBlockCostume.html
646   def cloaker &b
647     (class << self; self; end).class_eval do
648       define_method :cloaker_, &b
649       meth = instance_method :cloaker_
650       remove_method :cloaker_
651       meth
652     end
653   end
654 end
655
656 ## The top-level entry method into Trollop. Creates a Parser object,
657 ## passes the block to it, then parses +args+ with it, handling any
658 ## errors or requests for help or version information appropriately (and
659 ## then exiting). Modifies +args+ in place. Returns a hash of option
660 ## values.
661 ##
662 ## The block passed in should contain zero or more calls to +opt+
663 ## (Parser#opt), zero or more calls to +text+ (Parser#text), and
664 ## probably a call to +version+ (Parser#version).
665 ##
666 ## The returned block contains a value for every option specified with
667 ## +opt+.  The value will be the value given on the commandline, or the
668 ## default value if the option was not specified on the commandline. For
669 ## every option specified on the commandline, a key "<option
670 ## name>_given" will also be set in the hash.
671 ##
672 ## Example:
673 ##
674 ##   require 'trollop'
675 ##   opts = Trollop::options do
676 ##     opt :monkey, "Use monkey mode"                     # a flag --monkey, defaulting to false
677 ##     opt :goat, "Use goat mode", :default => true       # a flag --goat, defaulting to true
678 ##     opt :num_limbs, "Number of limbs", :default => 4   # an integer --num-limbs <i>, defaulting to 4
679 ##     opt :num_thumbs, "Number of thumbs", :type => :int # an integer --num-thumbs <i>, defaulting to nil
680 ##   end
681 ##
682 ##   ## if called with no arguments
683 ##   p opts # => { :monkey => false, :goat => true, :num_limbs => 4, :num_thumbs => nil }
684 ##
685 ##   ## if called with --monkey
686 ##   p opts # => {:monkey_given=>true, :monkey=>true, :goat=>true, :num_limbs=>4, :help=>false, :num_thumbs=>nil}
687 ##
688 ## See more examples at http://trollop.rubyforge.org.
689 def options args = ARGV, *a, &b
690   @p = Parser.new(*a, &b)
691   begin
692     vals = @p.parse args
693     args.clear
694     @p.leftovers.each { |l| args << l }
695     vals
696   rescue CommandlineError => e
697     $stderr.puts "Error: #{e.message}."
698     $stderr.puts "Try --help for help."
699     exit(-1)
700   rescue HelpNeeded
701     @p.educate
702     exit
703   rescue VersionNeeded
704     puts @p.version
705     exit
706   end
707 end
708
709 ## Informs the user that their usage of 'arg' was wrong, as detailed by
710 ## 'msg', and dies. Example:
711 ##
712 ##   options do
713 ##     opt :volume, :default => 0.0
714 ##   end
715 ##
716 ##   die :volume, "too loud" if opts[:volume] > 10.0
717 ##   die :volume, "too soft" if opts[:volume] < 0.1
718 ##
719 ## In the one-argument case, simply print that message, a notice
720 ## about -h, and die. Example:
721 ##
722 ##   options do
723 ##     opt :whatever # ...
724 ##   end
725 ##
726 ##   Trollop::die "need at least one filename" if ARGV.empty?
727 def die arg, msg=nil
728   if msg
729     $stderr.puts "Error: argument --#{@p.specs[arg][:long]} #{msg}."
730   else
731     $stderr.puts "Error: #{arg}."
732   end
733   $stderr.puts "Try --help for help."
734   exit(-1)
735 end
736
737 module_function :options, :die
738
739 end # module