optix 1.0.1

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