1#
2# GetoptLong for Ruby
3#
4# Copyright (C) 1998, 1999, 2000  Motoyuki Kasahara.
5#
6# You may redistribute and/or modify this library under the same license
7# terms as Ruby.
8#
9# See GetoptLong for documentation.
10#
11# Additional documents and the latest version of `getoptlong.rb' can be
12# found at http://www.sra.co.jp/people/m-kasahr/ruby/getoptlong/
13
14# The GetoptLong class allows you to parse command line options similarly to
15# the GNU getopt_long() C library call. Note, however, that GetoptLong is a
16# pure Ruby implementation.
17#
18# GetoptLong allows for POSIX-style options like <tt>--file</tt> as well
19# as single letter options like <tt>-f</tt>
20#
21# The empty option <tt>--</tt> (two minus symbols) is used to end option
22# processing. This can be particularly important if options have optional
23# arguments.
24#
25# Here is a simple example of usage:
26#
27#     require 'getoptlong'
28#
29#     opts = GetoptLong.new(
30#       [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
31#       [ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ],
32#       [ '--name', GetoptLong::OPTIONAL_ARGUMENT ]
33#     )
34#
35#     dir = nil
36#     name = nil
37#     repetitions = 1
38#     opts.each do |opt, arg|
39#       case opt
40#         when '--help'
41#           puts <<-EOF
42#     hello [OPTION] ... DIR
43#
44#     -h, --help:
45#        show help
46#
47#     --repeat x, -n x:
48#        repeat x times
49#
50#     --name [name]:
51#        greet user by name, if name not supplied default is John
52#
53#     DIR: The directory in which to issue the greeting.
54#           EOF
55#         when '--repeat'
56#           repetitions = arg.to_i
57#         when '--name'
58#           if arg == ''
59#             name = 'John'
60#           else
61#             name = arg
62#           end
63#       end
64#     end
65#
66#     if ARGV.length != 1
67#       puts "Missing dir argument (try --help)"
68#       exit 0
69#     end
70#
71#     dir = ARGV.shift
72#
73#     Dir.chdir(dir)
74#     for i in (1..repetitions)
75#       print "Hello"
76#       if name
77#         print ", #{name}"
78#       end
79#       puts
80#     end
81#
82# Example command line:
83#
84#     hello -n 6 --name -- /tmp
85#
86class GetoptLong
87  #
88  # Orderings.
89  #
90  ORDERINGS = [REQUIRE_ORDER = 0, PERMUTE = 1, RETURN_IN_ORDER = 2]
91
92  #
93  # Argument flags.
94  #
95  ARGUMENT_FLAGS = [NO_ARGUMENT = 0, REQUIRED_ARGUMENT = 1,
96    OPTIONAL_ARGUMENT = 2]
97
98  #
99  # Status codes.
100  #
101  STATUS_YET, STATUS_STARTED, STATUS_TERMINATED = 0, 1, 2
102
103  #
104  # Error types.
105  #
106  class Error  < StandardError; end
107  class AmbiguousOption   < Error; end
108  class NeedlessArgument < Error; end
109  class MissingArgument  < Error; end
110  class InvalidOption    < Error; end
111
112  #
113  # Set up option processing.
114  #
115  # The options to support are passed to new() as an array of arrays.
116  # Each sub-array contains any number of String option names which carry
117  # the same meaning, and one of the following flags:
118  #
119  # GetoptLong::NO_ARGUMENT :: Option does not take an argument.
120  #
121  # GetoptLong::REQUIRED_ARGUMENT :: Option always takes an argument.
122  #
123  # GetoptLong::OPTIONAL_ARGUMENT :: Option may or may not take an argument.
124  #
125  # The first option name is considered to be the preferred (canonical) name.
126  # Other than that, the elements of each sub-array can be in any order.
127  #
128  def initialize(*arguments)
129    #
130    # Current ordering.
131    #
132    if ENV.include?('POSIXLY_CORRECT')
133      @ordering = REQUIRE_ORDER
134    else
135      @ordering = PERMUTE
136    end
137
138    #
139    # Hash table of option names.
140    # Keys of the table are option names, and their values are canonical
141    # names of the options.
142    #
143    @canonical_names = Hash.new
144
145    #
146    # Hash table of argument flags.
147    # Keys of the table are option names, and their values are argument
148    # flags of the options.
149    #
150    @argument_flags = Hash.new
151
152    #
153    # Whether error messages are output to $stderr.
154    #
155    @quiet = FALSE
156
157    #
158    # Status code.
159    #
160    @status = STATUS_YET
161
162    #
163    # Error code.
164    #
165    @error = nil
166
167    #
168    # Error message.
169    #
170    @error_message = nil
171
172    #
173    # Rest of catenated short options.
174    #
175    @rest_singles = ''
176
177    #
178    # List of non-option-arguments.
179    # Append them to ARGV when option processing is terminated.
180    #
181    @non_option_arguments = Array.new
182
183    if 0 < arguments.length
184      set_options(*arguments)
185    end
186  end
187
188  #
189  # Set the handling of the ordering of options and arguments.
190  # A RuntimeError is raised if option processing has already started.
191  #
192  # The supplied value must be a member of GetoptLong::ORDERINGS. It alters
193  # the processing of options as follows:
194  #
195  # <b>REQUIRE_ORDER</b> :
196  #
197  # Options are required to occur before non-options.
198  #
199  # Processing of options ends as soon as a word is encountered that has not
200  # been preceded by an appropriate option flag.
201  #
202  # For example, if -a and -b are options which do not take arguments,
203  # parsing command line arguments of '-a one -b two' would result in
204  # 'one', '-b', 'two' being left in ARGV, and only ('-a', '') being
205  # processed as an option/arg pair.
206  #
207  # This is the default ordering, if the environment variable
208  # POSIXLY_CORRECT is set. (This is for compatibility with GNU getopt_long.)
209  #
210  # <b>PERMUTE</b> :
211  #
212  # Options can occur anywhere in the command line parsed. This is the
213  # default behavior.
214  #
215  # Every sequence of words which can be interpreted as an option (with or
216  # without argument) is treated as an option; non-option words are skipped.
217  #
218  # For example, if -a does not require an argument and -b optionally takes
219  # an argument, parsing '-a one -b two three' would result in ('-a','') and
220  # ('-b', 'two') being processed as option/arg pairs, and 'one','three'
221  # being left in ARGV.
222  #
223  # If the ordering is set to PERMUTE but the environment variable
224  # POSIXLY_CORRECT is set, REQUIRE_ORDER is used instead. This is for
225  # compatibility with GNU getopt_long.
226  #
227  # <b>RETURN_IN_ORDER</b> :
228  #
229  # All words on the command line are processed as options. Words not
230  # preceded by a short or long option flag are passed as arguments
231  # with an option of '' (empty string).
232  #
233  # For example, if -a requires an argument but -b does not, a command line
234  # of '-a one -b two three' would result in option/arg pairs of ('-a', 'one')
235  # ('-b', ''), ('', 'two'), ('', 'three') being processed.
236  #
237  def ordering=(ordering)
238    #
239    # The method is failed if option processing has already started.
240    #
241    if @status != STATUS_YET
242      set_error(ArgumentError, "argument error")
243      raise RuntimeError,
244        "invoke ordering=, but option processing has already started"
245    end
246
247    #
248    # Check ordering.
249    #
250    if !ORDERINGS.include?(ordering)
251      raise ArgumentError, "invalid ordering `#{ordering}'"
252    end
253    if ordering == PERMUTE && ENV.include?('POSIXLY_CORRECT')
254      @ordering = REQUIRE_ORDER
255    else
256      @ordering = ordering
257    end
258  end
259
260  #
261  # Return ordering.
262  #
263  attr_reader :ordering
264
265  #
266  # Set options. Takes the same argument as GetoptLong.new.
267  #
268  # Raises a RuntimeError if option processing has already started.
269  #
270  def set_options(*arguments)
271    #
272    # The method is failed if option processing has already started.
273    #
274    if @status != STATUS_YET
275      raise RuntimeError,
276        "invoke set_options, but option processing has already started"
277    end
278
279    #
280    # Clear tables of option names and argument flags.
281    #
282    @canonical_names.clear
283    @argument_flags.clear
284
285    arguments.each do |arg|
286      if !arg.is_a?(Array)
287       raise ArgumentError, "the option list contains non-Array argument"
288      end
289
290      #
291      # Find an argument flag and it set to `argument_flag'.
292      #
293      argument_flag = nil
294      arg.each do |i|
295        if ARGUMENT_FLAGS.include?(i)
296          if argument_flag != nil
297            raise ArgumentError, "too many argument-flags"
298          end
299          argument_flag = i
300        end
301      end
302
303      raise ArgumentError, "no argument-flag" if argument_flag == nil
304
305      canonical_name = nil
306      arg.each do |i|
307        #
308        # Check an option name.
309        #
310        next if i == argument_flag
311        begin
312          if !i.is_a?(String) || i !~ /^-([^-]|-.+)$/
313            raise ArgumentError, "an invalid option `#{i}'"
314          end
315          if (@canonical_names.include?(i))
316            raise ArgumentError, "option redefined `#{i}'"
317          end
318        rescue
319          @canonical_names.clear
320          @argument_flags.clear
321          raise
322        end
323
324        #
325        # Register the option (`i') to the `@canonical_names' and
326        # `@canonical_names' Hashes.
327        #
328        if canonical_name == nil
329          canonical_name = i
330        end
331        @canonical_names[i] = canonical_name
332        @argument_flags[i] = argument_flag
333      end
334      raise ArgumentError, "no option name" if canonical_name == nil
335    end
336    return self
337  end
338
339  #
340  # Set/Unset `quiet' mode.
341  #
342  attr_writer :quiet
343
344  #
345  # Return the flag of `quiet' mode.
346  #
347  attr_reader :quiet
348
349  #
350  # `quiet?' is an alias of `quiet'.
351  #
352  alias quiet? quiet
353
354  #
355  # Explicitly terminate option processing.
356  #
357  def terminate
358    return nil if @status == STATUS_TERMINATED
359    raise RuntimeError, "an error has occurred" if @error != nil
360
361    @status = STATUS_TERMINATED
362    @non_option_arguments.reverse_each do |argument|
363      ARGV.unshift(argument)
364    end
365
366    @canonical_names = nil
367    @argument_flags = nil
368    @rest_singles = nil
369    @non_option_arguments = nil
370
371    return self
372  end
373
374  #
375  # Returns true if option processing has terminated, false otherwise.
376  #
377  def terminated?
378    return @status == STATUS_TERMINATED
379  end
380
381  #
382  # Set an error (a protected method).
383  #
384  def set_error(type, message)
385    $stderr.print("#{$0}: #{message}\n") if !@quiet
386
387    @error = type
388    @error_message = message
389    @canonical_names = nil
390    @argument_flags = nil
391    @rest_singles = nil
392    @non_option_arguments = nil
393
394    raise type, message
395  end
396  protected :set_error
397
398  #
399  # Examine whether an option processing is failed.
400  #
401  attr_reader :error
402
403  #
404  # `error?' is an alias of `error'.
405  #
406  alias error? error
407
408  # Return the appropriate error message in POSIX-defined format.
409  # If no error has occurred, returns nil.
410  #
411  def error_message
412    return @error_message
413  end
414
415  #
416  # Get next option name and its argument, as an Array of two elements.
417  #
418  # The option name is always converted to the first (preferred)
419  # name given in the original options to GetoptLong.new.
420  #
421  # Example: ['--option', 'value']
422  #
423  # Returns nil if the processing is complete (as determined by
424  # STATUS_TERMINATED).
425  #
426  def get
427    option_name, option_argument = nil, ''
428
429    #
430    # Check status.
431    #
432    return nil if @error != nil
433    case @status
434    when STATUS_YET
435      @status = STATUS_STARTED
436    when STATUS_TERMINATED
437      return nil
438    end
439
440    #
441    # Get next option argument.
442    #
443    if 0 < @rest_singles.length
444      argument = '-' + @rest_singles
445    elsif (ARGV.length == 0)
446      terminate
447      return nil
448    elsif @ordering == PERMUTE
449      while 0 < ARGV.length && ARGV[0] !~ /^-./
450        @non_option_arguments.push(ARGV.shift)
451      end
452      if ARGV.length == 0
453        terminate
454        return nil
455      end
456      argument = ARGV.shift
457    elsif @ordering == REQUIRE_ORDER
458      if (ARGV[0] !~ /^-./)
459        terminate
460        return nil
461      end
462      argument = ARGV.shift
463    else
464      argument = ARGV.shift
465    end
466
467    #
468    # Check the special argument `--'.
469    # `--' indicates the end of the option list.
470    #
471    if argument == '--' && @rest_singles.length == 0
472      terminate
473      return nil
474    end
475
476    #
477    # Check for long and short options.
478    #
479    if argument =~ /^(--[^=]+)/ && @rest_singles.length == 0
480      #
481      # This is a long style option, which start with `--'.
482      #
483      pattern = $1
484      if @canonical_names.include?(pattern)
485        option_name = pattern
486      else
487        #
488        # The option `option_name' is not registered in `@canonical_names'.
489        # It may be an abbreviated.
490        #
491        matches = []
492        @canonical_names.each_key do |key|
493          if key.index(pattern) == 0
494            option_name = key
495            matches << key
496          end
497        end
498        if 2 <= matches.length
499          set_error(AmbiguousOption, "option `#{argument}' is ambiguous between #{matches.join(', ')}")
500        elsif matches.length == 0
501          set_error(InvalidOption, "unrecognized option `#{argument}'")
502        end
503      end
504
505      #
506      # Check an argument to the option.
507      #
508      if @argument_flags[option_name] == REQUIRED_ARGUMENT
509        if argument =~ /=(.*)$/
510          option_argument = $1
511        elsif 0 < ARGV.length
512          option_argument = ARGV.shift
513        else
514          set_error(MissingArgument,
515                    "option `#{argument}' requires an argument")
516        end
517      elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
518        if argument =~ /=(.*)$/
519          option_argument = $1
520        elsif 0 < ARGV.length && ARGV[0] !~ /^-./
521          option_argument = ARGV.shift
522        else
523          option_argument = ''
524        end
525      elsif argument =~ /=(.*)$/
526        set_error(NeedlessArgument,
527                  "option `#{option_name}' doesn't allow an argument")
528      end
529
530    elsif argument =~ /^(-(.))(.*)/
531      #
532      # This is a short style option, which start with `-' (not `--').
533      # Short options may be catenated (e.g. `-l -g' is equivalent to
534      # `-lg').
535      #
536      option_name, ch, @rest_singles = $1, $2, $3
537
538      if @canonical_names.include?(option_name)
539        #
540        # The option `option_name' is found in `@canonical_names'.
541        # Check its argument.
542        #
543        if @argument_flags[option_name] == REQUIRED_ARGUMENT
544          if 0 < @rest_singles.length
545            option_argument = @rest_singles
546            @rest_singles = ''
547          elsif 0 < ARGV.length
548            option_argument = ARGV.shift
549          else
550            # 1003.2 specifies the format of this message.
551            set_error(MissingArgument, "option requires an argument -- #{ch}")
552          end
553        elsif @argument_flags[option_name] == OPTIONAL_ARGUMENT
554          if 0 < @rest_singles.length
555            option_argument = @rest_singles
556            @rest_singles = ''
557          elsif 0 < ARGV.length && ARGV[0] !~ /^-./
558            option_argument = ARGV.shift
559          else
560            option_argument = ''
561          end
562        end
563      else
564        #
565        # This is an invalid option.
566        # 1003.2 specifies the format of this message.
567        #
568        if ENV.include?('POSIXLY_CORRECT')
569          set_error(InvalidOption, "invalid option -- #{ch}")
570        else
571          set_error(InvalidOption, "invalid option -- #{ch}")
572        end
573      end
574    else
575      #
576      # This is a non-option argument.
577      # Only RETURN_IN_ORDER falled into here.
578      #
579      return '', argument
580    end
581
582    return @canonical_names[option_name], option_argument
583  end
584
585  #
586  # `get_option' is an alias of `get'.
587  #
588  alias get_option get
589
590  # Iterator version of `get'.
591  #
592  # The block is called repeatedly with two arguments:
593  # The first is the option name.
594  # The second is the argument which followed it (if any).
595  # Example: ('--opt', 'value')
596  #
597  # The option name is always converted to the first (preferred)
598  # name given in the original options to GetoptLong.new.
599  #
600  def each
601    loop do
602      option_name, option_argument = get_option
603      break if option_name == nil
604      yield option_name, option_argument
605    end
606  end
607
608  #
609  # `each_option' is an alias of `each'.
610  #
611  alias each_option each
612end
613