lib/getoptlong.rb
DEFINITIONS
This source file includes following functions.
1 # -*- Ruby -*-
2 # Copyright (C) 1998, 1999, 2000 Motoyuki Kasahara
3 #
4 # You may redistribute it and/or modify it under the same license
5 # terms as Ruby.
6 #
7
8 #
9 # Documents and latest version of `getoptlong.rb' are found at:
10 # http:
11 #
12
13 #
14 # Parse command line options just like GNU getopt_long().
15 #
16 class GetoptLong
17 #
18 # Orderings.
19 #
20 ORDERINGS = [REQUIRE_ORDER = 0, PERMUTE = 1, RETURN_IN_ORDER = 2]
21
22 #
23 # Argument flags.
24 #
25 ARGUMENT_FLAGS = [NO_ARGUMENT = 0, REQUIRED_ARGUMENT = 1,
26 OPTIONAL_ARGUMENT = 2]
27
28 #
29 # Status codes.
30 #
31 STATUS_YET, STATUS_STARTED, STATUS_TERMINATED = 0, 1, 2
32
33 #
34 # Error types.
35 #
36 class AmbigousOption < StandardError; end
37 class NeedlessArgument < StandardError; end
38 class MissingArgument < StandardError; end
39 class InvalidOption < StandardError; end
40
41 #
42 # Initializer.
43 #
44 def initialize(*arguments)
45 #
46 # Current ordering.
47 #
48 if ENV.include?('POSIXLY_CORRECT')
49 @ordering = REQUIRE_ORDER
50 else
51 @ordering = PERMUTE
52 end
53
54 #
55 # Hash table of option names.
56 # Keyes of the table are option names, and their values are canonical
57 # names of the options.
58 #
59 @canonical_names = Hash.new
60
61 #
62 # Hash table of argument flags.
63 # Keyes of the table are option names, and their values are argument
64 # flags of the options.
65 #
66 @argument_flags = Hash.new
67
68 #
69 # Whether error messages are output to stderr.
70 #
71 @quiet = FALSE
72
73 #
74 # Status code.
75 #
76 @status = STATUS_YET
77
78 #
79 # Error code.
80 #
81 @error = nil
82
83 #
84 # Error message.
85 #
86 @error_message = nil
87
88 #
89 # Rest of catinated short options.
90 #
91 @rest_singles = ''
92
93 #
94 # List of non-option-arguments.
95 # Append them to ARGV when option processing is terminated.
96 #
97 @non_option_arguments = Array.new
98
99 if 0 < arguments.length
100 set_options(*arguments)
101 end
102 end
103
104 #
105 # Set ordering.
106 #
107 def ordering=(ordering)
108 #
109 # The method is failed if option processing has already started.
110 #
111 if @status != STATUS_YET
112 set_error(ArgumentError, "argument error")
113 raise RuntimeError,
114 "invoke ordering=, but option processing has already started"
115 end
116
117 #
118 # Check ordering.
119 #
120 if !ORDERINGS.include?(ordering)
121 raise ArgumentError, "invalid ordering `#{ordering}'"
122 end
123 if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT')
124 @ordering = REQUIRE_ORDER
125 else
126 @ordering = ordering
127 end
128 end
129
130 #
131 # Return ordering.
132 #
133 attr_reader :ordering
134
135 #
136 # Set options
137 #
138 def set_options(*arguments)
139 #
140 # The method is failed if option processing has already started.
141 #
142 if @status != STATUS_YET
143 raise RuntimeError,
144 "invoke set_options, but option processing has already started"
145 end
146
147 #
148 # Clear tables of option names and argument flags.
149 #
150 @canonical_names.clear
151 @argument_flags.clear
152
153 arguments.each do |arg|
154 #
155 # Each argument must be an Array.
156 #
157 if !arg.is_a?(Array)
158 raise ArgumentError, "the option list contains non-Array argument"
159 end
160
161 #
162 # Find an argument flag and it set to `argument_flag'.
163 #
164 argument_flag = nil
165 arg.each do |i|
166 if ARGUMENT_FLAGS.include?(i)
167 if argument_flag != nil
168 raise ArgumentError, "too many argument-flags"
169 end
170 argument_flag = i
171 end
172 end
173 raise ArgumentError, "no argument-flag" if argument_flag == nil
174
175 canonical_name = nil
176 arg.each do |i|
177 #
178 # Check an option name.
179 #
180 next if i == argument_flag
181 begin
182 if !i.is_a?(String) || i !~ /^-([^-]|-.+)$/
183 raise ArgumentError, "an invalid option `#{i}'"
184 end
185 if (@canonical_names.include?(i))
186 raise ArgumentError, "option redefined `#{i}'"
187 end
188 rescue
189 @canonical_names.clear
190 @argument_flags.clear
191 raise
192 end
193
194 #
195 # Register the option (`i') to the `@canonical_names' and
196 # `@canonical_names' Hashes.
197 #
198 if canonical_name == nil
199 canonical_name = i
200 end
201 @canonical_names[i] = canonical_name
202 @argument_flags[i] = argument_flag
203 end
204 raise ArgumentError, "no option name" if canonical_name == nil
205 end
206 return self
207 end
208
209 #
210 # Set/Unset `quit' mode.
211 #
212 attr_writer :quiet
213
214 #
215 # Return the flag of `quiet' mode.
216 #
217 attr_reader :quiet
218
219 #
220 # `quiet?' is an alias of `quiet'.
221 #
222 alias quiet? quiet
223
224 #
225 # Termintate option processing.
226 #
227 def terminate
228 return nil if @status == STATUS_TERMINATED
229 raise RuntimeError, "an error has occured" if @error != nil
230
231 @status = STATUS_TERMINATED
232 @non_option_arguments.reverse_each do |argument|
233 ARGV.unshift(argument)
234 end
235
236 @canonical_names = nil
237 @argument_flags = nil
238 @rest_singles = nil
239 @non_option_arguments = nil
240
241 return self
242 end
243
244 #
245 # Examine whether option processing is termintated or not.
246 #
247 def terminated?
248 return @status == STATUS_TERMINATED
249 end
250
251 #
252 # Set an error (protected).
253 #
254 def set_error(type, message)
255 $stderr.print("#{$0}: #{message}\n") if !@quiet
256
257 @error = type
258 @error_message = message
259 @canonical_names = nil
260 @argument_flags = nil
261 @rest_singles = nil
262 @non_option_arguments = nil
263
264 raise type, message
265 end
266 protected :set_error
267
268 #
269 # Examine whether an option processing is failed.
270 #
271 attr_reader :error
272
273 #
274 # `error?' is an alias of `error'.
275 #
276 alias error? error
277
278 #
279 # Return an error message.
280 #
281 def error_message
282 return @error_message
283 end
284
285 #
286 # Get next option name and its argument as an array.
287 #
288 def get
289 option_name, option_argument = nil, ''
290
291 #
292 # Check status.
293 #
294 return nil if @error != nil
295 case @status
296 when STATUS_YET
297 @status = STATUS_STARTED
298 when STATUS_TERMINATED
299 return nil
300 end
301
302 #
303 # Get next option argument.
304 #
305 if 0 < @rest_singles.length
306 argument = '-' + @rest_singles
307 elsif (ARGV.length == 0)
308 terminate
309 return nil
310 elsif @ordering == PERMUTE
311 while 0 < ARGV.length && ARGV[0] !~ /^-./
312 @non_option_arguments.push(ARGV.shift)
313 end
314 if ARGV.length == 0
315 terminate
316 return nil
317 end
318 argument = ARGV.shift
319 elsif @ordering == REQUIRE_ORDER
320 if (ARGV[0] !~ /^-./)
321 terminate
322 return nil
323 end
324 argument = ARGV.shift
325 else
326 argument = ARGV.shift
327 end
328
329 #
330 # Check the special argument `--'.
331 # `--' indicates the end of the option list.
332 #
333 if argument == '--' && @rest_singles.length == 0
334 terminate
335 return nil
336 end
337
338 #
339 # Check for long and short options.
340 #
341 if argument =~ /^(--[^=]+)/ && @rest_singles.length == 0
342 #
343 # This is a long style option, which start with `--'.
344 #
345 pattern = $1
346 if @canonical_names.include?(pattern)
347 option_name = pattern
348 else
349 #
350 # The option `option_name' is not registered in `@canonical_names'.
351 # It may be an abbreviated.
352 #
353 match_count = 0
354 @canonical_names.each_key do |key|
355 if key.index(pattern) == 0
356 option_name = key
357 match_count += 1
358 end
359 end
360 if 2 <= match_count
361 set_error(AmbigousOption, "option `#{argument}' is ambiguous")
362 elsif match_count == 0
363 set_error(InvalidOption, "unrecognized option `#{argument}'")
364 end
365 end
366
367 #
368 # Check an argument to the option.
369 #
370 if @argument_flags[option_name] == REQUIRED_ARGUMENT
371 if argument =~ /=(.*)$/
372 option_argument = $1
373 elsif 0 < ARGV.length
374 option_argument = ARGV.shift
375 else
376 set_error(MissingArgument,
377 "option `#{argument}' requires an argument")
378 end
379 elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
380 if argument =~ /=(.*)$/
381 option_argument = $1
382 elsif 0 < ARGV.length && ARGV[0] !~ /^-./
383 option_argument = ARGV.shift
384 else
385 option_argument = ''
386 end
387 elsif argument =~ /=(.*)$/
388 set_error(NeedlessArgument,
389 "option `#{option_name}' doesn't allow an argument")
390 end
391
392 elsif argument =~ /^(-(.))(.*)/
393 #
394 # This is a short style option, which start with `-' (not `--').
395 # Short options may be catinated (e.g. `-l -g' is equivalent to
396 # `-lg').
397 #
398 option_name, ch, @rest_singles = $1, $2, $3
399
400 if @canonical_names.include?(option_name)
401 #
402 # The option `option_name' is found in `@canonical_names'.
403 # Check its argument.
404 #
405 if @argument_flags[option_name] == REQUIRED_ARGUMENT
406 if 0 < @rest_singles.length
407 option_argument = @rest_singles
408 @rest_singles = ''
409 elsif 0 < ARGV.length
410 option_argument = ARGV.shift
411 else
412 # 1003.2 specifies the format of this message.
413 set_error(MissingArgument, "option requires an argument -- #{ch}")
414 end
415 elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
416 if 0 < @rest_singles.length
417 option_argument = @rest_singles
418 @rest_singles = ''
419 elsif 0 < ARGV.length && ARGV[0] !~ /^-./
420 option_argument = ARGV.shift
421 else
422 option_argument = ''
423 end
424 end
425 else
426 #
427 # This is an invalid option.
428 # 1003.2 specifies the format of this message.
429 #
430 if ENV.include?('POSIXLY_CORRECT')
431 set_error(InvalidOption, "illegal option -- #{ch}")
432 else
433 set_error(InvalidOption, "invalid option -- #{ch}")
434 end
435 end
436 else
437 #
438 # This is a non-option argument.
439 # Only RETURN_IN_ORDER falled into here.
440 #
441 return '', argument
442 end
443
444 return @canonical_names[option_name], option_argument
445 end
446
447 #
448 # `get_option' is an alias of `get'.
449 #
450 alias get_option get
451
452 #
453 # Iterator version of `get'.
454 #
455 def each
456 loop do
457 option_name, option_argument = get_option
458 break if option_name == nil
459 yield option_name, option_argument
460 end
461 end
462
463 #
464 # `each_option' is an alias of `each'.
465 #
466 alias each_option each
467 end