como 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|