como 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +12 -0
- data/README.rdoc +11 -5
- data/Rakefile +6 -1
- data/doc/Como/ArgsParseState.html +912 -0
- data/doc/Como/ComoCommon.html +305 -0
- data/doc/Como/MainOpt.html +636 -0
- data/doc/Como/MasterOpt.html +636 -0
- data/doc/Como/Opt/ErrorWithData.html +304 -0
- data/doc/Como/Opt/InvalidOption.html +158 -0
- data/doc/Como/Opt/MissingArgument.html +158 -0
- data/doc/Como/Opt.html +6098 -0
- data/doc/Como/RuleCheck.html +933 -0
- data/doc/Como/RuleDisplay.html +1193 -0
- data/doc/Como/Spec.html +1750 -0
- data/doc/Como.html +625 -0
- data/doc/_index.html +242 -0
- data/doc/class_list.html +53 -0
- data/doc/css/common.css +1 -0
- data/doc/css/full_list.css +57 -0
- data/doc/css/style.css +338 -0
- data/doc/file.CHANGELOG.html +90 -0
- data/doc/file.README.html +94 -0
- data/doc/file.como.html +1962 -0
- data/doc/file_list.html +58 -0
- data/doc/frames.html +28 -0
- data/doc/index.html +94 -0
- data/doc/js/app.js +214 -0
- data/doc/js/full_list.js +178 -0
- data/doc/js/jquery.js +4 -0
- data/doc/method_list.html +838 -0
- data/doc/top-level-namespace.html +112 -0
- data/lib/como.rb +1660 -661
- data/test/como_compatible +37 -0
- data/test/como_config +44 -0
- data/test/como_options +36 -0
- data/test/como_queries +44 -0
- data/test/como_rule_1 +28 -0
- data/test/como_rule_2 +28 -0
- data/test/como_subcmd +72 -0
- data/test/como_subcmd_config +88 -0
- data/test/golden/compatible.txt +438 -0
- data/test/golden/config.txt +319 -0
- data/test/golden/options.txt +438 -0
- data/test/golden/queries.txt +78 -0
- data/test/golden/rule_1.txt +454 -0
- data/test/golden/rule_2.txt +476 -0
- data/test/golden/subcmd.txt +360 -0
- data/test/golden/subcmd_config.txt +534 -0
- data/test/test_como.rb +22 -328
- data/test/test_compatible +28 -0
- data/test/test_config +12 -0
- data/test/test_options +28 -0
- data/test/test_queries +7 -0
- data/test/test_rule_1 +27 -0
- data/test/test_rule_2 +27 -0
- data/test/test_subcmd +30 -0
- data/test/test_subcmd_config +31 -0
- metadata +62 -6
data/lib/como.rb
CHANGED
@@ -1,76 +1,108 @@
|
|
1
1
|
# = Como
|
2
2
|
#
|
3
3
|
# == Introduction
|
4
|
+
#
|
4
5
|
# Como provides low manifest command line option parsing and
|
5
|
-
#
|
6
|
+
# deployment. The command line options are described in compact table
|
6
7
|
# format and option values are stored to conveniently named
|
7
|
-
# properties. Como
|
8
|
-
#
|
8
|
+
# properties. Como builds command usage information based on the
|
9
|
+
# option table (+ generic program info) and displays it automatically
|
10
|
+
# if necessary. Como supports also subcommands and checking for option
|
11
|
+
# combinations using a simple DSL.
|
12
|
+
#
|
13
|
+
#
|
14
|
+
#
|
15
|
+
# == Usage Examples
|
9
16
|
#
|
10
|
-
#
|
11
|
-
#
|
17
|
+
# Two simple examples are presented in this section. First one
|
18
|
+
# includes a straight forward command definition and the second is a
|
19
|
+
# bit more complicated with subcommand feature in use.
|
20
|
+
#
|
21
|
+
# === Simple example
|
22
|
+
#
|
23
|
+
# Below is a small example program ("como_simple") that demonstrates
|
12
24
|
# typical usage.
|
13
25
|
#
|
14
|
-
#
|
26
|
+
# ==== Program listing
|
27
|
+
#
|
15
28
|
# require "como"
|
16
29
|
# include Como
|
17
30
|
#
|
18
31
|
# # Define command line arguments:
|
19
|
-
# Spec.
|
32
|
+
# Spec.command( "como_simple", "Programmer", "2013",
|
20
33
|
# [
|
21
|
-
# [ :
|
22
|
-
# [ :single, "file", "-f", "File argument." ],
|
34
|
+
# [ :single, "file", "-f", "File argument." ],
|
23
35
|
# [ :switch, "debug", "-d", "Enable debugging." ],
|
24
36
|
# ] )
|
25
37
|
#
|
26
|
-
# puts "File option: #{Opt['file'].value}"
|
27
|
-
# puts "Debugging selected!" if Opt['debug'].given
|
38
|
+
# puts " File option: #{Opt['file'].value}"
|
39
|
+
# puts " Debugging selected!" if Opt['debug'].given
|
40
|
+
#
|
28
41
|
#
|
29
|
-
#
|
42
|
+
# First Como is required and Como module is included.
|
43
|
+
#
|
44
|
+
# "Spec.command" method takes 4 arguments:
|
30
45
|
# [progname] Name of the program (or command).
|
31
46
|
# [author] Author of the program.
|
32
47
|
# [year] Year (or any date) for the program.
|
33
|
-
# [option table] Description of the command options
|
34
|
-
# entries in each sub-array.
|
48
|
+
# [option table] Description of the command options.
|
35
49
|
#
|
36
|
-
# Each option table entry
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# [:single] Single means that the option requires one argument (and
|
50
|
+
# Each option table entry (row/sub-array) includes 4 fields and
|
51
|
+
# specifies one option:
|
52
|
+
# [ type, name, mnemonic, doc ]
|
53
|
+
#
|
54
|
+
# Two different types are present in the example:
|
55
|
+
# [:single] Single means that the option requires one argument (and
|
56
|
+
# only one).
|
42
57
|
# [:switch] Switch is an optional flag (default value is false).
|
43
58
|
#
|
44
|
-
# Option name is used to reference the option value
|
45
|
-
# The command line option values are stored
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# long option, i.e. one could
|
49
|
-
# line.
|
59
|
+
# Option name is used to reference the option value that user has
|
60
|
+
# given. The command line option values are stored automatically. For
|
61
|
+
# example the file option value is returned by:
|
62
|
+
# Opt['file'].value
|
63
|
+
# The option name also doubles as long option format, i.e. one could
|
64
|
+
# use "--file <filename>" on the command line.
|
50
65
|
#
|
51
66
|
# Existence of optional options can be tested using the "given"
|
52
|
-
# method. For example
|
53
|
-
#
|
67
|
+
# method. For example
|
68
|
+
# Opt['debug'].given
|
69
|
+
# would return "true" if "-d" was given on the command line.
|
70
|
+
#
|
71
|
+
# Mnemonic is the short form option specification e.g. "-f". If short
|
72
|
+
# form is replaced with "nil", the long option format is only
|
73
|
+
# available.
|
74
|
+
#
|
75
|
+
# Doc includes documentation for the option. It is displayed when
|
76
|
+
# "help" ("-h") option is given. Help option is added to the command
|
77
|
+
# automatically as default behavior.
|
78
|
+
#
|
79
|
+
# ==== Simple example executions
|
54
80
|
#
|
55
|
-
# === Example executions
|
56
81
|
# Normal behavior would be achieved by executing:
|
57
|
-
# shell>
|
82
|
+
# shell> como_simple -f example -d
|
58
83
|
#
|
59
84
|
# The program would execute with the following output:
|
60
|
-
#
|
61
|
-
#
|
85
|
+
# File option: example
|
86
|
+
# Debugging selected!
|
87
|
+
#
|
88
|
+
# Same output would be achieved with:
|
89
|
+
# shell> como_simple --file example --debug
|
90
|
+
#
|
91
|
+
# Since option name doubles as long option.
|
62
92
|
#
|
63
|
-
# Como includes certain "extra" behavior out-of-box.
|
64
|
-
#
|
65
|
-
#
|
93
|
+
# Como includes certain "extra" behavior out-of-box. Required
|
94
|
+
# arguments are checked for existence and error is displayed if
|
95
|
+
# arguments are not given.
|
96
|
+
#
|
97
|
+
# For example given the command:
|
98
|
+
# shell> como_simple
|
66
99
|
#
|
67
100
|
# The following is displayed on the screen:
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
101
|
+
#
|
102
|
+
# como_simple error: Option "-f" missing for "como_simple"...
|
71
103
|
#
|
72
104
|
# Usage:
|
73
|
-
#
|
105
|
+
# como_simple -f <file> [-d]
|
74
106
|
#
|
75
107
|
# -f File argument.
|
76
108
|
# -d Enable debugging.
|
@@ -78,64 +110,236 @@
|
|
78
110
|
#
|
79
111
|
# Copyright (c) 2013 by Programmer
|
80
112
|
#
|
113
|
+
#
|
81
114
|
# Missing option error is displayed since "file" is a mandatory
|
82
|
-
# option. The error
|
115
|
+
# option. The error message is followed by "usage" display (Usage
|
116
|
+
# Help). Documentation string is taken from the option specification to
|
117
|
+
# "usage" display.
|
118
|
+
#
|
119
|
+
# Given the command:
|
120
|
+
# shell> como_simple -h
|
121
|
+
#
|
122
|
+
# would display the same "usage" screen except without the error
|
123
|
+
# line.
|
124
|
+
#
|
125
|
+
# === Subccommand example
|
126
|
+
#
|
127
|
+
# Subcmd example includes a program which has subcommands. Subcommands
|
128
|
+
# can have their own command line switches and options.
|
129
|
+
#
|
130
|
+
# ==== Program listing
|
131
|
+
#
|
132
|
+
# require "como"
|
133
|
+
# include Como
|
134
|
+
#
|
135
|
+
# Spec.program( "Programmer", "2013" ) do
|
136
|
+
#
|
137
|
+
# subcmd( "como_subcmd", [
|
138
|
+
# [ :subcmd, "add", nil, "Add file." ],
|
139
|
+
# [ :subcmd, "rm", nil, "Remove file." ],
|
140
|
+
# ], )
|
141
|
+
#
|
142
|
+
# subcmd( "add", [
|
143
|
+
# [ :switch, "force", "-fo", "Force operation." ],
|
144
|
+
# [ :opt_single, "password", "-p", "User password." ],
|
145
|
+
# [ :opt_single, "username", "-u", "Username." ],
|
146
|
+
# [ :single, "file", "-f", "File." ],
|
147
|
+
# ] )
|
148
|
+
#
|
149
|
+
# checkRule do
|
150
|
+
# one(
|
151
|
+
# '-fo',
|
152
|
+
# all( 'password', 'username' )
|
153
|
+
# )
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# subcmd( "rm", [
|
157
|
+
# [ :single, "file", "-f", "File." ],
|
158
|
+
# ] )
|
159
|
+
#
|
160
|
+
# end
|
161
|
+
#
|
162
|
+
# subcmd = Opt.main.givenSubcmd
|
163
|
+
#
|
164
|
+
# case subcmd.name
|
165
|
+
# when 'add'; puts " Adding file \"#{subcmd['file'].value}\"..."
|
166
|
+
# when 'rm'; puts " Removing file \"#{subcmd['file'].value}\"..."
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# "Spec.program" method defines a program (command) with
|
170
|
+
# subcommands. The author and date are provided as parameters, and the
|
171
|
+
# program and subcommand options are defined in block.
|
172
|
+
#
|
173
|
+
# The first "subcmd" method call defines the main command
|
174
|
+
# ("Opt.main") which represents the program. It has two "subcmd"
|
175
|
+
# options.
|
176
|
+
#
|
177
|
+
# The rest of the "subcmd" methods define subcommands for the parent
|
178
|
+
# command. This example includes one subcommand level, but multiple
|
179
|
+
# levels are allowed.
|
180
|
+
#
|
181
|
+
# The "checkRule" method defines option combination (RuleCheck) for
|
182
|
+
# the previous subcommand definition. In this case the definition
|
183
|
+
# allows "add" to have either the "-fo" option defined or "password"
|
184
|
+
# and "username" in combination.
|
185
|
+
#
|
186
|
+
# Main (root) commands can be referenced through
|
187
|
+
# Opt.main
|
188
|
+
# or alternatively
|
189
|
+
# Opt['como_subcmd']
|
190
|
+
#
|
191
|
+
# The subcommands can be referenced through "Opt.main" (etc.)
|
192
|
+
# Opt.main['add']
|
193
|
+
# Opt['como_subcmd']['add']
|
194
|
+
#
|
195
|
+
# or directly from "Opt" if subcommand names do not collide:
|
196
|
+
# Opt['add']
|
197
|
+
#
|
198
|
+
# The given subcommand can be accessed with "givenSubcmd" method from
|
199
|
+
# each parent command.
|
200
|
+
#
|
201
|
+
# ==== Subcommand example executions
|
202
|
+
#
|
203
|
+
# Normal behavior would be achieved by executing:
|
204
|
+
# shell> como_subcmd add -fo -f example
|
205
|
+
#
|
206
|
+
# The program would execute with the following output:
|
207
|
+
# Adding file "example"...
|
208
|
+
#
|
209
|
+
# The option combinations for "add" subcommand are automatically
|
210
|
+
# checked. Thus executing:
|
211
|
+
# shell> como_subcmd add -f example
|
212
|
+
#
|
213
|
+
# Would result to:
|
214
|
+
# como_subcmd error: Option combination mismatch!
|
215
|
+
#
|
216
|
+
# Subcommand "add" usage:
|
217
|
+
# como_subcmd add [-fo] [-p <password>] [-u <username>] -f <file>
|
218
|
+
#
|
219
|
+
# -fo Force operation.
|
220
|
+
# -p User password.
|
221
|
+
# -u Username.
|
222
|
+
# -f File.
|
223
|
+
#
|
224
|
+
#
|
225
|
+
# Option Combinations:
|
226
|
+
# |--# One of:
|
227
|
+
# | |--<-fo>
|
228
|
+
# | |--# All of:
|
229
|
+
# | | |--<password>
|
230
|
+
# | | |--<username>
|
231
|
+
#
|
232
|
+
# Since the combination rule requires either "-fo" or both "password"
|
233
|
+
# and "username" in a pair.
|
234
|
+
#
|
235
|
+
# Help is automatically provided on each command level, thus these are
|
236
|
+
# both valid.
|
237
|
+
# shell> como_subcmd -h
|
238
|
+
# and
|
239
|
+
# shell> como_subcmd rm -h
|
83
240
|
#
|
84
|
-
# shell> como_test -h
|
85
241
|
#
|
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
242
|
#
|
243
|
+
# == Option specification
|
244
|
+
#
|
245
|
+
# === Overview
|
246
|
+
#
|
247
|
+
# Option specification includes the minimum set of information
|
248
|
+
# required for command line parsing. It is used to:
|
249
|
+
# * Parse the command line.
|
250
|
+
# * Check for wrong options and report.
|
251
|
+
# * Check for mandatory arguments and report.
|
252
|
+
# * Set the options given/non-given state.
|
253
|
+
# * Set the options value. Array/String for all except true/false for
|
254
|
+
# switches.
|
255
|
+
# * Generate Usage Help printout.
|
90
256
|
#
|
91
|
-
#
|
257
|
+
# === Option types
|
92
258
|
#
|
93
259
|
# The following types can be defined for the command line options:
|
260
|
+
# [:subcmd] Subcmd option. Subcmd specific options are provided
|
261
|
+
# separately.
|
94
262
|
# [:switch] Single switch option (no arguments).
|
95
263
|
# [:single] Mandatory single argument option.
|
96
|
-
# [:multi] Mandatory multiple argument option
|
264
|
+
# [:multi] Mandatory multiple argument option (one or many). Option
|
265
|
+
# values in array.
|
97
266
|
# [:opt_single] Optional single argument option.
|
98
|
-
# [:opt_multi] Optional multiple argument option
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
# referred with
|
267
|
+
# [:opt_multi] Optional multiple argument option (one or many). Option
|
268
|
+
# values in array.
|
269
|
+
# [:opt_any] Optional multiple argument option (also none
|
270
|
+
# accepted). Option values in array.
|
271
|
+
# [:default] Default option (no switch associated). Name and option
|
272
|
+
# String values can be left out, since only the document
|
273
|
+
# string is used. Default option is referred with
|
274
|
+
# ":default" or "nil".
|
105
275
|
# [:exclusive] Option that does not coexist with other options.
|
106
276
|
# [:silent] Option that does not coexist with other options and is not
|
107
|
-
# displayed as an option in
|
277
|
+
# displayed as an option in Usage Help display. In effect a
|
108
278
|
# sub-option of :exclusive.
|
109
279
|
#
|
280
|
+
# Options use typically all the 4 option fields:
|
281
|
+
# [ type, name, mnemonic, doc ]
|
282
|
+
#
|
283
|
+
# "type" field is mandatory for all options.
|
284
|
+
#
|
285
|
+
# "name" field is also mandatory for all options. "mnemonic" can be
|
286
|
+
# left out, but then option accepts only long option format.
|
110
287
|
#
|
111
|
-
#
|
288
|
+
# ":default" uses only "doc" and ":subcmd" doesn't use the "mnemonic"
|
289
|
+
# field.
|
290
|
+
#
|
291
|
+
# ":multi", ":opt_multi", and ":opt_any" option arguments are
|
292
|
+
# terminated only when an option specifier is found. This can be a
|
293
|
+
# problem if ":default" option follows. The recommended solution is to
|
294
|
+
# use a ":silent" option that can be used to terminate the argument
|
295
|
+
# list. For example:
|
296
|
+
# [ :silent, "terminator", "-", "The terminator." ],
|
297
|
+
#
|
298
|
+
#
|
299
|
+
# === Option specification method configuration
|
112
300
|
#
|
113
|
-
#
|
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.
|
301
|
+
# Option behavior can be controlled with several configuration options.
|
118
302
|
#
|
119
|
-
#
|
120
|
-
# "Spec.
|
303
|
+
# The configuration options are provided in a Hash. These are the
|
304
|
+
# passed as the last regular parameter for both "Spec.command" and
|
305
|
+
# "Spec.program" methods. Setting the configuration at "Spec.program"
|
306
|
+
# will propagate the config options to all the subcommands as
|
307
|
+
# well. Configuration can be given to each subcommand separately to
|
308
|
+
# override the inherited config values. Subcommand settings are not
|
309
|
+
# inherited, but apply only in the subcommand.
|
121
310
|
#
|
122
|
-
#
|
123
|
-
#
|
311
|
+
# The usable configuration Hash keys:
|
312
|
+
# [:autohelp] Add help option automatically (default: true). Custom
|
313
|
+
# help option can be provided and it can be made also
|
314
|
+
# visible to user.
|
315
|
+
# [:rulehelp] Include RuleCheck help to Usage Help (default: false).
|
124
316
|
# [:header] Header lines before standard usage printout.
|
125
317
|
# [:footer] Footer lines after standard usage printout.
|
126
|
-
# [:
|
127
|
-
#
|
128
|
-
# [:
|
318
|
+
# [:subcheck] Automatically check that a subcommand is provided
|
319
|
+
# (default: true).
|
320
|
+
# [:check_missing] Check for missing arguments (default: true).
|
321
|
+
# [:tab] Tab stop column for option documentation (default: 12).
|
322
|
+
# [:help_exit] Exit program if help displayed (default: true).
|
323
|
+
# [:error_exit] Exit program if error in options (default: true).
|
324
|
+
#
|
129
325
|
#
|
130
326
|
#
|
131
|
-
# ==
|
327
|
+
# == Option referencing
|
132
328
|
#
|
329
|
+
# === Existence and values
|
330
|
+
#
|
133
331
|
# Opt class includes the parsed option values. All options can be
|
134
|
-
# tested whether they are specified on the command line using
|
135
|
-
#
|
332
|
+
# tested whether they are specified on the command line using:
|
333
|
+
# Opt['name'].given
|
334
|
+
#
|
335
|
+
# The "given" method takes optionally a block argument. When block
|
336
|
+
# argument is used, the block is supplied with option value and the
|
337
|
+
# block is executed if the option has been set (See: Opt#given).
|
136
338
|
#
|
137
|
-
#
|
138
|
-
#
|
339
|
+
# Provided value is returned by:
|
340
|
+
# Opt['name'].value
|
341
|
+
#
|
342
|
+
# For ":switch" type it is true/false value and for the other types a
|
139
343
|
# String or an Array of Strings.
|
140
344
|
#
|
141
345
|
# If an option takes multiple arguments, the value for the option is
|
@@ -144,71 +348,77 @@
|
|
144
348
|
# puts val
|
145
349
|
# end
|
146
350
|
#
|
351
|
+
# Short syntax for value referencing is performed with unary operator
|
352
|
+
# "~". Thus
|
353
|
+
# ~Opt['files']
|
354
|
+
# is equal to
|
355
|
+
# Opt['files'].value
|
356
|
+
#
|
147
357
|
# With ":opt_any" type, the user should first check if the option was given:
|
148
358
|
# Opt['many_files_or_none'].given
|
149
359
|
# Then check how many arguments where given:
|
150
360
|
# Opt['many_files_or_none'].value.length
|
151
361
|
# And finally decide what to do.
|
152
362
|
#
|
153
|
-
#
|
154
|
-
# is returned as an Array with "Opt.external"
|
363
|
+
# === Options including parameters
|
155
364
|
#
|
365
|
+
# Sometimes it is convenient for the program to use an option to
|
366
|
+
# include multiple parameter settings. These settings can be parsed
|
367
|
+
# and mapped to a Hash. Como performs automatic conversion to numeric
|
368
|
+
# values if possible. For example with option:
|
369
|
+
# --set rounds=10 length=5
|
370
|
+
# Como can be used extract the parameter values with the "params" method:
|
371
|
+
# Opt['set'].params
|
372
|
+
# And a Hash is returned:
|
373
|
+
# { 'rounds' => 10, 'length' => 5 }
|
156
374
|
#
|
157
|
-
#
|
375
|
+
# === Subcommand options
|
158
376
|
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
377
|
+
# The given subcommand for the parent command is return by
|
378
|
+
# "givenSubcmd". Commonly the program creator should just check
|
379
|
+
# directly which subcommand has been selected and check for any
|
380
|
+
# subcommand options set. For example:
|
381
|
+
# if Opt['como_subcmd']['add'].given
|
382
|
+
# ...
|
162
383
|
#
|
384
|
+
# === Program external options
|
163
385
|
#
|
164
|
-
#
|
386
|
+
# If the user gives the "--" option (double-dash), the arguments after
|
387
|
+
# that option is returned as an Array with "Opt.external".
|
165
388
|
#
|
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
389
|
#
|
390
|
+
#
|
391
|
+
# == Option combination checks
|
392
|
+
#
|
393
|
+
# Como provides a facility to create relations between options using
|
394
|
+
# RuleCheck DSL. This is needed since sometimes options have to be
|
395
|
+
# used in combination to make sense for the program. Also options
|
396
|
+
# might be mutually exclusive.
|
397
|
+
#
|
200
398
|
# The following rules can be used (in combination):
|
201
399
|
# [all] All options in the list.
|
202
400
|
# [one] One and only one from the list.
|
203
401
|
# [any] At least one of the list is given.
|
204
402
|
# [none] No options are required.
|
403
|
+
# [inv] Logical negation for existence.
|
205
404
|
# [incr] Incremental options in order i.e. have to have previous to
|
206
405
|
# have later.
|
207
406
|
# [follow] Incremental options in order i.e. have to have all later if
|
208
407
|
# had first.
|
408
|
+
# [meh] Dont care, always succeeds.
|
409
|
+
#
|
410
|
+
# Examples can be found above.
|
411
|
+
#
|
412
|
+
#
|
413
|
+
# == Customization
|
414
|
+
#
|
415
|
+
# If the default behavior is not satisfactory, changes can be
|
416
|
+
# implemented simply by overloading the existing functions. Some
|
417
|
+
# knowledge of the internal workings of Como is required though.
|
209
418
|
|
210
419
|
module Como
|
211
420
|
|
421
|
+
|
212
422
|
# IO stream options for Como classes.
|
213
423
|
class ComoCommon
|
214
424
|
|
@@ -230,206 +440,494 @@ module Como
|
|
230
440
|
# User interface for Como.
|
231
441
|
class Spec < ComoCommon
|
232
442
|
|
233
|
-
#
|
234
|
-
|
443
|
+
# Create specification for program with subcmds.
|
444
|
+
#
|
445
|
+
# @param author [String] Program author.
|
446
|
+
# @param year [String] Year (or dates) for program.
|
447
|
+
# @yield [] Subcmd definitions.
|
448
|
+
def Spec.program( author, year, config = nil, &defs )
|
449
|
+
if config
|
450
|
+
Opt.configOverlay( config )
|
451
|
+
end
|
452
|
+
spec = Spec.new( author, year )
|
453
|
+
spec.instance_eval( &defs )
|
454
|
+
Opt.main.check( ArgsParseState.new( @@argv ) )
|
455
|
+
end
|
235
456
|
|
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
457
|
|
245
|
-
#
|
246
|
-
|
247
|
-
|
458
|
+
# The primary entry point to Como. Defines the command
|
459
|
+
# switches and parses the command line. Performs "usage"
|
460
|
+
# display if "help" was selected.
|
461
|
+
#
|
462
|
+
# @param prog [String] Program (i.e. command) name.
|
463
|
+
# @param author [String] Author of the program.
|
464
|
+
# @param year [String] Year (or dates) for program.
|
465
|
+
# @param defs [Array<Array>] Option definitions.
|
466
|
+
# @param config [Hash] Option definition's behavioral config
|
467
|
+
# (changes @@config defaults).
|
468
|
+
def Spec.command( prog, author, year, defs, config = {} )
|
469
|
+
Spec.defineCheck( prog, author, year, defs, config )
|
470
|
+
Spec.usage if Opt['help'].given
|
248
471
|
end
|
249
472
|
|
250
|
-
#
|
251
|
-
def Spec.
|
252
|
-
|
253
|
-
exit( 1 ) if @@options[ :help_exit ]
|
473
|
+
# Alias to Spec.command.
|
474
|
+
def Spec.defineCheckHelp( prog, author, year, defs, config = {} )
|
475
|
+
Spec.command( prog, author, year, defs, config )
|
254
476
|
end
|
255
|
-
|
256
477
|
|
257
|
-
#
|
258
|
-
|
259
|
-
|
478
|
+
# Same as "defineCheckHelp" except without automatic "help"
|
479
|
+
# option processing.
|
480
|
+
def Spec.defineCheck( prog, author, year, defs, config = {} )
|
481
|
+
spec = Spec.new( author, year )
|
482
|
+
spec.subcmd( prog, defs, config )
|
483
|
+
Opt.main.check( ArgsParseState.new( @@argv ) )
|
484
|
+
end
|
260
485
|
|
261
|
-
if @@options[ :header ]
|
262
|
-
str += @@options[ :header ]
|
263
|
-
str += "\n"
|
264
|
-
end
|
265
486
|
|
266
|
-
|
487
|
+
# Create Spec object that can handle subcmd definitions.
|
488
|
+
#
|
489
|
+
# @param author [String] Program author.
|
490
|
+
# @param year [String] Year (or dates) for program.
|
491
|
+
def initialize( author, year )
|
492
|
+
@author = author
|
493
|
+
@year = year
|
267
494
|
|
268
|
-
|
269
|
-
|
495
|
+
Spec.ArgCheck( author.class == String, "Author name is not a String" )
|
496
|
+
Spec.ArgCheck( year.class == String, "Year is not a String" )
|
497
|
+
end
|
270
498
|
|
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
499
|
|
278
|
-
|
500
|
+
# Define subcommand options.
|
501
|
+
#
|
502
|
+
# @param cmd [String] Subcmd name.
|
503
|
+
# @param defs [Array<Array>] Option definition table.
|
504
|
+
# @param config [] Configuration options.
|
505
|
+
def subcmd( cmd, defs, config = {} )
|
279
506
|
|
280
|
-
|
507
|
+
unless Opt.main
|
508
|
+
|
509
|
+
main = MainOpt.new( @author, @year,
|
510
|
+
cmd, nil, :subcmd, nil )
|
511
|
+
Opt.setMain( main )
|
512
|
+
subcmd = main
|
513
|
+
|
514
|
+
else
|
515
|
+
|
516
|
+
subcmd = Opt.findOpt( cmd )
|
517
|
+
|
518
|
+
Opt.setSubcmd( subcmd )
|
519
|
+
|
520
|
+
Spec.ArgCheck( false, "Subcommand \"#{cmd}\" not defined." ) unless subcmd
|
281
521
|
|
282
|
-
if @@options[ :footer ]
|
283
|
-
str += @@options[ :footer ]
|
284
|
-
str += "\n"
|
285
522
|
end
|
286
523
|
|
287
|
-
|
288
|
-
|
524
|
+
# Overlay user config on top of default.
|
525
|
+
subcmd.applyConfig( config )
|
289
526
|
|
527
|
+
if subcmd.config[ :autohelp ]
|
528
|
+
# Automatically add the help option.
|
529
|
+
defs.insert( 0, [ :silent, "help", "-h", "Display usage info." ] )
|
530
|
+
end
|
290
531
|
|
291
|
-
|
292
|
-
|
293
|
-
@@options[ :header ] = str
|
294
|
-
end
|
532
|
+
subcmd.setSubopt( *Spec.specify( defs ) )
|
533
|
+
subcmd
|
295
534
|
|
296
|
-
# Set optional footer for "usage".
|
297
|
-
def Spec.setUsageFooter( str )
|
298
|
-
@@options[ :footer ] = str
|
299
535
|
end
|
300
536
|
|
301
537
|
|
302
|
-
#
|
303
|
-
#
|
304
|
-
#
|
305
|
-
|
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
|
538
|
+
# Specify and check options spec.
|
539
|
+
#
|
540
|
+
# @param table [Array<Array>] Option definition table.
|
541
|
+
def Spec.specify( table )
|
314
542
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
543
|
+
options = {}
|
544
|
+
subcmds = {}
|
545
|
+
|
546
|
+
# Type checks for valid user input.
|
547
|
+
Spec.ArgCheck( table.class == Array, "Option table is not an Array" )
|
548
|
+
|
549
|
+
table.each_index do |idx|
|
550
|
+
|
551
|
+
i = table[ idx ]
|
552
|
+
|
553
|
+
Spec.ArgCheck( i.class == Array, "Option table entry is not an Array" )
|
554
|
+
|
555
|
+
if i[0] == :default && i.length == 2
|
556
|
+
|
557
|
+
# Add 2 dummy entries for :default type if needed.
|
558
|
+
table[ idx ] = [ i[0], nil, nil, i[1] ]
|
559
|
+
|
560
|
+
elsif i[0] == :subcmd && i.length == 3
|
561
|
+
|
562
|
+
# Add 1 dummy entry for :subcmd type if needed.
|
563
|
+
table[ idx ] = [ i[0], i[1], nil, i[2] ]
|
564
|
+
end
|
565
|
+
|
566
|
+
Spec.ArgCheck( table[ idx ].length == 4, "Option table entry length not 4" )
|
567
|
+
end
|
568
|
+
|
569
|
+
|
570
|
+
table.each do |e|
|
571
|
+
|
572
|
+
if e[0] == :subcmd
|
573
|
+
|
574
|
+
subcmds[ e[1] ] = Opt.subcmd( e[1], e[3] )
|
575
|
+
|
576
|
+
else
|
577
|
+
|
578
|
+
option = nil
|
579
|
+
|
580
|
+
case e[0]
|
581
|
+
|
582
|
+
when :switch
|
583
|
+
option = Opt.switch( e[1], e[2], e[3] )
|
584
|
+
|
585
|
+
when :exclusive
|
586
|
+
option = Opt.exclusive( e[1], e[2], e[3] )
|
587
|
+
|
588
|
+
when :silent
|
589
|
+
option = Opt.exclusive( e[1], e[2], e[3], true )
|
590
|
+
|
591
|
+
when :single, :multi, :opt_single, :opt_multi, :opt_any
|
592
|
+
option = Opt.full( e[1], e[2], e[0], e[3] )
|
593
|
+
|
594
|
+
when :default
|
595
|
+
option = Opt.defaultOpt( e[3] )
|
596
|
+
|
597
|
+
else
|
598
|
+
raise "Unknown option type: \"#{e[0]}\"..."
|
599
|
+
end
|
600
|
+
|
601
|
+
options[ option.name ] = option
|
602
|
+
|
603
|
+
end
|
604
|
+
|
328
605
|
end
|
606
|
+
|
607
|
+
[ options.values, subcmds.values ]
|
329
608
|
end
|
330
609
|
|
331
610
|
|
332
|
-
#
|
333
|
-
|
334
|
-
|
335
|
-
|
611
|
+
# Command line options source.
|
612
|
+
@@argv = ARGV
|
613
|
+
|
614
|
+
# Set command line options source, i.e. @@argv (default: ARGV).
|
615
|
+
def Spec.setArgv( newArgv )
|
616
|
+
@@argv = newArgv
|
336
617
|
end
|
337
618
|
|
619
|
+
# Display program usage (and optionally exit).
|
620
|
+
def Spec.usage
|
621
|
+
Opt.main.usage
|
622
|
+
end
|
338
623
|
|
339
|
-
#
|
340
|
-
def Spec.
|
341
|
-
|
624
|
+
# Set optional header for "usage".
|
625
|
+
def Spec.setUsageHeader( str )
|
626
|
+
Opt.main.setUsageHeader( str )
|
342
627
|
end
|
343
628
|
|
344
629
|
|
345
|
-
#
|
346
|
-
def Spec.
|
347
|
-
|
348
|
-
|
349
|
-
Opt.checkRule( &rule )
|
350
|
-
rescue Opt::MissingArgument, Opt::InvalidOption => str
|
351
|
-
@@io.puts
|
352
|
-
Opt.error( str )
|
630
|
+
# Set optional footer for "usage".
|
631
|
+
def Spec.setUsageFooter( str )
|
632
|
+
Opt.main.setUsageFooter( str )
|
633
|
+
end
|
353
634
|
|
354
|
-
# Display the possible combination:
|
355
|
-
@@io.puts "\n Option combination rules:\n\n"
|
356
|
-
Opt::RuleDisplay.new.evalAndDisplay( &rule )
|
357
635
|
|
358
|
-
|
636
|
+
# Check option combination rules.
|
637
|
+
#
|
638
|
+
# @param opt [String] Opt name to which rules are set. If not
|
639
|
+
# given, Opt.current is used.
|
640
|
+
# @param rule [Proc] Rules to check.
|
641
|
+
def Spec.checkRule( opt = nil, &rule )
|
642
|
+
if opt
|
643
|
+
opt = Opt[ opt ]
|
644
|
+
else
|
645
|
+
opt = Opt.current
|
359
646
|
end
|
647
|
+
opt.setRuleCheck( &rule )
|
648
|
+
opt.checkRule
|
360
649
|
end
|
361
650
|
|
362
651
|
|
652
|
+
# Check option combination rules.
|
653
|
+
#
|
654
|
+
# @param opt [String] Opt name to which rules are set. If not
|
655
|
+
# given, Opt.current is used.
|
656
|
+
# @param rule [Proc] Rules to check.
|
657
|
+
def checkRule( opt = nil, &rule )
|
658
|
+
if opt
|
659
|
+
opt = Opt[ opt ]
|
660
|
+
else
|
661
|
+
opt = Opt.current
|
662
|
+
end
|
663
|
+
opt.setRuleCheck( &rule )
|
664
|
+
end
|
665
|
+
|
363
666
|
# Additional option check.
|
364
667
|
# @param opt [String] Option name.
|
365
668
|
# @param error [String] Error string for false return values (from check).
|
366
|
-
# @param check [Proc] Checker proc run for the option. Either
|
669
|
+
# @param check [Proc] Checker proc run for the option. Either
|
670
|
+
# @return false or generate an exception when errors found.
|
367
671
|
def Spec.checkAlso( opt, error, &check )
|
368
|
-
|
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
|
672
|
+
Opt.main.checkAlso( opt, error, &check )
|
378
673
|
end
|
379
674
|
|
675
|
+
|
676
|
+
private
|
677
|
+
|
678
|
+
def Spec.ArgCheck( cond, str )
|
679
|
+
raise( ArgumentError, str ) unless cond
|
680
|
+
end
|
681
|
+
|
682
|
+
|
380
683
|
end
|
381
684
|
|
382
685
|
|
383
686
|
|
687
|
+
|
384
688
|
# Opt includes all options spec information and parsed options
|
385
689
|
# and their values. Option instance is accessed with
|
386
690
|
# "Opt['name']". The option status (Opt instance) can be
|
387
691
|
# queried with for example "given" and "value" methods.
|
388
|
-
|
389
692
|
class Opt < ComoCommon
|
390
693
|
|
391
|
-
class Error < StandardError; end
|
392
|
-
class MissingArgument < Error; end
|
393
|
-
class InvalidOption < Error; end
|
394
694
|
|
695
|
+
# Create exception with capability to pass arbitrary data
|
696
|
+
class ErrorWithData < StandardError
|
697
|
+
attr_reader :data
|
698
|
+
def initialize( message = nil, data = nil )
|
699
|
+
super( message )
|
700
|
+
@data = data
|
701
|
+
end
|
702
|
+
end
|
395
703
|
|
396
|
-
# Option name.
|
397
|
-
attr_accessor :name
|
398
704
|
|
399
|
-
#
|
400
|
-
|
705
|
+
# Missing argument exception.
|
706
|
+
class MissingArgument < ErrorWithData; end
|
401
707
|
|
402
|
-
#
|
403
|
-
|
708
|
+
# Invalid (non-existing) option exception.
|
709
|
+
class InvalidOption < ErrorWithData; end
|
404
710
|
|
405
|
-
# Option type.
|
406
|
-
attr_accessor :type
|
407
711
|
|
408
|
-
# Option value.
|
409
|
-
attr_accessor :value
|
410
712
|
|
411
|
-
#
|
412
|
-
|
713
|
+
# ------------------------------------------------------------
|
714
|
+
# Option specification:
|
413
715
|
|
414
|
-
# Is option specified?
|
415
|
-
attr_writer :given
|
416
716
|
|
417
|
-
#
|
418
|
-
|
717
|
+
# Program i.e. highest level subcommand.
|
718
|
+
@@main = nil
|
419
719
|
|
420
|
-
#
|
720
|
+
# List of parsed option specs and option values.
|
421
721
|
@@opts = []
|
422
722
|
|
423
|
-
#
|
424
|
-
@@
|
723
|
+
# Current subcommand recorded.
|
724
|
+
@@subcmd = nil
|
725
|
+
|
726
|
+
|
727
|
+
# Set of default configs for printout.
|
728
|
+
@@config = {
|
729
|
+
:autohelp => true,
|
730
|
+
:rulehelp => false,
|
731
|
+
:header => nil,
|
732
|
+
:footer => nil,
|
733
|
+
:subcheck => true,
|
734
|
+
:check_missing => true,
|
735
|
+
:tab => 12,
|
736
|
+
:help_exit => true,
|
737
|
+
:error_exit => true,
|
738
|
+
}
|
425
739
|
|
426
740
|
|
427
|
-
#
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
741
|
+
# Set main option.
|
742
|
+
def Opt.setMain( main )
|
743
|
+
@@main = main
|
744
|
+
Opt.setSubcmd( main )
|
745
|
+
end
|
746
|
+
|
747
|
+
# Get main option.
|
748
|
+
def Opt.main
|
749
|
+
@@main
|
750
|
+
end
|
751
|
+
|
752
|
+
|
753
|
+
# Add option to options list.
|
754
|
+
def Opt.addOpt( opt )
|
755
|
+
@@opts.push opt
|
756
|
+
end
|
757
|
+
|
758
|
+
|
759
|
+
# Set current subcmd.
|
760
|
+
def Opt.setSubcmd( opt )
|
761
|
+
@@subcmd = opt
|
762
|
+
end
|
763
|
+
|
764
|
+
|
765
|
+
# Current subcmd processed.
|
766
|
+
def Opt.current
|
767
|
+
@@subcmd
|
768
|
+
end
|
769
|
+
|
770
|
+
|
771
|
+
# Find option by name.
|
772
|
+
def Opt.findOpt( name )
|
773
|
+
idx = @@opts.index do |i| i.name == name end
|
774
|
+
if idx
|
775
|
+
@@opts[ idx ]
|
776
|
+
else
|
777
|
+
nil
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
|
782
|
+
# Reset "dynamic" class members.
|
783
|
+
def Opt.reset
|
784
|
+
@@opts = []
|
785
|
+
end
|
786
|
+
|
787
|
+
|
788
|
+
# Select option object by name. Main is searched first and
|
789
|
+
# then the flattened list of all options.
|
790
|
+
def Opt.[](str)
|
791
|
+
|
792
|
+
# Search Main first.
|
793
|
+
ret = Opt.main.argByName( str )
|
794
|
+
|
795
|
+
unless ret
|
796
|
+
ret = Opt.findOpt( str )
|
797
|
+
unless ret
|
798
|
+
raise RuntimeError, "Option \"#{str}\" does not exist..."
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
ret
|
803
|
+
end
|
804
|
+
|
805
|
+
|
806
|
+
# Return program name.
|
807
|
+
def Opt.progname
|
808
|
+
@@main.name
|
809
|
+
end
|
810
|
+
|
811
|
+
|
812
|
+
# Return program year.
|
813
|
+
def Opt.year
|
814
|
+
@@main.year
|
815
|
+
end
|
816
|
+
|
817
|
+
|
818
|
+
# Return author.
|
819
|
+
def Opt.author
|
820
|
+
@@main.author
|
821
|
+
end
|
822
|
+
|
823
|
+
|
824
|
+
# Return arguments (options) that are specified as command
|
825
|
+
# external (i.e. after '--').
|
826
|
+
def Opt.external
|
827
|
+
Opt.main.external
|
828
|
+
end
|
829
|
+
|
830
|
+
|
831
|
+
# Return arguments (options) that have no switch.
|
832
|
+
def Opt.default
|
833
|
+
Opt.main.default
|
834
|
+
end
|
835
|
+
|
836
|
+
|
837
|
+
# Create option spec.
|
838
|
+
def Opt.full( name, opt, type, doc = "No doc." )
|
839
|
+
new( name, opt, type, doc )
|
840
|
+
end
|
841
|
+
|
842
|
+
# Create sub-command option spec.
|
843
|
+
def Opt.subcmd( name, doc = "No doc." )
|
844
|
+
new( name, nil, :subcmd, doc, false )
|
845
|
+
end
|
846
|
+
|
847
|
+
# Create switch option spec.
|
848
|
+
def Opt.switch( name, opt, doc = "No doc." )
|
849
|
+
new( name, opt, :switch, doc, false )
|
850
|
+
end
|
851
|
+
|
852
|
+
# Create exclusive option spec.
|
853
|
+
def Opt.exclusive( name, opt, doc = "No doc.", silent = false )
|
854
|
+
o = new( name, opt, :exclusive, doc, false )
|
855
|
+
o.silent = silent
|
856
|
+
o
|
857
|
+
end
|
858
|
+
|
859
|
+
# Create default option spec, no switch.
|
860
|
+
def Opt.defaultOpt( doc = "No doc." )
|
861
|
+
new( "<default>", "<args>", :default, doc, [] )
|
862
|
+
end
|
863
|
+
|
864
|
+
# Options iterator for all options.
|
865
|
+
def Opt.each( &blk )
|
866
|
+
Opt.main.each &blk
|
867
|
+
end
|
868
|
+
|
869
|
+
# Options iterator for given options.
|
870
|
+
def Opt.each_given( &blk )
|
871
|
+
Opt.main.each_given( &blk )
|
872
|
+
end
|
873
|
+
|
874
|
+
# Overlay Opt default configuration options.
|
875
|
+
def Opt.configOverlay( config )
|
876
|
+
@@config.merge!( config )
|
877
|
+
end
|
878
|
+
|
879
|
+
|
880
|
+
# ------------------------------------------------------------
|
881
|
+
# Opt properties:
|
882
|
+
|
883
|
+
|
884
|
+
# Subcommand parent (i.e. host).
|
885
|
+
attr_accessor :parent
|
886
|
+
|
887
|
+
|
888
|
+
# Option name.
|
889
|
+
attr_accessor :name
|
890
|
+
|
891
|
+
# Short option string.
|
892
|
+
attr_accessor :opt
|
893
|
+
|
894
|
+
# Long option string.
|
895
|
+
attr_accessor :longOpt
|
896
|
+
|
897
|
+
# Option type.
|
898
|
+
attr_accessor :type
|
899
|
+
|
900
|
+
# Option value.
|
901
|
+
attr_accessor :value
|
902
|
+
|
903
|
+
# Option documentation string.
|
904
|
+
attr_accessor :doc
|
905
|
+
|
906
|
+
# Is option specified?
|
907
|
+
attr_writer :given
|
908
|
+
|
909
|
+
# Is option hidden (usage).
|
910
|
+
attr_accessor :silent
|
911
|
+
|
912
|
+
# List of suboptions.
|
913
|
+
attr_reader :subopt
|
914
|
+
|
915
|
+
# List of subcommands.
|
916
|
+
attr_reader :subcmd
|
917
|
+
|
918
|
+
# Opt configuration.
|
919
|
+
attr_reader :config
|
920
|
+
|
921
|
+
# Opt rules.
|
922
|
+
attr_reader :rules
|
923
|
+
|
924
|
+
|
925
|
+
# Create Opt object:
|
926
|
+
# [name] Option name string.
|
927
|
+
# [opt] Switch string.
|
928
|
+
# [type] Option type. One of:
|
929
|
+
# * :switch
|
930
|
+
# * :single
|
433
931
|
# * :multi
|
434
932
|
# * :opt_single
|
435
933
|
# * :opt_multi
|
@@ -441,6 +939,7 @@ module Como
|
|
441
939
|
# [value] Default value.
|
442
940
|
|
443
941
|
def initialize( name, opt, type, doc, value = nil )
|
942
|
+
@parent = nil
|
444
943
|
@name = name
|
445
944
|
@opt = opt
|
446
945
|
@longOpt = "--#{name}"
|
@@ -450,36 +949,370 @@ module Como
|
|
450
949
|
@silent = false
|
451
950
|
# Whether option was set or not.
|
452
951
|
@given = false
|
453
|
-
|
952
|
+
@subopt = nil
|
953
|
+
@subcmd = nil
|
954
|
+
@rules = nil
|
955
|
+
|
956
|
+
@config = @@config.dup
|
957
|
+
|
958
|
+
Opt.addOpt( self )
|
959
|
+
end
|
960
|
+
|
961
|
+
|
962
|
+
# Set subcommand suboptions.
|
963
|
+
#
|
964
|
+
# @param opts [Array<Opt>]
|
965
|
+
def setSubopt( opts, subs )
|
966
|
+
opts.each do |i|
|
967
|
+
i.parent = self
|
968
|
+
end
|
969
|
+
|
970
|
+
subs.each do |i|
|
971
|
+
i.parent = self
|
972
|
+
end
|
973
|
+
|
974
|
+
@subopt = opts
|
975
|
+
@subcmd = subs
|
976
|
+
end
|
977
|
+
|
978
|
+
|
979
|
+
# Merge config to base config.
|
980
|
+
#
|
981
|
+
# @param config [Hash] Configuration Hash to merge.
|
982
|
+
def applyConfig( config )
|
983
|
+
@config.merge!( config )
|
984
|
+
end
|
985
|
+
|
986
|
+
|
987
|
+
# Set rule checks for the option.
|
988
|
+
#
|
989
|
+
# @param rule [Proc] Rule to check after command line parsing.
|
990
|
+
def setRuleCheck( &rule )
|
991
|
+
@rules = rule
|
992
|
+
end
|
993
|
+
|
994
|
+
|
995
|
+
# ------------------------------------------------------------
|
996
|
+
# Command line parsing and checking:
|
997
|
+
|
998
|
+
# Check provided args.
|
999
|
+
def check( argsState )
|
1000
|
+
|
1001
|
+
# Start at top.
|
1002
|
+
top = self
|
1003
|
+
|
1004
|
+
begin
|
1005
|
+
|
1006
|
+
# Parse and check for invalid arguments.
|
1007
|
+
begin
|
1008
|
+
top = top.parse( argsState, top.config[ :check_missing ] )
|
1009
|
+
end while( top )
|
1010
|
+
|
1011
|
+
# Check for any missing valid arguments.
|
1012
|
+
checkMissing
|
1013
|
+
|
1014
|
+
rescue Opt::MissingArgument, Opt::InvalidOption => err
|
1015
|
+
|
1016
|
+
@@io.puts
|
1017
|
+
|
1018
|
+
error( err.to_s )
|
1019
|
+
|
1020
|
+
# Display subcmd specific usage info.
|
1021
|
+
err.data.usage
|
1022
|
+
|
1023
|
+
exit( 1 ) if Opt.main.config[ :error_exit ]
|
1024
|
+
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
# Revert back to top after hierarchy travelsal.
|
1028
|
+
usageIfHelp
|
1029
|
+
|
1030
|
+
# Check rules.
|
1031
|
+
cur = self
|
1032
|
+
while cur
|
1033
|
+
cur.checkRule
|
1034
|
+
cur = cur.givenSubcmd
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
self
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
|
1041
|
+
|
1042
|
+
# Parse cmdline options from args.
|
1043
|
+
def parse( args, checkInvalids = true )
|
1044
|
+
|
1045
|
+
while args.get
|
1046
|
+
|
1047
|
+
#puts "Opt.parse (#{@name}): #{args.get}"
|
1048
|
+
|
1049
|
+
if args.isOptTerm
|
1050
|
+
|
1051
|
+
# Rest of the args do not belong to this program.
|
1052
|
+
args.next
|
1053
|
+
Opt.main.external = args.rest
|
1054
|
+
break
|
1055
|
+
|
1056
|
+
elsif args.isOpt
|
1057
|
+
|
1058
|
+
o = findOpt( args.get )
|
1059
|
+
|
1060
|
+
if !o
|
1061
|
+
if checkInvalids
|
1062
|
+
raise \
|
1063
|
+
InvalidOption.new( "Unknown option \"#{args.get}\"...",
|
1064
|
+
self )
|
1065
|
+
else
|
1066
|
+
o = findOpt( nil )
|
1067
|
+
if !o
|
1068
|
+
raise \
|
1069
|
+
InvalidOption.new(
|
1070
|
+
"No default option specified for \"#{args.get}\"...",
|
1071
|
+
self )
|
1072
|
+
else
|
1073
|
+
# Default option.
|
1074
|
+
o.value.push args.toValue
|
1075
|
+
args.next
|
1076
|
+
end
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
elsif o && o.hasArg
|
1080
|
+
|
1081
|
+
args.next
|
1082
|
+
|
1083
|
+
if ( !args.get || args.isOpt ) &&
|
1084
|
+
o.type != :opt_any
|
1085
|
+
|
1086
|
+
raise MissingArgument.new(
|
1087
|
+
"No argument given for \"#{o.opt}\"...",
|
1088
|
+
self )
|
1089
|
+
|
1090
|
+
else
|
1091
|
+
|
1092
|
+
if o.hasMany
|
1093
|
+
|
1094
|
+
# Get all argument for multi-option.
|
1095
|
+
o.value = [] if !o.given
|
1096
|
+
while args.get && !args.isOpt
|
1097
|
+
o.value.push args.toValue
|
1098
|
+
args.next
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
else
|
1102
|
+
|
1103
|
+
# Get one argument for single-option.
|
1104
|
+
|
1105
|
+
if o.given
|
1106
|
+
raise \
|
1107
|
+
InvalidOption.new(
|
1108
|
+
"Too many arguments for option (\"#{o.name}\")...",
|
1109
|
+
self )
|
1110
|
+
else
|
1111
|
+
o.value = args.toValue
|
1112
|
+
end
|
1113
|
+
args.next
|
1114
|
+
end
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
o.given = true
|
1118
|
+
|
1119
|
+
else
|
1120
|
+
|
1121
|
+
if !o
|
1122
|
+
raise InvalidOption.new( "No valid options specified...",
|
1123
|
+
self )
|
1124
|
+
else
|
1125
|
+
o.value = !o.value if !o.given
|
1126
|
+
o.given = true
|
1127
|
+
args.next
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
else
|
1132
|
+
|
1133
|
+
# Subcmd or default. Check for Subcmd first.
|
1134
|
+
|
1135
|
+
# Search for Subcmd.
|
1136
|
+
o = findOpt( args.get )
|
1137
|
+
|
1138
|
+
if !o
|
1139
|
+
|
1140
|
+
# Search for default option.
|
1141
|
+
o = findOpt( nil )
|
1142
|
+
if !o
|
1143
|
+
raise \
|
1144
|
+
InvalidOption.new(
|
1145
|
+
"No default option specified for \"#{args.get}\"...",
|
1146
|
+
self )
|
1147
|
+
else
|
1148
|
+
# Default option.
|
1149
|
+
o.given = true
|
1150
|
+
o.value.push args.toValue
|
1151
|
+
args.next
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
else
|
1155
|
+
|
1156
|
+
o.given = true
|
1157
|
+
args.next
|
1158
|
+
return o
|
1159
|
+
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
end
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
nil
|
1166
|
+
end
|
1167
|
+
|
1168
|
+
|
1169
|
+
# Check for any non-given required arguments recursively
|
1170
|
+
# through hierarchy of subcommands. MissingArgument Exception
|
1171
|
+
# is generated if argument is missing.
|
1172
|
+
def checkMissing
|
1173
|
+
|
1174
|
+
# Full cmd name.
|
1175
|
+
cmd = ( getParents.map do |i| i.name end ).join( ' ' )
|
1176
|
+
|
1177
|
+
# Check for any exclusive args first.
|
1178
|
+
@subopt.each do |o|
|
1179
|
+
if o.type == :exclusive && o.given
|
1180
|
+
return
|
1181
|
+
end
|
1182
|
+
end
|
1183
|
+
|
1184
|
+
|
1185
|
+
# Check for required arguments for this level before
|
1186
|
+
# subcmds.
|
1187
|
+
@subopt.each do |o|
|
1188
|
+
if o.isRequired
|
1189
|
+
unless o.given
|
1190
|
+
raise MissingArgument.new(
|
1191
|
+
"Option \"#{o.opt}\" missing for \"#{cmd}\"...",
|
1192
|
+
self )
|
1193
|
+
end
|
1194
|
+
end
|
1195
|
+
end
|
1196
|
+
|
1197
|
+
if hasSubcmd
|
1198
|
+
if @config[ :subcheck ]
|
1199
|
+
# Compulsory Subcommand checking enabled.
|
1200
|
+
subcmdMissing = true
|
1201
|
+
else
|
1202
|
+
subcmdMissing = false
|
1203
|
+
end
|
1204
|
+
else
|
1205
|
+
subcmdMissing = false
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
# Check for any subcmd args.
|
1209
|
+
sub = givenSubcmd
|
1210
|
+
if sub
|
1211
|
+
subcmdMissing = false
|
1212
|
+
# Use recursion to examine the next level.
|
1213
|
+
return sub.checkMissing
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
# If no subcmds are given, issue error.
|
1217
|
+
raise MissingArgument.new(
|
1218
|
+
"Subcommand required for \"#{cmd}\"...",
|
1219
|
+
self ) if subcmdMissing
|
1220
|
+
|
1221
|
+
end
|
1222
|
+
|
1223
|
+
|
1224
|
+
|
1225
|
+
# Check option combination rules.
|
1226
|
+
def checkRule
|
1227
|
+
|
1228
|
+
return unless @rules
|
1229
|
+
|
1230
|
+
begin
|
1231
|
+
raise Opt::InvalidOption.new( "Option combination mismatch!", self ) unless
|
1232
|
+
RuleCheck.check( self, &@rules )
|
1233
|
+
|
1234
|
+
rescue Opt::MissingArgument, Opt::InvalidOption => err
|
1235
|
+
@@io.puts
|
1236
|
+
error( err.to_s )
|
1237
|
+
|
1238
|
+
usage( nil, true )
|
1239
|
+
|
1240
|
+
end
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
|
1244
|
+
# Additional option check.
|
1245
|
+
# @param opt [String] Option name.
|
1246
|
+
# @param error [String] Error string for false return values (from check).
|
1247
|
+
# @param check [Proc] Checker proc run for the option. Either
|
1248
|
+
# @return false or generate an exception when errors found.
|
1249
|
+
def checkAlso( opt, error, &check )
|
1250
|
+
begin
|
1251
|
+
if self[opt].evalCheck( &check ) != true
|
1252
|
+
raise Opt::InvalidOption.new( error, self )
|
1253
|
+
end
|
1254
|
+
rescue Opt::MissingArgument, Opt::InvalidOption => err
|
1255
|
+
@@io.puts
|
1256
|
+
error( err.to_s )
|
1257
|
+
err.data.usage
|
1258
|
+
exit( 1 )
|
1259
|
+
end
|
1260
|
+
end
|
1261
|
+
|
1262
|
+
|
1263
|
+
|
1264
|
+
|
1265
|
+
# ------------------------------------------------------------
|
1266
|
+
# Opt query user interface:
|
1267
|
+
|
1268
|
+
# Select option object by name operator.
|
1269
|
+
def []( str )
|
1270
|
+
ret = argByName( str )
|
1271
|
+
unless ret
|
1272
|
+
raise RuntimeError, "Subopt \"#{str}\" does not exist for \"#{@name}\"!"
|
1273
|
+
end
|
1274
|
+
ret
|
1275
|
+
end
|
1276
|
+
|
1277
|
+
|
1278
|
+
# All subcommand options, options and subcommands.
|
1279
|
+
def suball
|
1280
|
+
@subopt + @subcmd
|
454
1281
|
end
|
455
1282
|
|
456
1283
|
|
457
1284
|
# Options list iterator.
|
458
|
-
def
|
459
|
-
|
1285
|
+
def each( &blk )
|
1286
|
+
suball.each do |o|
|
460
1287
|
yield o
|
461
1288
|
end
|
462
1289
|
end
|
463
1290
|
|
464
1291
|
|
465
1292
|
# Options iterator for given options.
|
466
|
-
def
|
467
|
-
|
1293
|
+
def each_given( &blk )
|
1294
|
+
suball.each do |o|
|
468
1295
|
yield o if o.given
|
469
1296
|
end
|
470
1297
|
end
|
471
1298
|
|
472
1299
|
|
473
1300
|
# Number of given options.
|
474
|
-
def
|
1301
|
+
def givenCount
|
475
1302
|
cnt = 0
|
476
|
-
|
1303
|
+
each_given do |i|
|
477
1304
|
cnt += 1
|
478
1305
|
end
|
479
1306
|
cnt
|
480
1307
|
end
|
481
1308
|
|
482
1309
|
|
1310
|
+
# Short syntax for value reference. Example: "~Opt['file']".
|
1311
|
+
def ~()
|
1312
|
+
@value
|
1313
|
+
end
|
1314
|
+
|
1315
|
+
|
483
1316
|
# Return option value if given otherwise the default.
|
484
1317
|
# Example usage: fileName = Opt["file"].apply( "no_name.txt" )
|
485
1318
|
def apply( default = nil )
|
@@ -494,10 +1327,17 @@ module Como
|
|
494
1327
|
# Returns true if option is given, and block is not
|
495
1328
|
# present. When block is present, the block is executed (with
|
496
1329
|
# value as parameter) if option has been given.
|
497
|
-
|
1330
|
+
#
|
1331
|
+
# @param yieldOpt [Boolean] Pass Opt to block instead of its
|
1332
|
+
# value.
|
1333
|
+
def given( yieldOpt = false, &prog )
|
498
1334
|
if block_given?
|
499
1335
|
if @given
|
500
|
-
|
1336
|
+
if yieldOpt
|
1337
|
+
yield( self )
|
1338
|
+
else
|
1339
|
+
yield( @value )
|
1340
|
+
end
|
501
1341
|
else
|
502
1342
|
false
|
503
1343
|
end
|
@@ -507,346 +1347,488 @@ module Como
|
|
507
1347
|
end
|
508
1348
|
|
509
1349
|
|
510
|
-
#
|
511
|
-
|
1350
|
+
# Alias for given.
|
1351
|
+
alias given? given
|
512
1352
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
1353
|
+
|
1354
|
+
# Return the selected subcommand.
|
1355
|
+
def givenSubcmd
|
1356
|
+
( @subcmd.select do |o| o.given end )[0]
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
|
1360
|
+
# Returns Hash of option value parameters. Example command
|
1361
|
+
# line content:
|
1362
|
+
# -p rounds=10 length=5
|
1363
|
+
# Option value content in this case would be (Array of param
|
1364
|
+
# settings):
|
1365
|
+
# [ "rounds=10", "length=5" ]
|
1366
|
+
# @return [Hash] Parameter settings included in the option.
|
1367
|
+
def params
|
1368
|
+
map = {}
|
1369
|
+
@value.each do |i|
|
1370
|
+
name, value = i.split('=')
|
1371
|
+
value = str_to_num( value )
|
1372
|
+
map[ name ] = value
|
1373
|
+
end
|
1374
|
+
map
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
|
1378
|
+
# Return default options.
|
1379
|
+
def default
|
1380
|
+
argByName( nil )
|
1381
|
+
end
|
1382
|
+
|
1383
|
+
|
1384
|
+
# Select option object by name.
|
1385
|
+
def argByName( str )
|
1386
|
+
if str == nil || str == :default
|
1387
|
+
@subopt.each do |o|
|
1388
|
+
if o.type == :default
|
1389
|
+
return o
|
1390
|
+
end
|
517
1391
|
end
|
1392
|
+
nil
|
1393
|
+
else
|
1394
|
+
suball.each do |o|
|
1395
|
+
if str == o.name
|
1396
|
+
return o
|
1397
|
+
end
|
1398
|
+
end
|
1399
|
+
nil
|
518
1400
|
end
|
1401
|
+
end
|
519
1402
|
|
520
|
-
|
521
|
-
|
522
|
-
|
1403
|
+
|
1404
|
+
# Select option object by name/opt/longOpt.
|
1405
|
+
def argById( str )
|
1406
|
+
if str == nil || str == :default
|
1407
|
+
@subopt.each do |o|
|
1408
|
+
if o.type == :default
|
1409
|
+
return o
|
1410
|
+
end
|
1411
|
+
end
|
1412
|
+
nil
|
1413
|
+
else
|
1414
|
+
suball.each do |o|
|
1415
|
+
if str == o.name || str == o.opt || str == o.longOpt
|
1416
|
+
return o
|
1417
|
+
end
|
523
1418
|
end
|
1419
|
+
nil
|
524
1420
|
end
|
525
1421
|
end
|
526
1422
|
|
527
1423
|
|
528
|
-
#
|
529
|
-
def
|
530
|
-
|
531
|
-
|
1424
|
+
# Option requires argument?
|
1425
|
+
def hasArg
|
1426
|
+
case @type
|
1427
|
+
when :single, :multi, :opt_single, :opt_multi, :opt_any; true
|
1428
|
+
else false
|
1429
|
+
end
|
532
1430
|
end
|
533
1431
|
|
534
1432
|
|
535
|
-
#
|
536
|
-
def
|
537
|
-
|
1433
|
+
# Option requires many arguments?
|
1434
|
+
def hasMany
|
1435
|
+
case @type
|
1436
|
+
when :multi, :opt_multi, :opt_any; true
|
1437
|
+
else false
|
1438
|
+
end
|
1439
|
+
end
|
1440
|
+
|
1441
|
+
|
1442
|
+
# Is mandatory argument?
|
1443
|
+
def isRequired
|
1444
|
+
case @type
|
1445
|
+
when :single, :multi; true
|
1446
|
+
else false
|
1447
|
+
end
|
1448
|
+
end
|
1449
|
+
|
1450
|
+
|
1451
|
+
# Test if option is silent.
|
1452
|
+
def silent?
|
1453
|
+
@silent
|
1454
|
+
end
|
1455
|
+
|
1456
|
+
|
1457
|
+
# Test if option is of switch type.
|
1458
|
+
def isSwitch
|
1459
|
+
case @type
|
1460
|
+
when :switch, :exclusive, :default; true
|
1461
|
+
else false
|
1462
|
+
end
|
1463
|
+
end
|
1464
|
+
|
1465
|
+
|
1466
|
+
# ------------------------------------------------------------
|
1467
|
+
# Opt "Usage Help" user interface:
|
1468
|
+
|
1469
|
+
# Set optional header for "usage".
|
1470
|
+
def setUsageHeader( str )
|
1471
|
+
@config[ :header ] = str
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
|
1475
|
+
# Set optional footer for "usage".
|
1476
|
+
def setUsageFooter( str )
|
1477
|
+
@config[ :footer ] = str
|
1478
|
+
end
|
1479
|
+
|
1480
|
+
|
1481
|
+
# Display program usage (and optionally exit).
|
1482
|
+
#
|
1483
|
+
# @param doExit [Boolean] Exit program after help
|
1484
|
+
# display. Default to help_exit config
|
1485
|
+
# if nil.
|
1486
|
+
# @param ruleHelp [Boolean] Include rule help to help
|
1487
|
+
# display. Default to rulehelp config
|
1488
|
+
# if nil.
|
1489
|
+
def usage( doExit = nil, ruleHelp = nil )
|
1490
|
+
|
1491
|
+
doExit = @config[ :help_exit ] if doExit == nil
|
1492
|
+
ruleHelp = @config[ :rulehelp ] if ruleHelp == nil
|
1493
|
+
|
1494
|
+
@@io.puts usageNormal
|
1495
|
+
|
1496
|
+
if ruleHelp
|
1497
|
+
@@io.puts "\n Option Combinations:"
|
1498
|
+
@@io.puts RuleDisplay.print( &@rules )
|
1499
|
+
end
|
1500
|
+
|
1501
|
+
exit( 1 ) if doExit
|
1502
|
+
end
|
1503
|
+
|
1504
|
+
|
1505
|
+
# Display program usage (and optionally exit).
|
1506
|
+
def usageIfHelp
|
1507
|
+
if self['help'].given
|
1508
|
+
usage
|
1509
|
+
elsif hasSubcmd && givenSubcmd
|
1510
|
+
givenSubcmd.usageIfHelp
|
1511
|
+
end
|
1512
|
+
end
|
1513
|
+
|
1514
|
+
|
1515
|
+
# Usage printout for command.
|
1516
|
+
def usageCommand
|
1517
|
+
str = ""
|
1518
|
+
str += "
|
1519
|
+
Subcommand \"#{@name}\" usage:
|
1520
|
+
#{fullCommand} #{cmdline.join(" ")}
|
1521
|
+
|
1522
|
+
"
|
1523
|
+
str += suboptDoc
|
1524
|
+
|
1525
|
+
str += "\n"
|
1526
|
+
end
|
1527
|
+
|
1528
|
+
# Usage info for Opt:s.
|
1529
|
+
def usageNormal
|
1530
|
+
str = ""
|
1531
|
+
|
1532
|
+
if @config[ :header ]
|
1533
|
+
str += "\n"
|
1534
|
+
str += @config[ :header ]
|
1535
|
+
str += "\n"
|
1536
|
+
end
|
1537
|
+
|
1538
|
+
str += usageCommand
|
1539
|
+
|
1540
|
+
if @config[ :footer ]
|
1541
|
+
str += @config[ :footer ]
|
1542
|
+
str += "\n"
|
1543
|
+
end
|
1544
|
+
|
1545
|
+
str
|
1546
|
+
end
|
1547
|
+
|
1548
|
+
|
1549
|
+
# Return cmdline usage strings for options in an Array.
|
1550
|
+
def cmdline
|
1551
|
+
opts = []
|
1552
|
+
|
1553
|
+
@subopt.each do |o|
|
1554
|
+
|
1555
|
+
next if o.silent?
|
1556
|
+
|
1557
|
+
prural = nil
|
1558
|
+
case o.type
|
1559
|
+
when :multi, :opt_multi; prural = "+"
|
1560
|
+
when :opt_any; prural = "*"
|
1561
|
+
else prural = ""
|
1562
|
+
end
|
1563
|
+
|
1564
|
+
|
1565
|
+
if !( o.isSwitch )
|
1566
|
+
name = " <#{o.name}>#{prural}"
|
1567
|
+
else
|
1568
|
+
name = ""
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
if o.opt == nil
|
1572
|
+
opt = o.longOpt
|
1573
|
+
else
|
1574
|
+
opt = o.opt
|
1575
|
+
end
|
1576
|
+
|
1577
|
+
if o.isRequired
|
1578
|
+
opts.push "#{opt}#{name}"
|
1579
|
+
else
|
1580
|
+
opts.push "[#{opt}#{name}]"
|
1581
|
+
end
|
1582
|
+
end
|
1583
|
+
|
1584
|
+
|
1585
|
+
if hasSubcmd
|
1586
|
+
opts.push "<<subcommand>>"
|
1587
|
+
end
|
1588
|
+
|
1589
|
+
opts
|
1590
|
+
|
1591
|
+
end
|
1592
|
+
|
1593
|
+
|
1594
|
+
# Return document strings for options.
|
1595
|
+
def suboptDoc
|
1596
|
+
|
1597
|
+
str = ""
|
1598
|
+
# format = Proc.new do |s,d| ( " %-#{@config[ :tab ]}s%s\n" % [ s, d ] ) end
|
1599
|
+
|
1600
|
+
str += suboptDocFormat( "Options:", "" ) if hasSubcmd && hasVisibleOptions
|
1601
|
+
|
1602
|
+
@subopt.each do |o|
|
1603
|
+
next if o.silent?
|
1604
|
+
str += suboptDocFormat( o.opt ? o.opt : o.longOpt, o.doc )
|
1605
|
+
end
|
1606
|
+
|
1607
|
+
str += "\n" + suboptDocFormat( "Subcommands:", "" ) if hasSubcmd
|
1608
|
+
|
1609
|
+
@subcmd.each do |o|
|
1610
|
+
str += suboptDocFormat( o.name, o.doc )
|
1611
|
+
end
|
1612
|
+
|
1613
|
+
str
|
538
1614
|
end
|
539
1615
|
|
540
1616
|
|
541
|
-
#
|
542
|
-
def
|
1617
|
+
# Find option object by option str.
|
1618
|
+
def findOpt( str )
|
543
1619
|
if str == nil
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
nil
|
1620
|
+
suball.detect { |i| i.type == :default }
|
1621
|
+
elsif str[0..1] == "--"
|
1622
|
+
suball.detect { |i| i.longOpt == str }
|
1623
|
+
elsif str[0..0] != "-"
|
1624
|
+
suball.detect { |i| i.name == str }
|
550
1625
|
else
|
551
|
-
|
552
|
-
if str == o.name
|
553
|
-
return o
|
554
|
-
end
|
555
|
-
end
|
556
|
-
nil
|
1626
|
+
suball.detect { |i| i.opt == str }
|
557
1627
|
end
|
558
1628
|
end
|
1629
|
+
|
559
1630
|
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
@@progname
|
564
|
-
end
|
565
|
-
|
566
|
-
# Return program year.
|
567
|
-
def Opt.year
|
568
|
-
@@year
|
569
|
-
end
|
570
|
-
|
571
|
-
# Return author.
|
572
|
-
def Opt.author
|
573
|
-
@@author
|
1631
|
+
# Como error printout.
|
1632
|
+
def error( str )
|
1633
|
+
@@io.puts "#{Opt.progname} error: #{str}"
|
574
1634
|
end
|
575
1635
|
|
1636
|
+
|
576
1637
|
|
577
|
-
# Return options that are specified as command external
|
578
|
-
# (i.e. after '--').
|
579
|
-
def Opt.external
|
580
|
-
@@external
|
581
|
-
end
|
582
1638
|
|
583
|
-
#
|
584
|
-
|
585
|
-
doc = []
|
586
|
-
@@opts.each do |o|
|
587
|
-
next if o.silent?
|
588
|
-
doc.push( [o.opt ? o.opt : o.longOpt, o.doc] )
|
589
|
-
end
|
590
|
-
doc
|
591
|
-
end
|
1639
|
+
# ------------------------------------------------------------
|
1640
|
+
# Internal methods:
|
592
1641
|
|
1642
|
+
private
|
593
1643
|
|
594
1644
|
|
595
|
-
#
|
596
|
-
#
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
# incr( "gcov", "exclude", "refreshed" ),
|
603
|
-
# "manifest",
|
604
|
-
# "pairs",
|
605
|
-
# "files"
|
606
|
-
# )
|
607
|
-
# end
|
608
|
-
class RuleCheck
|
609
|
-
|
610
|
-
# Get given count.
|
611
|
-
def getScore( *args )
|
612
|
-
score = 0
|
613
|
-
args.each do |i|
|
614
|
-
if i.class == TrueClass || i.class == FalseClass
|
615
|
-
score += 1 if i
|
616
|
-
else
|
617
|
-
score += 1 if Opt[i].given
|
618
|
-
end
|
619
|
-
end
|
620
|
-
score
|
621
|
-
end
|
1645
|
+
# Convert string to number if possible. The order of checks:
|
1646
|
+
# integer, float, and no-conversion.
|
1647
|
+
def str_to_num( str )
|
1648
|
+
begin return Integer( str ); rescue ; end
|
1649
|
+
begin return Float( str ); rescue ; end
|
1650
|
+
str
|
1651
|
+
end
|
622
1652
|
|
623
|
-
# Special condition when no options are given.
|
624
|
-
def none
|
625
|
-
Opt.givenCount == 0
|
626
|
-
end
|
627
1653
|
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
score = getScore( args[i] )
|
637
|
-
had += 1 if ( score == 1 ) && add
|
638
|
-
add = false if ( score == 0 )
|
639
|
-
scoreAll += score
|
640
|
-
i += 1
|
641
|
-
end
|
1654
|
+
# Return list of parents.
|
1655
|
+
def getParents
|
1656
|
+
list = []
|
1657
|
+
cur = self
|
1658
|
+
begin
|
1659
|
+
list.push cur
|
1660
|
+
cur = cur.parent
|
1661
|
+
end while cur
|
642
1662
|
|
643
|
-
|
644
|
-
|
1663
|
+
list.reverse
|
1664
|
+
end
|
645
1665
|
|
646
|
-
# Incremental options in order i.e. have to have all later
|
647
|
-
# if had first.
|
648
|
-
def follow( *args )
|
649
|
-
if getScore( args[0] )
|
650
|
-
getScore( *args ) == args.length
|
651
|
-
else
|
652
|
-
true
|
653
|
-
end
|
654
|
-
end
|
655
1666
|
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
1667
|
+
# Full command name.
|
1668
|
+
def fullCommand
|
1669
|
+
( getParents.map do |i| i.name end ).join( ' ' )
|
1670
|
+
end
|
660
1671
|
|
661
|
-
# At least one is given.
|
662
|
-
def any( *args )
|
663
|
-
getScore( *args ) > 0
|
664
|
-
end
|
665
1672
|
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
end
|
1673
|
+
# Test if option is of subcmd type.
|
1674
|
+
def isSubcmd
|
1675
|
+
@type == :subcmd
|
670
1676
|
end
|
671
1677
|
|
672
1678
|
|
1679
|
+
# Test if option is of subcmd type.
|
1680
|
+
def hasVisibleOptions
|
1681
|
+
# Count non-silent options.
|
1682
|
+
( @subopt.select do |i| !i.silent? end ).length > 0
|
1683
|
+
end
|
673
1684
|
|
674
|
-
# Display utility for RuleCheck. Same usage model.
|
675
|
-
#
|
676
|
-
# Example expansion of options:
|
677
|
-
#
|
678
|
-
# |--# One of:
|
679
|
-
# | |--# Adding in order:
|
680
|
-
# | | |--<gcov>
|
681
|
-
# | | |--<exclude>
|
682
|
-
# | | |--<refreshed>
|
683
|
-
# | |--<manifest>
|
684
|
-
# | |--<pairs>
|
685
|
-
# | |--<files>
|
686
|
-
#
|
687
|
-
class RuleDisplay < ComoCommon
|
688
1685
|
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
end
|
1686
|
+
# Test if option is of subcmd type.
|
1687
|
+
def hasSubcmd
|
1688
|
+
!@subcmd.empty?
|
1689
|
+
end
|
694
1690
|
|
695
|
-
# Eval rules to get an nested array and then display it.
|
696
|
-
def evalAndDisplay( &rule )
|
697
|
-
printRule( instance_eval( &rule ) )
|
698
|
-
end
|
699
1691
|
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
1692
|
+
# Custom check for the option. User has to know some Como
|
1693
|
+
# internals.
|
1694
|
+
def evalCheck( &check )
|
1695
|
+
instance_eval &check
|
1696
|
+
end
|
704
1697
|
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
else
|
710
|
-
cnt = item
|
711
|
-
end
|
712
|
-
@prefixStr = @prefixStr[0..-(cnt+1)]
|
713
|
-
end
|
1698
|
+
# Format option documentation. If newline is followed by tab,
|
1699
|
+
# rest of the documentation is aligned with the description of
|
1700
|
+
# previous line.
|
1701
|
+
def suboptDocFormat( switch, doc )
|
714
1702
|
|
715
|
-
#
|
716
|
-
|
717
|
-
@@io.puts( @prefixStr + str )
|
718
|
-
end
|
1703
|
+
# Format doc nicely to multiple lines if newline is
|
1704
|
+
# followed by tab stop.
|
719
1705
|
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
addPrefix( item )
|
1706
|
+
parts = doc.split( "\n" )
|
1707
|
+
lines = [ ( " %-#{@config[ :tab ]}s%s\n" % [ switch, parts[0] ] ) ]
|
1708
|
+
|
1709
|
+
if parts[1]
|
1710
|
+
parts[1..-1].each do |p|
|
726
1711
|
|
727
|
-
|
728
|
-
|
729
|
-
printRule( i )
|
1712
|
+
if p[0] == "\t"
|
1713
|
+
lines.push ( " #{' '*@config[ :tab ]}%s\n" % [ p[1..-1] ] )
|
730
1714
|
else
|
731
|
-
p
|
1715
|
+
lines.push p
|
732
1716
|
end
|
1717
|
+
|
733
1718
|
end
|
734
|
-
rmPrefix( item )
|
735
1719
|
end
|
736
1720
|
|
737
|
-
|
738
|
-
|
739
|
-
[ "NONE" ]
|
740
|
-
end
|
1721
|
+
lines.join
|
1722
|
+
end
|
741
1723
|
|
742
|
-
|
743
|
-
# to have later.
|
744
|
-
def incr( *args )
|
745
|
-
[ "Adding in order", *args ]
|
746
|
-
end
|
1724
|
+
end
|
747
1725
|
|
748
|
-
# Incremental options in order i.e. have to have all later
|
749
|
-
# if had first.
|
750
|
-
def follow( *args )
|
751
|
-
[ "If first then rest", *args ]
|
752
|
-
end
|
753
1726
|
|
754
|
-
# One of list given.
|
755
|
-
def one( *args )
|
756
|
-
[ "One of", *args ]
|
757
|
-
end
|
758
1727
|
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
end
|
1728
|
+
# Specialized Opt class for program (i.e. highest level
|
1729
|
+
# subcommand).
|
1730
|
+
class MainOpt < Opt
|
763
1731
|
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
1732
|
+
# Program external arguments:
|
1733
|
+
attr_accessor :external
|
1734
|
+
|
1735
|
+
# Program author and year (date).
|
1736
|
+
attr_reader :author, :year
|
1737
|
+
|
1738
|
+
|
1739
|
+
def initialize( author, year,
|
1740
|
+
name, opt, type, doc, value = nil )
|
1741
|
+
@author = author
|
1742
|
+
@year = year
|
1743
|
+
@external = nil
|
1744
|
+
super( name, opt, type, doc, value = nil )
|
768
1745
|
|
769
1746
|
end
|
1747
|
+
|
770
1748
|
|
1749
|
+
# Full command name.
|
1750
|
+
def fullCommand
|
1751
|
+
Opt.progname
|
1752
|
+
end
|
771
1753
|
|
772
1754
|
|
773
|
-
#
|
774
|
-
def
|
775
|
-
|
776
|
-
|
1755
|
+
# Usage printout for command.
|
1756
|
+
def usageCommand
|
1757
|
+
str = "
|
1758
|
+
Usage:
|
1759
|
+
#{fullCommand} #{cmdline.join(" ")}
|
777
1760
|
|
1761
|
+
"
|
1762
|
+
str += suboptDoc
|
778
1763
|
|
779
|
-
|
780
|
-
def Opt.full( name, opt, type, doc = "No doc." )
|
781
|
-
new( name, opt, type, doc )
|
782
|
-
end
|
1764
|
+
str += "
|
783
1765
|
|
784
|
-
|
785
|
-
def Opt.switch( name, opt, doc = "No doc." )
|
786
|
-
new( name, opt, :switch, doc, false )
|
787
|
-
end
|
1766
|
+
Copyright (c) #{Opt.year} by #{Opt.author}
|
788
1767
|
|
789
|
-
|
790
|
-
def Opt.exclusive( name, opt, doc = "No doc.", silent = false )
|
791
|
-
new( name, opt, :exclusive, doc, false )
|
792
|
-
@@opts[-1].silent = silent
|
1768
|
+
"
|
793
1769
|
end
|
794
1770
|
|
795
|
-
|
796
|
-
|
797
|
-
|
1771
|
+
end
|
1772
|
+
|
1773
|
+
|
1774
|
+
# Command argument parsing state.
|
1775
|
+
class ArgsParseState
|
1776
|
+
|
1777
|
+
# @param list [Array<String>] List of Command Line Arguments
|
1778
|
+
# (default: ARGV).
|
1779
|
+
def initialize( list )
|
1780
|
+
set( list )
|
1781
|
+
@idx = 0
|
798
1782
|
end
|
799
1783
|
|
1784
|
+
# Set list of arguments.
|
1785
|
+
def set( list )
|
1786
|
+
@args = list
|
1787
|
+
end
|
800
1788
|
|
801
|
-
#
|
802
|
-
def
|
1789
|
+
# Step to next argument.
|
1790
|
+
def next
|
1791
|
+
@idx += 1
|
1792
|
+
end
|
803
1793
|
|
804
|
-
|
805
|
-
|
806
|
-
|
807
|
-
|
808
|
-
check.call( year.class == String, "Year is not a String" )
|
809
|
-
check.call( table.class == Array, "Option table is not an Array" )
|
810
|
-
table.each do |i|
|
811
|
-
check.call( i.class == Array, "Option table entry is not an Array" )
|
812
|
-
check.call( i.length == 4, "Option table entry length not 4" )
|
813
|
-
end
|
1794
|
+
# Step to previous argument.
|
1795
|
+
def prev
|
1796
|
+
@idx -= 1
|
1797
|
+
end
|
814
1798
|
|
1799
|
+
# Get current argument.
|
1800
|
+
def get( idx = @idx )
|
1801
|
+
@args[ idx ]
|
1802
|
+
end
|
815
1803
|
|
816
|
-
|
817
|
-
|
818
|
-
|
1804
|
+
# Get last argument.
|
1805
|
+
def last( idx = @idx )
|
1806
|
+
idx == ( @args.length-1 )
|
1807
|
+
end
|
819
1808
|
|
820
|
-
|
1809
|
+
# Get rest of the arguments.
|
1810
|
+
def rest( idx = @idx )
|
1811
|
+
@args[ idx..-1 ]
|
1812
|
+
end
|
821
1813
|
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
when :exclusive
|
826
|
-
Opt.exclusive( e[1], e[2], e[3] )
|
827
|
-
when :silent
|
828
|
-
Opt.exclusive( e[1], e[2], e[3], true )
|
829
|
-
when :single, :multi, :opt_single, :opt_multi, :opt_any
|
830
|
-
Opt.full( e[1], e[2], e[0], e[3] )
|
831
|
-
when :default
|
832
|
-
Opt.default( e[3] )
|
833
|
-
end
|
834
|
-
end
|
1814
|
+
# Parser at argument list end?
|
1815
|
+
def done?
|
1816
|
+
@idx >= @list.length
|
835
1817
|
end
|
836
1818
|
|
837
1819
|
|
838
1820
|
# Test whether str is an option.
|
839
|
-
def
|
1821
|
+
def isOpt( str = get )
|
840
1822
|
str[0..0] == "-"
|
841
1823
|
end
|
842
1824
|
|
843
1825
|
# Test whether str is an option list terminator.
|
844
|
-
def
|
1826
|
+
def isOptTerm( str = get )
|
845
1827
|
str == "--"
|
846
1828
|
end
|
847
1829
|
|
848
1830
|
# Format value string if escaped.
|
849
|
-
def
|
1831
|
+
def toValue( str = get )
|
850
1832
|
if str[0..0] == "\\"
|
851
1833
|
str[1..-1]
|
852
1834
|
else
|
@@ -854,204 +1836,221 @@ module Como
|
|
854
1836
|
end
|
855
1837
|
end
|
856
1838
|
|
857
|
-
|
858
|
-
def hasArg
|
859
|
-
case @type
|
860
|
-
when :single, :multi, :opt_single, :opt_multi, :opt_any; true
|
861
|
-
else false
|
862
|
-
end
|
863
|
-
end
|
1839
|
+
end
|
864
1840
|
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
1841
|
+
|
1842
|
+
|
1843
|
+
# Set of methods which represent option combination checking.
|
1844
|
+
# In effect this is a meta language (DSL) for option
|
1845
|
+
# combinations.
|
1846
|
+
#
|
1847
|
+
# Example:
|
1848
|
+
# RuleCheck.check( opt ) do
|
1849
|
+
# one(
|
1850
|
+
# incr( "gcov", "exclude", "refreshed" ),
|
1851
|
+
# "manifest",
|
1852
|
+
# "pairs",
|
1853
|
+
# "files"
|
1854
|
+
# )
|
1855
|
+
# end
|
1856
|
+
class RuleCheck
|
1857
|
+
|
1858
|
+
def RuleCheck.check( opt, &rule )
|
1859
|
+
rc = RuleCheck.new( opt )
|
1860
|
+
rc.instance_eval( &rule )
|
871
1861
|
end
|
872
1862
|
|
873
|
-
|
874
|
-
|
875
|
-
case @type
|
876
|
-
when :single, :multi; true
|
877
|
-
else false
|
878
|
-
end
|
1863
|
+
def initialize( opt, &rule )
|
1864
|
+
@opt = opt
|
879
1865
|
end
|
880
1866
|
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
1867
|
+
|
1868
|
+
# Get given count.
|
1869
|
+
def getScore( *args )
|
1870
|
+
score = 0
|
1871
|
+
args.each do |i|
|
1872
|
+
if i.class == TrueClass || i.class == FalseClass
|
1873
|
+
score += 1 if i
|
1874
|
+
else
|
1875
|
+
score += 1 if @opt.argById(i).given
|
1876
|
+
end
|
886
1877
|
end
|
1878
|
+
score
|
887
1879
|
end
|
888
1880
|
|
889
|
-
#
|
890
|
-
def
|
891
|
-
@
|
1881
|
+
# Special condition when no options are given.
|
1882
|
+
def none
|
1883
|
+
@opt.givenCount == 0
|
892
1884
|
end
|
893
1885
|
|
894
|
-
#
|
895
|
-
#
|
896
|
-
def
|
897
|
-
|
898
|
-
|
1886
|
+
# Incremental options in order i.e. have to have previous
|
1887
|
+
# to have later.
|
1888
|
+
def incr( *args )
|
1889
|
+
had = 0
|
1890
|
+
add = true
|
1891
|
+
scoreAll = 0
|
1892
|
+
i = 0
|
1893
|
+
while args[i]
|
1894
|
+
score = getScore( args[i] )
|
1895
|
+
had += 1 if ( score == 1 ) && add
|
1896
|
+
add = false if ( score == 0 )
|
1897
|
+
scoreAll += score
|
1898
|
+
i += 1
|
1899
|
+
end
|
899
1900
|
|
1901
|
+
( had == scoreAll && had > 0 )
|
1902
|
+
end
|
900
1903
|
|
901
|
-
#
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
( @@opts.select { |i| i.longOpt == str } )[0]
|
1904
|
+
# Incremental options in order i.e. have to have all later
|
1905
|
+
# if had first.
|
1906
|
+
def follow( *args )
|
1907
|
+
if getScore( args[0] )
|
1908
|
+
getScore( *args ) == args.length
|
907
1909
|
else
|
908
|
-
|
1910
|
+
true
|
909
1911
|
end
|
910
1912
|
end
|
911
|
-
|
912
|
-
# Como error printout.
|
913
|
-
def Opt.error( str )
|
914
|
-
@@io.puts "#{@@progname} error: #{str}"
|
915
|
-
end
|
916
1913
|
|
1914
|
+
# One of list given.
|
1915
|
+
def one( *args )
|
1916
|
+
getScore( *args ) == 1
|
1917
|
+
end
|
917
1918
|
|
918
|
-
#
|
919
|
-
def
|
920
|
-
|
921
|
-
|
922
|
-
while args[ind]
|
923
|
-
|
924
|
-
if Opt.isOptTerm( args[ind] )
|
925
|
-
|
926
|
-
# Rest of the args do not belong to this program.
|
927
|
-
ind += 1
|
928
|
-
@@external = args[ind..-1]
|
929
|
-
break
|
930
|
-
|
931
|
-
elsif Opt.isOpt( args[ind] )
|
932
|
-
|
933
|
-
o = Opt.findOpt( args[ind] )
|
934
|
-
|
935
|
-
if !o
|
936
|
-
if checkInvalids
|
937
|
-
raise InvalidOption, \
|
938
|
-
"Unknown option (\"#{args[ind]}\")..."
|
939
|
-
else
|
940
|
-
o = Opt.findOpt( nil )
|
941
|
-
if !o
|
942
|
-
raise InvalidOption, "No default option specified for \"#{args[ind]}\"..."
|
943
|
-
else
|
944
|
-
# Default option.
|
945
|
-
o.value.push Opt.toValue( args[ind] )
|
946
|
-
ind += 1
|
947
|
-
end
|
948
|
-
end
|
1919
|
+
# At least one is given.
|
1920
|
+
def any( *args )
|
1921
|
+
getScore( *args ) > 0
|
1922
|
+
end
|
949
1923
|
|
950
|
-
|
1924
|
+
# All are given.
|
1925
|
+
def all( *args )
|
1926
|
+
getScore( *args ) == args.length
|
1927
|
+
end
|
951
1928
|
|
952
|
-
|
1929
|
+
# Logical inversion.
|
1930
|
+
def inv( *args )
|
1931
|
+
getScore( *args ) == 0
|
1932
|
+
end
|
953
1933
|
|
954
|
-
|
955
|
-
|
1934
|
+
# Dont care.
|
1935
|
+
def meh( *args )
|
1936
|
+
true
|
1937
|
+
end
|
1938
|
+
end
|
956
1939
|
|
957
|
-
raise MissingArgument, \
|
958
|
-
"No argument given for \"#{o.opt}\"..."
|
959
1940
|
|
960
|
-
else
|
961
1941
|
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
1942
|
+
# Display utility for RuleCheck. Usage model.
|
1943
|
+
#
|
1944
|
+
# RuleDisplay.new.evalAndDisplay( &rule )
|
1945
|
+
#
|
1946
|
+
# Example expansion of options:
|
1947
|
+
#
|
1948
|
+
# |--# One of:
|
1949
|
+
# | |--# Adding in order:
|
1950
|
+
# | | |--<gcov>
|
1951
|
+
# | | |--<exclude>
|
1952
|
+
# | | |--<refreshed>
|
1953
|
+
# | |--<manifest>
|
1954
|
+
# | |--<pairs>
|
1955
|
+
# | |--<files>
|
1956
|
+
#
|
1957
|
+
class RuleDisplay < ComoCommon
|
1958
|
+
|
1959
|
+
# Eval rules to get an nested array and then display it.
|
1960
|
+
def RuleDisplay.print( prefixStr = " ", &rule )
|
1961
|
+
rd = RuleDisplay.new( prefixStr )
|
1962
|
+
rd.evalAndDisplay( &rule )
|
1963
|
+
end
|
971
1964
|
|
972
|
-
|
1965
|
+
def initialize( prefixStr )
|
1966
|
+
# Prefix string for lines. Rules add/rm from it.
|
1967
|
+
@prefixStr = prefixStr
|
1968
|
+
end
|
973
1969
|
|
974
|
-
|
1970
|
+
def evalAndDisplay( &rule )
|
1971
|
+
printRule( instance_eval( &rule ) )
|
1972
|
+
end
|
975
1973
|
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
o.value = Opt.toValue( args[ind] )
|
981
|
-
end
|
982
|
-
ind += 1
|
983
|
-
end
|
984
|
-
end
|
1974
|
+
# Increase prefix string.
|
1975
|
+
def addPrefix( str )
|
1976
|
+
@prefixStr += str
|
1977
|
+
end
|
985
1978
|
|
986
|
-
|
1979
|
+
# Remove from prefix (either str or length ).
|
1980
|
+
def rmPrefix( item )
|
1981
|
+
if item.class == String
|
1982
|
+
cnt = item.length
|
1983
|
+
else
|
1984
|
+
cnt = item
|
1985
|
+
end
|
1986
|
+
@prefixStr = @prefixStr[0..-(cnt+1)]
|
1987
|
+
end
|
987
1988
|
|
988
|
-
|
1989
|
+
# Print prefix + str.
|
1990
|
+
def p( str )
|
1991
|
+
@@io.puts( @prefixStr + str )
|
1992
|
+
end
|
989
1993
|
|
990
|
-
|
991
|
-
|
992
|
-
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
end
|
997
|
-
end
|
1994
|
+
# Recursively go through the nested array of rule items and
|
1995
|
+
# print out rules.
|
1996
|
+
def printRule( arr )
|
1997
|
+
p( "|--# #{arr[0]}:" )
|
1998
|
+
item = "| "
|
1999
|
+
addPrefix( item )
|
998
2000
|
|
2001
|
+
arr[1..-1].each do |i|
|
2002
|
+
if i.class == Array
|
2003
|
+
printRule( i )
|
999
2004
|
else
|
1000
|
-
|
1001
|
-
# Default option.
|
1002
|
-
|
1003
|
-
o = Opt.findOpt( nil )
|
1004
|
-
|
1005
|
-
if !o
|
1006
|
-
raise InvalidOption, "No default option specified for \"#{args[ind]}\"..."
|
1007
|
-
else
|
1008
|
-
while ( args[ind] && !Opt.isOpt( args[ind] ) )
|
1009
|
-
o.value.push Opt.toValue( args[ind] )
|
1010
|
-
ind += 1
|
1011
|
-
end
|
1012
|
-
end
|
2005
|
+
p( "|--<#{i}>" )
|
1013
2006
|
end
|
1014
2007
|
end
|
2008
|
+
rmPrefix( item )
|
1015
2009
|
end
|
1016
2010
|
|
2011
|
+
# Special condition where no arguments are given.
|
2012
|
+
def none
|
2013
|
+
[ "NONE" ]
|
2014
|
+
end
|
1017
2015
|
|
1018
|
-
#
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
2016
|
+
# Incremental options in order i.e. have to have previous
|
2017
|
+
# to have later.
|
2018
|
+
def incr( *args )
|
2019
|
+
[ "Adding in order", *args ]
|
2020
|
+
end
|
1023
2021
|
|
1024
|
-
|
2022
|
+
# Incremental options in order i.e. have to have all later
|
2023
|
+
# if had first.
|
2024
|
+
def follow( *args )
|
2025
|
+
[ "If first then rest", *args ]
|
2026
|
+
end
|
1025
2027
|
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
else prural = ""
|
1031
|
-
end
|
2028
|
+
# One of list given.
|
2029
|
+
def one( *args )
|
2030
|
+
[ "One of", *args ]
|
2031
|
+
end
|
1032
2032
|
|
1033
|
-
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1037
|
-
end
|
2033
|
+
# At least one is given.
|
2034
|
+
def any( *args )
|
2035
|
+
[ "One or more of", *args ]
|
2036
|
+
end
|
1038
2037
|
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
end
|
2038
|
+
# All are given.
|
2039
|
+
def all( *args )
|
2040
|
+
[ "All of", *args ]
|
2041
|
+
end
|
1044
2042
|
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
end
|
1050
|
-
end
|
2043
|
+
# Logical inversion.
|
2044
|
+
def inv( *args )
|
2045
|
+
[ "Not", *args ]
|
2046
|
+
end
|
1051
2047
|
|
1052
|
-
|
2048
|
+
# Dont care.
|
2049
|
+
def meh( *args )
|
2050
|
+
[ "Ignoring", *args ]
|
1053
2051
|
end
|
1054
2052
|
|
1055
2053
|
end
|
1056
2054
|
|
2055
|
+
|
1057
2056
|
end
|