como 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +15 -0
  3. data/Rakefile +24 -0
  4. data/lib/como.rb +1041 -0
  5. data/test/test_como.rb +344 -0
  6. metadata +56 -0
data/lib/como.rb ADDED
@@ -0,0 +1,1041 @@
1
+ # = Como
2
+ #
3
+ # == Introduction
4
+ # Como provides low manifest command line option parsing and
5
+ # handling. Command line options are described in a compact table
6
+ # format and option values are stored to conveniently named
7
+ # properties. Como displays the command usage information based on
8
+ # the option table (+ generic program info).
9
+ #
10
+ # == Simple example
11
+ # Below is a small example program ("como_test") that demonstrates
12
+ # typical usage.
13
+ #
14
+ # === Program listing
15
+ # require "como"
16
+ # include Como
17
+ #
18
+ # # Define command line arguments:
19
+ # Spec.defineCheckHelp( "como_test", "Programmer", "2013",
20
+ # [
21
+ # [ :silent, "help", "-h", "Display usage info." ],
22
+ # [ :single, "file", "-f", "File argument." ],
23
+ # [ :switch, "debug", "-d", "Enable debugging." ],
24
+ # ] )
25
+ #
26
+ # puts "File option: #{Opt['file'].value}"
27
+ # puts "Debugging selected!" if Opt['debug'].given?
28
+ #
29
+ # "Spec.defineCheckHelp" method takes 4 arguments:
30
+ # [progname] Name of the program (or command).
31
+ # [author] Author of the program.
32
+ # [year] Year (or any date) for the program.
33
+ # [option table] Description of the command options in format with 4
34
+ # entries in each sub-array.
35
+ #
36
+ # Each option table entry is an Array of 4 values: type, name,
37
+ # mnemonic, doc. Three different types are present in the example:
38
+ # [:silent] Silent is left out from the "usage" printout (see
39
+ # below). Also "help" is reserved as special option name to
40
+ # designate command line usage help.
41
+ # [:single] Single means that the option requires one argument (and only one).
42
+ # [:switch] Switch is an optional flag (default value is false).
43
+ #
44
+ # Option name is used to reference the option value from Opt class.
45
+ # The command line option values are stored to Opt class
46
+ # automatically. For example the file option value is returned by
47
+ # executing "Opt['file'].value". The option name also doubles as
48
+ # long option, i.e. one could use "--file <filename>" on the command
49
+ # line.
50
+ #
51
+ # Existence of optional options can be tested using the "given"
52
+ # method. For example "Opt['debug'].given" would return "true" if
53
+ # "-d" was given on the command line.
54
+ #
55
+ # === Example executions
56
+ # Normal behavior would be achieved by executing:
57
+ # shell> como_test -f example -d
58
+ #
59
+ # The program would execute with the following output:
60
+ # File option: example
61
+ # Debugging selected!
62
+ #
63
+ # Como includes certain "extra" behavior out-of-box. For example
64
+ # given the command:
65
+ # shell> como_test
66
+ #
67
+ # The following is displayed on the screen:
68
+ #
69
+ # como_test error: Option "-f" missing...
70
+ #
71
+ #
72
+ # Usage:
73
+ # como_test -f <file> [-d]
74
+ #
75
+ # -f File argument.
76
+ # -d Enable debugging.
77
+ #
78
+ #
79
+ # Copyright (c) 2013 by Programmer
80
+ #
81
+ # Missing option error is displayed since "file" is a mandatory
82
+ # option. The error display is followed by "usage" display.
83
+ #
84
+ # shell> como_test -h
85
+ #
86
+ # Would display the same "usage" screen except without the error
87
+ # line. Documentation string is taken from the specification to
88
+ # "usage" display.
89
+ #
90
+ #
91
+ # == Option types
92
+ #
93
+ # The following types can be defined for the command line options:
94
+ # [:switch] Single switch option (no arguments).
95
+ # [:single] Mandatory single argument option.
96
+ # [:multi] Mandatory multiple argument option. Option values in array.
97
+ # [:opt_single] Optional single argument option.
98
+ # [:opt_multi] Optional multiple argument option. Option values in array.
99
+ # [:opt_any] Optional multiple argument option (also none accepted).
100
+ # Option values in array.
101
+ # [:default] Default option (no switch associated). Any name and
102
+ # option String value can be supplied to the spec, since
103
+ # only the document string is used. Default option is
104
+ # referred with "nil".
105
+ # [:exclusive] Option that does not coexist with other options.
106
+ # [:silent] Option that does not coexist with other options and is not
107
+ # displayed as an option in "usage" display. In effect a
108
+ # sub-option of :exclusive.
109
+ #
110
+ #
111
+ # == Specification method options
112
+ #
113
+ # The common method for specifying the options is to use
114
+ # "Spec.defineCheckHelp". Method invocation includes definition
115
+ # of the options, parsing the command line, checking for missing
116
+ # mandatory options, and it will automatically display "usage" if
117
+ # "help" option is given.
118
+ #
119
+ # Automatic "help" option processing can be avoided using
120
+ # "Spec.defineCheck" instead.
121
+ #
122
+ # Both methods above accept additional parameters passed in a
123
+ # Hash. The usable hash keys:
124
+ # [:header] Header lines before standard usage printout.
125
+ # [:footer] Footer lines after standard usage printout.
126
+ # [:check] Check for missing arguments (default: true).
127
+ # [:help_exit] Exit program if help displayed (default: true).
128
+ # [:error_exit] Exit program if error in options (default: true).
129
+ #
130
+ #
131
+ # == Using Opt class
132
+ #
133
+ # Opt class includes the parsed option values. All options can be
134
+ # tested whether they are specified on the command line using
135
+ # "Opt['name'].given"
136
+ #
137
+ # "Opt['name'].value" returns the provided option value. For
138
+ # ":switch" type it is true/false value and for the other types a
139
+ # String or an Array of Strings.
140
+ #
141
+ # If an option takes multiple arguments, the value for the option is
142
+ # an Array. The values can be iterated simply by:
143
+ # Opt['files'].value.each do |val|
144
+ # puts val
145
+ # end
146
+ #
147
+ # With ":opt_any" type, the user should first check if the option was given:
148
+ # Opt['many_files_or_none'].given
149
+ # Then check how many arguments where given:
150
+ # Opt['many_files_or_none'].value.length
151
+ # And finally decide what to do.
152
+ #
153
+ # If the user gives the "--" option, the arguments after that option
154
+ # is returned as an Array with "Opt.external"
155
+ #
156
+ #
157
+ # == Customization
158
+ #
159
+ # If the default behavior is not satisfactory, changes can be
160
+ # implemented simply by overloading the existing functions. Some
161
+ # knowledge of the internal workings of Como is required though.
162
+ #
163
+ #
164
+ # == Additional checks
165
+ #
166
+ # Sometimes the options have to be used in combination to make sense
167
+ # for the program. Como provides a facility to create relations
168
+ # between options. Consider the following options spec:
169
+ # Spec.defineCheckHelp( "como_fulltest", "Programmer", "2013",
170
+ # [
171
+ # [ :silent, "help", "-h", "Display usage info." ],
172
+ # [ :single, "file", "-f", "File argument." ],
173
+ # [ :switch, "o1", "-o1", "o1" ],
174
+ # [ :opt_single, "o2", "-o2", "o2" ],
175
+ # [ :opt_single, "o3", "-o3", "o3" ],
176
+ # [ :opt_multi, "o4", "-o4", "o4" ],
177
+ # [ :opt_any, "o5", "-o5", "o5" ],
178
+ # [ :switch, "debug", "-d", "Enable debugging." ],
179
+ # ] )
180
+ #
181
+ # Spec.checkRule do
182
+ # all( 'file',
183
+ # one(
184
+ # all( 'o1', 'o2' ),
185
+ # one( 'o3', 'o4', 'o5' )
186
+ # )
187
+ # )
188
+ # end
189
+ #
190
+ # This spec includes multiple optional options ("o?"). The
191
+ # "Spec.checkRule" method accepts a block where option rule
192
+ # check DSL (Domain Specific Language) is used. The rule specifies
193
+ # that the "file" option has to be used in combination with some other
194
+ # options. These are "all( 'o1', 'o2' )" or "one( 'o3', 'o4', 'o5' )",
195
+ # i.e. either both "o1" and "o2", or one of ["o3","o4","o5]. The
196
+ # checker will validate this rule and error if for example the command
197
+ # line reads:
198
+ # shell> como_fulltest --file myfile -o3 black -o5
199
+ #
200
+ # The following rules can be used (in combination):
201
+ # [all] All options in the list.
202
+ # [one] One and only one from the list.
203
+ # [any] At least one of the list is given.
204
+ # [none] No options are required.
205
+ # [incr] Incremental options in order i.e. have to have previous to
206
+ # have later.
207
+ # [follow] Incremental options in order i.e. have to have later if has
208
+ # previous (combined options).
209
+
210
+ module Como
211
+
212
+ # IO stream options for Como classes.
213
+ class ComoCommon
214
+
215
+ # Default value for display output.
216
+ @@io = STDOUT
217
+
218
+ # Set @@io.
219
+ def ComoCommon.setIo( io )
220
+ @@io = io
221
+ end
222
+
223
+ # Get @@io.
224
+ def ComoCommon.getIo
225
+ @@io
226
+ end
227
+ end
228
+
229
+
230
+ # User interface for Como.
231
+ class Spec < ComoCommon
232
+
233
+ # Command line options source.
234
+ @@argv = ARGV
235
+
236
+ # Set of default options for prinout.
237
+ @@options = {
238
+ :header => nil,
239
+ :footer => nil,
240
+ :check => true,
241
+ :help_exit => true,
242
+ :error_exit => true,
243
+ }
244
+
245
+ # Set command line options source, i.e. @@argv (default: ARGV).
246
+ def Spec.setArgv( newArgv )
247
+ @@argv = newArgv
248
+ end
249
+
250
+ # Display program usage (and optionally exit).
251
+ def Spec.usage
252
+ @@io.puts Spec.usageNormal
253
+ exit( 1 ) if @@options[ :help_exit ]
254
+ end
255
+
256
+
257
+ # Usage info for Opt:s.
258
+ def Spec.usageNormal
259
+ str = ""
260
+
261
+ if @@options[ :header ]
262
+ str += @@options[ :header ]
263
+ str += "\n"
264
+ end
265
+
266
+ str += "
267
+
268
+ Usage:
269
+ #{Opt.progname} #{Opt.cmdline.join(" ")}
270
+
271
+ "
272
+ Opt.doc.each do |i|
273
+ str += ( " %-8s%s" % [ i[0], i[1..-1].join(" ") ] )
274
+ str += "\n"
275
+ end
276
+ str += "
277
+
278
+ Copyright (c) #{Opt.year} by #{Opt.author}
279
+
280
+ "
281
+
282
+ if @@options[ :footer ]
283
+ str += @@options[ :footer ]
284
+ str += "\n"
285
+ end
286
+
287
+ str
288
+ end
289
+
290
+
291
+ # Set optional header for "usage".
292
+ def Spec.setUsageHeader( str )
293
+ @@options[ :header ] = str
294
+ end
295
+
296
+ # Set optional footer for "usage".
297
+ def Spec.setUsageFooter( str )
298
+ @@options[ :footer ] = str
299
+ end
300
+
301
+
302
+ # The primary entry point to Como. Defines the command
303
+ # switches and parses the command line. Performs "usage"
304
+ # display if "help" was selected.
305
+ # @param prog [String] Program (i.e. command) name.
306
+ # @param author [String] Author of the program.
307
+ # @param year [String] Year (or dates) for program.
308
+ # @param defs [Array] Option definitions.
309
+ # @param option [Hash] Option definition's behavioral config (changes @@options defaults).
310
+ def Spec.defineCheckHelp( prog, author, year, defs, option = {} )
311
+ Spec.defineCheck( prog, author, year, defs, option )
312
+ Spec.usage if Opt['help'].given
313
+ end
314
+
315
+ # Same as "defineCheckHelp" except without automatic "help"
316
+ # option processing.
317
+ def Spec.defineCheck( prog, author, year, defs, option = {} )
318
+ begin
319
+ Spec.applyOptionDefaults( option )
320
+ @@options = option
321
+ Opt.specify( prog, author, year, defs )
322
+ Spec.check
323
+ rescue Opt::MissingArgument, Opt::InvalidOption => str
324
+ @@io.puts
325
+ Opt.error( str )
326
+ Spec.usage
327
+ exit( 1 ) if @@options[ :error_exit ]
328
+ end
329
+ end
330
+
331
+
332
+ # Check only.
333
+ def Spec.check
334
+ Opt.parse( @@argv, @@options[ :check ] )
335
+ Opt.checkMissing
336
+ end
337
+
338
+
339
+ # Overlay "option" on top of options defaults (@@options).
340
+ def Spec.applyOptionDefaults( option )
341
+ option.replace( @@options.merge( option ) )
342
+ end
343
+
344
+
345
+ # Check option combination rules.
346
+ def Spec.checkRule( &rule )
347
+ begin
348
+ raise( Opt::InvalidOption, "Option combination mismatch!" ) unless
349
+ Opt.checkRule( &rule )
350
+ rescue Opt::MissingArgument, Opt::InvalidOption => str
351
+ @@io.puts
352
+ Opt.error( str )
353
+
354
+ # Display the possible combination:
355
+ @@io.puts "\n Option combination rules:\n\n"
356
+ Opt::RuleDisplay.new.evalAndDisplay( &rule )
357
+
358
+ Spec.usage
359
+ end
360
+ end
361
+
362
+
363
+ # Additional option check.
364
+ # @param opt [String] Option name.
365
+ # @param error [String] Error string for false return values (from check).
366
+ # @param check [Proc] Checker proc run for the option. Either return false or generate an exception when errors found.
367
+ def Spec.checkAlso( opt, error, &check )
368
+ begin
369
+ if Opt[opt].check( &check ) != true
370
+ raise Opt::InvalidOption, error
371
+ end
372
+ rescue Opt::MissingArgument, Opt::InvalidOption => str
373
+ @@io.puts
374
+ Opt.error( str )
375
+ Spec.usage
376
+ exit( 1 )
377
+ end
378
+ end
379
+
380
+ end
381
+
382
+
383
+
384
+ # Opt includes all options spec information and parsed options
385
+ # and their values. Option instance is accessed with
386
+ # "Opt['name']". The option status (Opt instance) can be
387
+ # queried with for example "given" and "value" methods.
388
+
389
+ class Opt < ComoCommon
390
+
391
+ class Error < StandardError; end
392
+ class MissingArgument < Error; end
393
+ class InvalidOption < Error; end
394
+
395
+
396
+ # Option name.
397
+ attr_accessor :name
398
+
399
+ # Short option string.
400
+ attr_accessor :opt
401
+
402
+ # Long option string.
403
+ attr_accessor :longOpt
404
+
405
+ # Option type.
406
+ attr_accessor :type
407
+
408
+ # Option value.
409
+ attr_accessor :value
410
+
411
+ # Option documentation string.
412
+ attr_accessor :doc
413
+
414
+ # Is option specified?
415
+ attr_accessor :given
416
+
417
+ # Is option hidden (usage).
418
+ attr_accessor :silent
419
+
420
+ # Parsed option specs and option values.
421
+ @@opts = []
422
+
423
+ # Program external arguments (e.g. subprogram args)
424
+ @@external = nil
425
+
426
+
427
+ # Create Opt object:
428
+ # [name] Option name string.
429
+ # [opt] Switch string.
430
+ # [type] Option type. One of:
431
+ # * :switch
432
+ # * :single
433
+ # * :multi
434
+ # * :opt_single
435
+ # * :opt_multi
436
+ # * :opt_any
437
+ # * :default
438
+ # * :exclusive
439
+ # * :silent
440
+ # [doc] Option documentation.
441
+ # [value] Default value.
442
+
443
+ def initialize( name, opt, type, doc, value = nil )
444
+ @name = name
445
+ @opt = opt
446
+ @longOpt = "--#{name}"
447
+ @type = type
448
+ @value = value
449
+ @doc = doc
450
+ @silent = false
451
+ # Whether option was set or not.
452
+ @given = false
453
+ @@opts.push self
454
+ end
455
+
456
+
457
+ # Options list iterator.
458
+ def Opt.each
459
+ @@opts.each do |o|
460
+ yield o
461
+ end
462
+ end
463
+
464
+
465
+ # Options iterator for given options.
466
+ def Opt.each_given
467
+ @@opts.each do |o|
468
+ yield o if o.given
469
+ end
470
+ end
471
+
472
+
473
+ # Number of given options.
474
+ def Opt.givenCount
475
+ cnt = 0
476
+ Opt.each_given do |i|
477
+ cnt += 1
478
+ end
479
+ cnt
480
+ end
481
+
482
+
483
+ # Return option value if given otherwise the default.
484
+ # Example usage: fileName = Opt["file"].apply( "no_name.txt" )
485
+ def apply( default = nil )
486
+ if given
487
+ value
488
+ else
489
+ default
490
+ end
491
+ end
492
+
493
+
494
+ # Check for any non-given required arguments.
495
+ def Opt.checkMissing
496
+
497
+ # Check for any exclusive args first
498
+ @@opts.each do |o|
499
+ if o.type == :exclusive && o.given
500
+ return
501
+ end
502
+ end
503
+
504
+ @@opts.each do |o|
505
+ if o.isRequired
506
+ raise MissingArgument, "Option \"#{o.opt}\" missing..." if !o.given
507
+ end
508
+ end
509
+ end
510
+
511
+
512
+ # Reset "dynamic" class members.
513
+ def Opt.reset
514
+ @@opts = []
515
+ @@external = nil
516
+ end
517
+
518
+
519
+ # Select option object by name operator.
520
+ def Opt.[](str)
521
+ Opt.arg(str)
522
+ end
523
+
524
+
525
+ # Select option object by name.
526
+ def Opt.arg( str )
527
+ if str == nil
528
+ @@opts.each do |o|
529
+ if o.type == :default
530
+ return o
531
+ end
532
+ end
533
+ nil
534
+ else
535
+ @@opts.each do |o|
536
+ if str == o.name
537
+ return o
538
+ end
539
+ end
540
+ nil
541
+ end
542
+ end
543
+
544
+
545
+ # Return program name.
546
+ def Opt.progname
547
+ @@progname
548
+ end
549
+
550
+ # Return program year.
551
+ def Opt.year
552
+ @@year
553
+ end
554
+
555
+ # Return author.
556
+ def Opt.author
557
+ @@author
558
+ end
559
+
560
+
561
+ # Return options that are specified as command external
562
+ # (i.e. after '--').
563
+ def Opt.external
564
+ @@external
565
+ end
566
+
567
+ # Return document strings for options.
568
+ def Opt.doc
569
+ doc = []
570
+ @@opts.each do |o|
571
+ next if o.silent?
572
+ doc.push( [o.opt ? o.opt : o.longOpt, o.doc] )
573
+ end
574
+ doc
575
+ end
576
+
577
+
578
+
579
+ # Set of methods which represent option combination checking.
580
+ # In effect this is a meta language (DSL) for option
581
+ # combinations.
582
+ #
583
+ # Example:
584
+ # RuleCheck.checkRule do
585
+ # one(
586
+ # incr( "gcov", "exclude", "refreshed" ),
587
+ # "manifest",
588
+ # "pairs",
589
+ # "files"
590
+ # )
591
+ # end
592
+ class RuleCheck
593
+
594
+ # Get given count.
595
+ def getScore( *args )
596
+ score = 0
597
+ args.each do |i|
598
+ if i.class == TrueClass || i.class == FalseClass
599
+ score += 1 if i
600
+ else
601
+ score += 1 if Opt[i].given
602
+ end
603
+ end
604
+ score
605
+ end
606
+
607
+ # Special condition when no options are given.
608
+ def none
609
+ Opt.givenCount == 0
610
+ end
611
+
612
+ # Incremental options in order i.e. have to have previous
613
+ # to have later.
614
+ def incr( *args )
615
+ had = 0
616
+ add = true
617
+ scoreAll = 0
618
+ i = 0
619
+ while args[i]
620
+ score = getScore( args[i] )
621
+ had += 1 if ( score == 1 ) && add
622
+ add = false if ( score == 0 )
623
+ scoreAll += score
624
+ i += 1
625
+ end
626
+
627
+ ( had == scoreAll && had > 0 )
628
+ end
629
+
630
+ # Incremental options in order i.e. have to have all later
631
+ # if had first.
632
+ def follow( *args )
633
+ if getScore( args[0] )
634
+ getScore( *args ) == args.length
635
+ else
636
+ true
637
+ end
638
+ end
639
+
640
+ # One of list given.
641
+ def one( *args )
642
+ getScore( *args ) == 1
643
+ end
644
+
645
+ # At least one is given.
646
+ def any( *args )
647
+ getScore( *args ) > 0
648
+ end
649
+
650
+ # All are given.
651
+ def all( *args )
652
+ getScore( *args ) == args.length
653
+ end
654
+ end
655
+
656
+
657
+
658
+ # Display utility for RuleCheck. Same usage model.
659
+ #
660
+ # Example expansion of options:
661
+ #
662
+ # |--# One of:
663
+ # | |--# Adding in order:
664
+ # | | |--<gcov>
665
+ # | | |--<exclude>
666
+ # | | |--<refreshed>
667
+ # | |--<manifest>
668
+ # | |--<pairs>
669
+ # | |--<files>
670
+ #
671
+ class RuleDisplay < ComoCommon
672
+
673
+ def initialize
674
+ # Prefix string for lines. Rules add/rm from it.
675
+ @prefixStr = " "
676
+ self
677
+ end
678
+
679
+ # Eval rules to get an nested array and then display it.
680
+ def evalAndDisplay( &rule )
681
+ printRule( instance_eval( &rule ) )
682
+ end
683
+
684
+ # Increase prefix string.
685
+ def addPrefix( str )
686
+ @prefixStr += str
687
+ end
688
+
689
+ # Remove from prefix (either str or length ).
690
+ def rmPrefix( item )
691
+ if item.class == String
692
+ cnt = item.length
693
+ else
694
+ cnt = item
695
+ end
696
+ @prefixStr = @prefixStr[0..-(cnt+1)]
697
+ end
698
+
699
+ # Print prefix + str.
700
+ def p( str )
701
+ @@io.puts( @prefixStr + str )
702
+ end
703
+
704
+ # Recursively go through the nested array of rule items and
705
+ # print out rules.
706
+ def printRule( arr )
707
+ p( "|--# #{arr[0]}:" )
708
+ item = "| "
709
+ addPrefix( item )
710
+
711
+ arr[1..-1].each do |i|
712
+ if i.class == Array
713
+ printRule( i )
714
+ else
715
+ p( "|--<#{i}>" )
716
+ end
717
+ end
718
+ rmPrefix( item )
719
+ end
720
+
721
+ # Special condition where no arguments are given.
722
+ def none
723
+ [ "NONE" ]
724
+ end
725
+
726
+ # Incremental options in order i.e. have to have previous
727
+ # to have later.
728
+ def incr( *args )
729
+ [ "Adding in order", *args ]
730
+ end
731
+
732
+ # Incremental options in order i.e. have to have all later
733
+ # if had first.
734
+ def follow( *args )
735
+ [ "If first then rest", *args ]
736
+ end
737
+
738
+ # One of list given.
739
+ def one( *args )
740
+ [ "One of", *args ]
741
+ end
742
+
743
+ # At least one is given.
744
+ def any( *args )
745
+ [ "One or more of", *args ]
746
+ end
747
+
748
+ # All are given.
749
+ def all( *args )
750
+ [ "All of", *args ]
751
+ end
752
+
753
+ end
754
+
755
+
756
+
757
+ # Check against option combination rules.
758
+ def Opt.checkRule( &rule )
759
+ RuleCheck.new.instance_eval( &rule )
760
+ end
761
+
762
+
763
+ # Create option spec.
764
+ def Opt.full( name, opt, type, doc = "No doc." )
765
+ new( name, opt, type, doc )
766
+ end
767
+
768
+ # Create switch option spec.
769
+ def Opt.switch( name, opt, doc = "No doc." )
770
+ new( name, opt, :switch, doc, false )
771
+ end
772
+
773
+ # Create exclusive option spec.
774
+ def Opt.exclusive( name, opt, doc = "No doc.", silent = false )
775
+ new( name, opt, :exclusive, doc, false )
776
+ @@opts[-1].silent = silent
777
+ end
778
+
779
+ # Create default option spec, no switch.
780
+ def Opt.default( doc = "No doc." )
781
+ new( nil, "<arg>", :default, doc, [] )
782
+ end
783
+
784
+
785
+ # Specify and check options spec.
786
+ def Opt.specify( progname, author, year, table )
787
+
788
+ # Type checks for valid user input.
789
+ check = Proc.new do |cond,str| raise( ArgumentError, str ) unless cond end
790
+ check.call( progname.class == String, "Program name is not a String" )
791
+ check.call( author.class == String, "Author name is not a String" )
792
+ check.call( year.class == String, "Year is not a String" )
793
+ check.call( table.class == Array, "Option table is not an Array" )
794
+ table.each do |i|
795
+ check.call( i.class == Array, "Option table entry is not an Array" )
796
+ check.call( i.length == 4, "Option table entry length not 4" )
797
+ end
798
+
799
+
800
+ @@progname = progname
801
+ @@year = year
802
+ @@author = author
803
+
804
+ table.each do |e|
805
+
806
+ case e[0]
807
+ when :switch
808
+ Opt.switch( e[1], e[2], e[3] )
809
+ when :exclusive
810
+ Opt.exclusive( e[1], e[2], e[3] )
811
+ when :silent
812
+ Opt.exclusive( e[1], e[2], e[3], true )
813
+ when :single, :multi, :opt_single, :opt_multi, :opt_any
814
+ Opt.full( e[1], e[2], e[0], e[3] )
815
+ when :default
816
+ Opt.default( e[3] )
817
+ end
818
+ end
819
+ end
820
+
821
+
822
+ # Test whether str is an option.
823
+ def Opt.isOpt( str )
824
+ str[0..0] == "-"
825
+ end
826
+
827
+ # Test whether str is an option list terminator.
828
+ def Opt.isOptTerm( str )
829
+ str == "--"
830
+ end
831
+
832
+ # Format value string if escaped.
833
+ def Opt.toValue( str )
834
+ if str[0..0] == "\\"
835
+ str[1..-1]
836
+ else
837
+ str
838
+ end
839
+ end
840
+
841
+ # Option requires argument?
842
+ def hasArg
843
+ case @type
844
+ when :single, :multi, :opt_single, :opt_multi, :opt_any; true
845
+ else false
846
+ end
847
+ end
848
+
849
+ # Option requires many arguments?
850
+ def hasMany
851
+ case @type
852
+ when :multi, :opt_multi, :opt_any; true
853
+ else false
854
+ end
855
+ end
856
+
857
+ # Is mandatory argument?
858
+ def isRequired
859
+ case @type
860
+ when :single, :multi; true
861
+ else false
862
+ end
863
+ end
864
+
865
+ # Test if option is of switch type.
866
+ def isSwitch
867
+ case @type
868
+ when :switch, :exclusive, :default; true
869
+ else false
870
+ end
871
+ end
872
+
873
+ # Test if option is silent.
874
+ def silent?
875
+ @silent
876
+ end
877
+
878
+ # Custom check for the option. User have to know some Como
879
+ # internals.
880
+ def check( &check )
881
+ instance_eval &check
882
+ end
883
+
884
+
885
+ # Find option object by option str.
886
+ def Opt.findOpt( str )
887
+ if str == nil
888
+ ( @@opts.select { |i| i.type == :default } )[0]
889
+ elsif str[0..1] == "--"
890
+ ( @@opts.select { |i| i.longOpt == str } )[0]
891
+ else
892
+ ( @@opts.select { |i| i.opt == str } )[0]
893
+ end
894
+ end
895
+
896
+ # Como error printout.
897
+ def Opt.error( str )
898
+ @@io.puts "#{@@progname} error: #{str}"
899
+ end
900
+
901
+
902
+ # Parse cmdline options from args.
903
+ def Opt.parse( args, checkInvalids = true )
904
+ ind = 0
905
+
906
+ while args[ind]
907
+
908
+ if Opt.isOptTerm( args[ind] )
909
+
910
+ # Rest of the args do not belong to this program.
911
+ ind += 1
912
+ @@external = args[ind..-1]
913
+ break
914
+
915
+ elsif Opt.isOpt( args[ind] )
916
+
917
+ o = Opt.findOpt( args[ind] )
918
+
919
+ if !o
920
+ if checkInvalids
921
+ raise InvalidOption, \
922
+ "Unknown option (\"#{args[ind]}\")..."
923
+ else
924
+ o = Opt.findOpt( nil )
925
+ if !o
926
+ raise InvalidOption, "No default option specified for \"#{args[ind]}\"..."
927
+ else
928
+ # Default option.
929
+ o.value.push Opt.toValue( args[ind] )
930
+ ind += 1
931
+ end
932
+ end
933
+
934
+ elsif o && o.hasArg
935
+
936
+ ind += 1
937
+
938
+ if ( !args[ind] || Opt.isOpt( args[ind] ) ) &&
939
+ o.type != :opt_any
940
+
941
+ raise MissingArgument, \
942
+ "No argument given for \"#{o.opt}\"..."
943
+
944
+ else
945
+
946
+ if o.hasMany
947
+
948
+ # Get all argument for multi-option.
949
+ o.value = [] if !o.given
950
+ while ( args[ind] &&
951
+ !Opt.isOpt( args[ind] ) )
952
+ o.value.push Opt.toValue( args[ind] )
953
+ ind += 1
954
+ end
955
+
956
+ else
957
+
958
+ # Get one argument for single-option.
959
+
960
+ if o.given
961
+ raise InvalidOption, \
962
+ "Too many arguments for option (\"#{o.name}\")..."
963
+ else
964
+ o.value = Opt.toValue( args[ind] )
965
+ end
966
+ ind += 1
967
+ end
968
+ end
969
+
970
+ o.given = true
971
+
972
+ else
973
+
974
+ if !o
975
+ raise InvalidOption, "No valid options specified..."
976
+ else
977
+ o.value = !o.value if !o.given
978
+ o.given = true
979
+ ind += 1
980
+ end
981
+ end
982
+
983
+ else
984
+
985
+ # Default option.
986
+
987
+ o = Opt.findOpt( nil )
988
+
989
+ if !o
990
+ raise InvalidOption, "No default option specified for \"#{args[ind]}\"..."
991
+ else
992
+ while ( args[ind] && !Opt.isOpt( args[ind] ) )
993
+ o.value.push Opt.toValue( args[ind] )
994
+ ind += 1
995
+ end
996
+ end
997
+ end
998
+ end
999
+ end
1000
+
1001
+
1002
+ # Return cmdline usage strings for options in an Array.
1003
+ def Opt.cmdline
1004
+ opts = []
1005
+
1006
+ @@opts.each do |o|
1007
+
1008
+ next if o.silent?
1009
+
1010
+ prural = nil
1011
+ case o.type
1012
+ when :multi, :opt_multi; prural = "+"
1013
+ when :opt_any; prural = "*"
1014
+ else prural = ""
1015
+ end
1016
+
1017
+ if !( o.isSwitch )
1018
+ name = " <#{o.name}>#{prural}"
1019
+ else
1020
+ name = ""
1021
+ end
1022
+
1023
+ if o.opt == nil
1024
+ opt = o.longOpt
1025
+ else
1026
+ opt = o.opt
1027
+ end
1028
+
1029
+ if o.isRequired
1030
+ opts.push "#{opt}#{name}"
1031
+ else
1032
+ opts.push "[#{opt}#{name}]"
1033
+ end
1034
+ end
1035
+
1036
+ opts
1037
+ end
1038
+
1039
+ end
1040
+
1041
+ end