1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2008 Floréal Morandat <morandat@lirmm.fr>
4 # Copyright 2008 Jean Privat <jean@pryen.org>
6 # This file is free software, which comes along with NIT. This software is
7 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
9 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
10 # is kept unaltered, and a notification of the changes is added.
11 # You are allowed to redistribute it and sell it, alone or is a part of
14 # Management of options on the command line
17 # Super class of all option's class
19 # Names for the option (including long and short ones)
20 var names
: Array[String]
22 # Type of the value of the option
23 type VALUE: nullable Object
25 # Human readable description of the option
28 # Gathering errors during parsing
29 var errors
: Array[String] = new Array[String]
31 # Is this option mandatory?
32 var mandatory
: Bool = false is writable
34 # Is this option hidden from `usage`?
35 var hidden
: Bool = false is writable
37 # Has this option been read?
38 var read
: Bool = false is writable
40 # Current value of this option
41 var value
: VALUE is writable
43 # Default value of this option
44 var default_value
: VALUE is writable
47 init(help
: String, default
: VALUE, names
: nullable Array[String]) is old_style_init
do
48 init_opt
(help
, default
, names
)
51 # Init option `helptext`, `default_value` and `names`.
53 # Also set current `value` to `default`.
54 fun init_opt
(help
: String, default
: VALUE, names
: nullable Array[String])
57 self.names
= new Array[String]
59 self.names
= names
.to_a
62 default_value
= default
66 # Add new aliases for this option
67 fun add_aliases
(names
: String...) do names
.add_all
(names
)
69 # An help text for this option with default settings
70 redef fun to_s
do return pretty
(2)
72 # A pretty print for this help
73 fun pretty
(off
: Int): String
75 var text
= new FlatBuffer.from
(" ")
76 text
.append
(names
.join
(", "))
78 var rest
= off
- text
.length
79 if rest
> 0 then text
.append
(" " * rest
)
81 #text.append(pretty_default)
85 # Pretty print the default value.
86 fun pretty_default
: String
88 var dv
= default_value
89 if dv
!= null then return " ({dv.to_s})"
93 # Consume parameters for this option
94 protected fun read_param
(opts
: OptionContext, it
: Iterator[String])
100 # Not really an option. Just add a line of text when displaying the usage
104 # Init a new OptionText with `text`.
105 init(text
: String) is old_style_init
do super(text
, null, null)
107 redef fun pretty
(off
) do return to_s
109 redef fun to_s
do return helptext
112 # A boolean option, `true` when present, `false` if not
115 redef type VALUE: Bool
117 # Init a new OptionBool with a `help` message and `names`.
118 init(help
: String, names
: String...) is old_style_init
do super(help
, false, names
)
120 redef fun read_param
(opts
, it
)
127 # A count option. Count the number of time this option is present
130 redef type VALUE: Int
132 # Init a new OptionCount with a `help` message and `names`.
133 init(help
: String, names
: String...) is old_style_init
do super(help
, 0, names
)
135 redef fun read_param
(opts
, it
)
142 # Option with one parameter (mandatory by default)
143 abstract class OptionParameter
146 # Convert `str` to a value of type `VALUE`.
147 protected fun convert
(str
: String): VALUE is abstract
149 # Is the parameter mandatory?
150 var parameter_mandatory
= true is writable
152 redef fun read_param
(opts
, it
)
157 if ok
and not parameter_mandatory
and not it
.item
.is_empty
and it
.item
.chars
.first
== '-' then
158 # The next item may looks like a known command
159 # Only check if `not parameter_mandatory`
160 for opt
in opts
.options
do
161 if opt
.names
.has
(it
.item
) then
162 # The next item is a known command
170 value
= convert
(it
.item
)
173 errors
.add
("Parameter expected for option {names.first}.")
178 # An option with a `String` as parameter
180 super OptionParameter
181 redef type VALUE: nullable String
183 # Init a new OptionString with a `help` message and `names`.
184 init(help
: String, names
: String...) is old_style_init
do super(help
, null, names
)
186 redef fun convert
(str
) do return str
189 # An option to choose from an enumeration
191 # Declare an enumeration option with all its possible values as an array.
192 # Once the arguments are processed, `value` is set as the index of the selected value, if any.
194 super OptionParameter
195 redef type VALUE: Int
197 # Values in the enumeration.
198 var values
: Array[String]
200 # Init a new OptionEnum from `values` with a `help` message and `names`.
202 # `default` is the index of the default value in `values`.
203 init(values
: Array[String], help
: String, default
: Int, names
: String...) is old_style_init
do
204 assert values
.length
> 0
205 self.values
= values
.to_a
206 super("{help} <{values.join(", ")}>", default
, names
)
209 redef fun convert
(str
)
211 var id
= values
.index_of
(str
)
213 var e
= "Unrecognized value for option {names.join(", ")}.\n"
214 e
+= "Expected values are: {values.join(", ")}."
220 # Get the value name from `values`.
221 fun value_name
: String do return values
[value
]
223 redef fun pretty_default
225 return " ({values[default_value]})"
229 # An option with an Int as parameter
231 super OptionParameter
232 redef type VALUE: Int
234 # Init a new OptionInt with a `help` message, a `default` value and `names`.
235 init(help
: String, default
: Int, names
: String...) is old_style_init
do
236 super(help
, default
, names
)
239 redef fun convert
(str
) do return str
.to_i
242 # An option with a Float as parameter
244 super OptionParameter
245 redef type VALUE: Float
247 # Init a new OptionFloat with a `help` message, a `default` value and `names`.
248 init(help
: String, default
: Float, names
: String...) is old_style_init
do
249 super(help
, default
, names
)
252 redef fun convert
(str
) do return str
.to_f
255 # An option with an array as parameter
256 # `myprog -optA arg1 -optA arg2` is giving an Array `["arg1", "arg2"]`
258 super OptionParameter
259 redef type VALUE: Array[String]
261 # Init a new OptionArray with a `help` message and `names`.
262 init(help
: String, names
: String...) is old_style_init
do
263 values
= new Array[String]
264 super(help
, values
, names
)
267 private var values
: Array[String]
268 redef fun convert
(str
)
275 # Context where the options process
277 # Options present in the context
278 var options
= new Array[Option]
280 # Rest of the options after `parse` is called
281 var rest
= new Array[String]
283 # Errors found in the context after parsing
284 var context_errors
= new Array[String]
286 private var optmap
= new HashMap[String, Option]
288 # Add one or more options to the context
289 fun add_option
(opts
: Option...) do
290 options
.add_all
(opts
)
293 # Display all the options available
302 if lmax
< l
then lmax
= l
307 print
(i
.pretty
(lmax
))
312 # Parse and assign options in `argv` or `args`
313 fun parse
(argv
: nullable Collection[String])
315 if argv
== null then argv
= args
316 var it
= argv
.iterator
320 # Must all option be given before the first argument?
322 # When set to `false` (the default), options of the command line are
323 # all parsed until the end of the list of arguments or until "--" is met (in this case "--" is discarded).
325 # When set to `true` options are parsed until the first non-option is met.
326 var options_before_rest
= false is writable
328 # Parse the command line
329 protected fun parse_intern
(it
: Iterator[String])
335 while parseargs
and it
.is_ok
do
339 rest
.add_all
(it
.to_a
)
342 # We're looking for packed short options
343 if str
.chars
.last_index_of
('-') == 0 and str
.length
> 2 then
344 var next_called
= false
345 for i
in [1..str
.length
[ do
346 var short_opt
= "-" + str
.chars
[i
].to_s
347 if optmap
.has_key
(short_opt
) then
348 var option
= optmap
[short_opt
]
349 if option
isa OptionParameter then
353 option
.read_param
(self, it
)
356 if not next_called
then it
.next
358 if optmap
.has_key
(str
) then
359 var opt
= optmap
[str
]
361 opt
.read_param
(self, it
)
365 if options_before_rest
then
366 rest
.add_all
(it
.to_a
)
374 for opt
in options
do
375 if opt
.mandatory
and not opt
.read
then
376 context_errors
.add
("Mandatory option {opt.names.join(", ")} not found.")
390 # Options parsing errors.
391 fun errors
: Array[String]
393 var errors
= new Array[String]
394 errors
.add_all context_errors