getopt 1.3.6 → 1.3.7

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,53 +1,61 @@
1
- == 8-Aug-2007 - 1.3.6
2
- * The Getopt::StdError class is now Getopt::Std::Error.
3
- * The Getopt::LongError class is now Getopt::Long::Error.
4
- * Added some inline rdoc documentation to the source code.
5
- * Added a Rakefile with tasks for installation and testing.
6
- * Removed the install.rb file - use the 'rake install' task instead.
7
-
8
- == 5-Jul-2006 - 1.3.5
9
- * Fixed a bug where multiple long switches with the same first character
10
- could cause invalid results. Thanks go to Michael Campbell for the spot.
11
- * Added documentation to the README file that explains what happens if you
12
- specify multiple long switches with the same first character and no short
13
- switch alias.
14
-
15
- == 7-Mar-2006 - 1.3.4
16
- * Fixed Getopt::Long so that it can handle embedded hyphens in the long
17
- form, e.g. --foo-bar. Thanks go to Mark Meves for the spot.
18
- * Corresponding test suite additions.
19
- * Added example to the 'example_long.rb' file that uses long form with
20
- embedded hyphens.
21
-
22
- == 22-Feb-2006 - 1.3.3
23
- * Bug fix for the two argument form of Getopt::Long.getopts.
24
- * Corresponding test suite additions.
25
-
26
- == 13-Feb-2006 - 1.3.2
27
- * Improved error message if an option is passed without a preceding switch.
28
- * Minor documentation fixes and clarifications.
29
-
30
- == 18-Nov-2005 - 1.3.1
31
- * Added support for compressed switches with getopt/long.
32
- * More tests.
33
- * Fixed a bug in the gemspec.
34
-
35
- == 4-Nov-2005 - 1.3.0
36
- * Added the Getopt::Long class (long.rb). This is a complete revamp of the
37
- old getoptlong package, with ideas tossed in from Perl's Getopt::Long
38
- package. See the README and example script for more detail.
39
- * Added an example, and renamed the "test_std.rb" example to "example_std.rb".
40
- * Added lots of documentation to the README file.
41
- * Updated the MANIFEST, test suite, etc.
42
-
43
- == 24-Oct-2005 - 1.2.0
44
- * Altered the way multiple occurrences of the same switch are handled, for
45
- those switches that accept arguments.
46
-
47
- == 7-Oct-2005 - 1.1.0
48
- * Changed parser, added a bit stricter enforcement
49
- * Now handles squished arguments properly, e.g. "-ID" as well as "-I -D"
50
- * Some test suite changes
51
-
52
- == 5-Oct-2005 - 1.0.0
53
- * Initial commit
1
+ == 27-Jul-2008 - 1.3.7
2
+ * Fixed a potential infinite hash recursion bug in ARGV processing. This
3
+ was smoked out as the result of the alternate hash implementations in
4
+ JRuby and Ruby 1.9.
5
+ * Added the example programs to the gemspec.
6
+ * Removed the ts_all.rb file, and renamed the other test files. The Rakefile
7
+ test task was updated accordingly.
8
+
9
+ == 8-Aug-2007 - 1.3.6
10
+ * The Getopt::StdError class is now Getopt::Std::Error.
11
+ * The Getopt::LongError class is now Getopt::Long::Error.
12
+ * Added some inline rdoc documentation to the source code.
13
+ * Added a Rakefile with tasks for installation and testing.
14
+ * Removed the install.rb file - use the 'rake install' task instead.
15
+
16
+ == 5-Jul-2006 - 1.3.5
17
+ * Fixed a bug where multiple long switches with the same first character
18
+ could cause invalid results. Thanks go to Michael Campbell for the spot.
19
+ * Added documentation to the README file that explains what happens if you
20
+ specify multiple long switches with the same first character and no short
21
+ switch alias.
22
+
23
+ == 7-Mar-2006 - 1.3.4
24
+ * Fixed Getopt::Long so that it can handle embedded hyphens in the long
25
+ form, e.g. --foo-bar. Thanks go to Mark Meves for the spot.
26
+ * Corresponding test suite additions.
27
+ * Added example to the 'example_long.rb' file that uses long form with
28
+ embedded hyphens.
29
+
30
+ == 22-Feb-2006 - 1.3.3
31
+ * Bug fix for the two argument form of Getopt::Long.getopts.
32
+ * Corresponding test suite additions.
33
+
34
+ == 13-Feb-2006 - 1.3.2
35
+ * Improved error message if an option is passed without a preceding switch.
36
+ * Minor documentation fixes and clarifications.
37
+
38
+ == 18-Nov-2005 - 1.3.1
39
+ * Added support for compressed switches with getopt/long.
40
+ * More tests.
41
+ * Fixed a bug in the gemspec.
42
+
43
+ == 4-Nov-2005 - 1.3.0
44
+ * Added the Getopt::Long class (long.rb). This is a complete revamp of the
45
+ old getoptlong package, with ideas tossed in from Perl's Getopt::Long
46
+ package. See the README and example script for more detail.
47
+ * Added an example, and renamed the "test_std.rb" example to "example_std.rb".
48
+ * Added lots of documentation to the README file.
49
+ * Updated the MANIFEST, test suite, etc.
50
+
51
+ == 24-Oct-2005 - 1.2.0
52
+ * Altered the way multiple occurrences of the same switch are handled, for
53
+ those switches that accept arguments.
54
+
55
+ == 7-Oct-2005 - 1.1.0
56
+ * Changed parser, added a bit stricter enforcement
57
+ * Now handles squished arguments properly, e.g. "-ID" as well as "-I -D"
58
+ * Some test suite changes
59
+
60
+ == 5-Oct-2005 - 1.0.0
61
+ * Initial commit
data/MANIFEST CHANGED
@@ -1,12 +1,11 @@
1
- * CHANGES
2
- * MANIFEST
3
- * README
4
- * Rakefile
5
- * getopt.gemspec
6
- * examples/example_std.rb
7
- * examples/example_long.rb
8
- * lib/getopt/std.rb
9
- * lib/getopt/long.rb
10
- * test/tc_getopt_std.rb
11
- * test/tc_getopt_long.rb
12
- * test/ts_all.rb
1
+ * CHANGES
2
+ * MANIFEST
3
+ * README
4
+ * Rakefile
5
+ * getopt.gemspec
6
+ * examples/example_std.rb
7
+ * examples/example_long.rb
8
+ * lib/getopt/std.rb
9
+ * lib/getopt/long.rb
10
+ * test/test_getopt_std.rb
11
+ * test/test_getopt_long.rb
data/Rakefile CHANGED
@@ -1,23 +1,22 @@
1
- require 'rake'
2
- require 'rake/testtask'
3
-
4
- desc "Install the getopt package (non-gem)"
5
- task :install do
6
- dest = File.join(Config::CONFIG['sitelibdir'], 'getopt')
7
- Dir.mkdir(dest) unless File.exists? dest
8
- cp 'lib/getopt/std.rb', dest, :verbose => true
9
- cp 'lib/getopt/long.rb', dest, :verbose => true
10
- end
11
-
12
- desc "Install the getopt package as a gem"
13
- task :install_gem do
14
- ruby 'getopt.gemspec'
15
- file = Dir["*.gem"].first
16
- sh "gem install #{file}"
17
- end
18
-
19
- Rake::TestTask.new do |t|
20
- t.libs << 'lib'
21
- t.warning = true
22
- t.test_files = FileList['test/ts_all.rb']
23
- end
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ desc "Install the getopt package (non-gem)"
5
+ task :install do
6
+ dest = File.join(Config::CONFIG['sitelibdir'], 'getopt')
7
+ Dir.mkdir(dest) unless File.exists? dest
8
+ cp 'lib/getopt/std.rb', dest, :verbose => true
9
+ cp 'lib/getopt/long.rb', dest, :verbose => true
10
+ end
11
+
12
+ desc "Install the getopt package as a gem"
13
+ task :install_gem do
14
+ ruby 'getopt.gemspec'
15
+ file = Dir["*.gem"].first
16
+ sh "gem install #{file}"
17
+ end
18
+
19
+ Rake::TestTask.new do |t|
20
+ t.warning = true
21
+ t.verbose = true
22
+ end
@@ -0,0 +1,65 @@
1
+ ##########################################################################
2
+ # example_long.rb
3
+ #
4
+ # A series of examples to demonstrate the different ways that you can
5
+ # handle command line options.
6
+ ##########################################################################
7
+ require "getopt/long"
8
+
9
+ # The truly lazy way. This creates two valid command line switches,
10
+ # automatically creates single letter switches (-f, -b), and sets each
11
+ # switch type to BOOLEAN.
12
+ opts = Getopt::Long.getopts("--foo --bar")
13
+
14
+ # Here's a comprehensive example that uses all types and options.
15
+ opts = Getopt::Long.getopts(
16
+ ["--foo"], # --foo, -f, BOOLEAN
17
+ ["--bar", "-z"], # --bar, -z, BOOLEAN
18
+ ["--baz", "-b", OPTIONAL], # --baz, -b, OPTIONAL
19
+ ["--name", "-n", REQUIRED], # --name, -n, REQUIRED
20
+ ["--more", "-m", INCREMENT], # --more, -m, INCREMENT
21
+ ["--verbose", "-v", BOOLEAN], # --verbose, -v, BOOLEAN
22
+ ["--my-name", "-x", REQUIRED] # --my-name, -x, REQUIRED
23
+ )
24
+
25
+ # Using the above example:
26
+
27
+ # User passes "-f"
28
+ # opts -> { "f" => true, "foo" => true }
29
+
30
+ # User passes "-z"
31
+ # opts -> { "z" => true, "bar" => true }
32
+
33
+ # User passes "--verbose"
34
+ # opts -> { "v" => true, "verbose" => true }
35
+
36
+ # User passes "-m"
37
+ # opts -> { "m" => 1, "more" => 1 }
38
+
39
+ # User passes "-m -m"
40
+ # opts -> { "m" => 2, "more" => 2 }
41
+
42
+ # User passes "--name Dan" or "--name=Dan" or "-n Dan" or "-nDan"
43
+ # opts -> { "n" => "Dan", "name" => "Dan" }
44
+
45
+ # User passes "--my-name Dan" or "--my-name=Dan" or "-x Dan" or "-xDan"
46
+ # opts -> { "x" => "Dan", "my-name" => "Dan" }
47
+
48
+ # User passes "--name Dan --name Matz"
49
+ # opts -> { "n" => ["Dan","Matz"], "name" => ["Dan","Matz"] }
50
+
51
+ # User passes "--baz" with no argument
52
+ # opts -> { "b" => nil, "baz" => nil }
53
+
54
+ # User passes "--baz hello"
55
+ # opts =-> { "b" => "hello", "baz" => "hello" }
56
+
57
+ # User passes "-n" with no argument
58
+ # Getopt::LongError is raised, since an argument is REQUIRED.
59
+
60
+ # User passes "-f hello"
61
+ # Getopt::LongError is raised, since a BOOLEAN switch does not take an argument
62
+
63
+ # User passes "--warning"
64
+ # Getopt::LongError is raised, since "--warning" was not specified as a valid
65
+ # switch in the call to Getopt::Long.getopts
@@ -0,0 +1,38 @@
1
+ ####################################################
2
+ # example_std.rb
3
+ #
4
+ # Some samples of how to use the Getopt::Std class.
5
+ #####################################################
6
+ base = File.basename(Dir.pwd)
7
+
8
+ if base == "examples" || base =~ /getopt/
9
+ Dir.chdir("..") if base == "examples"
10
+ $LOAD_PATH.unshift(Dir.pwd + "/lib")
11
+ Dir.chdir("examples") rescue nil
12
+ end
13
+
14
+ require "getopt/std"
15
+ include Getopt
16
+
17
+ # Try passing different switches to this script to see what happens
18
+ opts = Std.getopts("o:ID")
19
+ p opts
20
+
21
+ # User passes "-o hello -I"
22
+ # Result: {"o" => "hello", "I" => true}
23
+
24
+ # User passes "-I -D"
25
+ # Result: {"I" => true, "D" => true}
26
+
27
+ # User passes nothing
28
+ # Result: {}
29
+
30
+ # User passes "-o hello -o world -I"
31
+ # Result: {"I" => true, "o" => ["hello", "world"]}
32
+
33
+ # User passes "-o -I"
34
+ # Result: Getopt::StdError, because -o requires an argument (and does not
35
+ # accept -I as an argument, since it is a valid switch)
36
+
37
+ # User passes "-I -X"
38
+ # Result: Getopt::StdError, because -X was not listed as a valid switch.
@@ -0,0 +1,24 @@
1
+ require "rubygems"
2
+
3
+ spec = Gem::Specification.new do |gem|
4
+ gem.name = "getopt"
5
+ gem.version = "1.3.7"
6
+ gem.author = "Daniel J. Berger"
7
+ gem.email = "djberg96@gmail.com"
8
+ gem.homepage = "http://www.rubyforge.org/projects/shards"
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.summary = "Getopt::Std and Getopt::Long option parsers for Ruby"
11
+ gem.description = "Getopt::Std and Getopt::Long option parsers for Ruby"
12
+ gem.test_files = Dir["test/*.rb"]
13
+ gem.has_rdoc = true
14
+ gem.files = Dir['lib/**/*.rb'] + Dir['[A-Z]*'] + Dir['test/*'] + Dir['examples/*.rb']
15
+ gem.files.reject! { |fn| fn.include? "CVS" }
16
+ gem.require_path = "lib"
17
+ gem.extra_rdoc_files = ["README", "CHANGES", "MANIFEST"]
18
+ gem.rubyforge_project = 'shards'
19
+ end
20
+
21
+ if $0 == __FILE__
22
+ Gem.manage_gems
23
+ Gem::Builder.new(spec).build
24
+ end
@@ -1,232 +1,234 @@
1
- module Getopt
2
-
3
- REQUIRED = 0
4
- BOOLEAN = 1
5
- OPTIONAL = 2
6
- INCREMENT = 3
7
- NEGATABLE = 4
8
- NUMERIC = 5
9
-
10
- class Long
11
- class Error < StandardError; end
12
-
13
- VERSION = '1.3.6'
14
-
15
- # Takes an array of switches. Each array consists of up to three
16
- # elements that indicate the name and type of switch. Returns a hash
17
- # containing each switch name, minus the '-', as a key. The value
18
- # for each key depends on the type of switch and/or the value provided
19
- # by the user.
20
- #
21
- # The long switch _must_ be provided. The short switch defaults to the
22
- # first letter of the short switch. The default type is BOOLEAN.
23
- #
24
- # Example:
25
- #
26
- # opts = Getopt::Long.getopts(
27
- # ["--debug"],
28
- # ["--verbose", "-v"],
29
- # ["--level", "-l", NUMERIC]
30
- # )
31
- #
32
- # See the README file for more information.
33
- #
34
- def self.getopts(*switches)
35
- if switches.empty?
36
- raise ArgumentError, "no switches provided"
37
- end
38
-
39
- hash = {} # Hash returned to user
40
- valid = [] # Tracks valid switches
41
- types = {} # Tracks argument types
42
- syns = {} # Tracks long and short arguments, or multiple shorts
43
-
44
- # If a string is passed, split it and convert it to an array of arrays
45
- if switches.first.kind_of?(String)
46
- switches = switches.join.split
47
- switches.map!{ |switch| switch = [switch] }
48
- end
49
-
50
- # Set our list of valid switches, and proper types for each switch
51
- switches.each{ |switch|
52
- valid.push(switch[0]) # Set valid long switches
53
-
54
- # Set type for long switch, default to BOOLEAN.
55
- if switch[1].kind_of?(Fixnum)
56
- switch[2] = switch[1]
57
- types[switch[0]] = switch[2]
58
- switch[1] = switch[0][1..2]
59
- else
60
- switch[2] ||= BOOLEAN
61
- types[switch[0]] = switch[2]
62
- switch[1] ||= switch[0][1..2]
63
- end
64
-
65
- # Create synonym hash. Default to first char of long switch for
66
- # short switch, e.g. "--verbose" creates a "-v" synonym. The same
67
- # synonym can only be used once - first one wins.
68
- syns[switch[0]] = switch[1] unless syns[switch[1]]
69
- syns[switch[1]] = switch[0] unless syns[switch[1]]
70
-
71
- switch[1].each{ |char|
72
- types[char] = switch[2] # Set type for short switch
73
- valid.push(char) # Set valid short switches
74
- }
75
- }
76
-
77
- re_long = /^(--\w+[-\w+]*)?$/
78
- re_short = /^(-\w)$/
79
- re_long_eq = /^(--\w+[-\w+]*)?=(.*?)$|(-\w?)=(.*?)$/
80
- re_short_sq = /^(-\w)(\S+?)$/
81
-
82
- ARGV.each_with_index{ |opt, index|
83
-
84
- # Allow either -x -v or -xv style for single char args
85
- if re_short_sq.match(opt)
86
- chars = opt.split("")[1..-1].map{ |s| s = "-#{s}" }
87
-
88
- chars.each_with_index{ |char, i|
89
- unless valid.include?(char)
90
- raise Error, "invalid switch '#{char}'"
91
- end
92
-
93
- # Grab the next arg if the switch takes a required arg
94
- if types[char] == REQUIRED
95
- # Deal with a argument squished up against switch
96
- if chars[i+1]
97
- arg = chars[i+1..-1].join.tr("-","")
98
- ARGV.push(char, arg)
99
- break
100
- else
101
- arg = ARGV.delete_at(index+1)
102
- if arg.nil? || valid.include?(arg) # Minor cheat here
103
- err = "no value provided for required argument '#{char}'"
104
- raise Error, err
105
- end
106
- ARGV.push(char, arg)
107
- end
108
- elsif types[char] == OPTIONAL
109
- if chars[i+1] && !valid.include?(chars[i+1])
110
- arg = chars[i+1..-1].join.tr("-","")
111
- ARGV.push(char, arg)
112
- break
113
- elsif
114
- if ARGV[index+1] && !valid.include?(ARGV[index+1])
115
- arg = ARGV.delete_at(index+1)
116
- ARGV.push(char, arg)
117
- end
118
- else
119
- ARGV.push(char)
120
- end
121
- else
122
- ARGV.push(char)
123
- end
124
- }
125
- next
126
- end
127
-
128
- if match = re_long.match(opt) || match = re_short.match(opt)
129
- switch = match.captures.first
130
- end
131
-
132
- if match = re_long_eq.match(opt)
133
- switch, value = match.captures.compact
134
- ARGV.push(switch, value)
135
- next
136
- end
137
-
138
- # Make sure that all the switches are valid. If 'switch' isn't
139
- # defined at this point, it means an option was passed without
140
- # a preceding switch, e.g. --option foo bar.
141
- unless valid.include?(switch)
142
- switch ||= opt
143
- raise Error, "invalid switch '#{switch}'"
144
- end
145
-
146
- # Required arguments
147
- if types[switch] == REQUIRED
148
- nextval = ARGV[index+1]
149
-
150
- # Make sure there's a value for mandatory arguments
151
- if nextval.nil?
152
- err = "no value provided for required argument '#{switch}'"
153
- raise Error, err
154
- end
155
-
156
- # If there is a value, make sure it's not another switch
157
- if valid.include?(nextval)
158
- err = "cannot pass switch '#{nextval}' as an argument"
159
- raise Error, err
160
- end
161
-
162
- # If the same option appears more than once, put the values
163
- # in array.
164
- if hash[switch]
165
- hash[switch] = [hash[switch], nextval].flatten
166
- else
167
- hash[switch] = nextval
168
- end
169
- ARGV.delete_at(index+1)
170
- end
171
-
172
- # For boolean arguments set the switch's value to true.
173
- if types[switch] == BOOLEAN
174
- if hash.has_key?(switch)
175
- raise Error, "boolean switch already set"
176
- end
177
- hash[switch] = true
178
- end
179
-
180
- # For increment arguments, set the switch's value to 0, or
181
- # increment it by one if it already exists.
182
- if types[switch] == INCREMENT
183
- if hash.has_key?(switch)
184
- hash[switch] += 1
185
- else
186
- hash[switch] = 1
187
- end
188
- end
189
-
190
- # For optional argument, there may be an argument. If so, it
191
- # cannot be another switch. If not, it is set to true.
192
- if types[switch] == OPTIONAL
193
- nextval = ARGV[index+1]
194
- if valid.include?(nextval)
195
- hash[switch] = true
196
- else
197
- hash[switch] = nextval
198
- ARGV.delete_at(index+1)
199
- end
200
- end
201
- }
202
-
203
- # Set synonymous switches to the same value, e.g. if -t is a synonym
204
- # for --test, and the user passes "--test", then set "-t" to the same
205
- # value that "--test" was set to.
206
- #
207
- # This allows users to refer to the long or short switch and get
208
- # the same value
209
- hash.each{ |switch, val|
210
- if syns.keys.include?(switch)
211
- syns[switch].each{ |key|
212
- hash[key] = val
213
- }
214
- end
215
- }
216
-
217
- # Get rid of leading "--" and "-" to make it easier to reference
218
- hash.each{ |key, value|
219
- if key[0,2] == '--'
220
- nkey = key.sub('--', '')
221
- else
222
- nkey = key.sub('-', '')
223
- end
224
- hash.delete(key)
225
- hash[nkey] = value
226
- }
227
-
228
- hash
229
- end
230
-
231
- end
232
- end
1
+ module Getopt
2
+
3
+ REQUIRED = 0
4
+ BOOLEAN = 1
5
+ OPTIONAL = 2
6
+ INCREMENT = 3
7
+ NEGATABLE = 4
8
+ NUMERIC = 5
9
+
10
+ class Long
11
+ class Error < StandardError; end
12
+
13
+ VERSION = '1.3.7'
14
+
15
+ # Takes an array of switches. Each array consists of up to three
16
+ # elements that indicate the name and type of switch. Returns a hash
17
+ # containing each switch name, minus the '-', as a key. The value
18
+ # for each key depends on the type of switch and/or the value provided
19
+ # by the user.
20
+ #
21
+ # The long switch _must_ be provided. The short switch defaults to the
22
+ # first letter of the short switch. The default type is BOOLEAN.
23
+ #
24
+ # Example:
25
+ #
26
+ # opts = Getopt::Long.getopts(
27
+ # ["--debug"],
28
+ # ["--verbose", "-v"],
29
+ # ["--level", "-l", NUMERIC]
30
+ # )
31
+ #
32
+ # See the README file for more information.
33
+ #
34
+ def self.getopts(*switches)
35
+ if switches.empty?
36
+ raise ArgumentError, "no switches provided"
37
+ end
38
+
39
+ hash = {} # Hash returned to user
40
+ valid = [] # Tracks valid switches
41
+ types = {} # Tracks argument types
42
+ syns = {} # Tracks long and short arguments, or multiple shorts
43
+
44
+ # If a string is passed, split it and convert it to an array of arrays
45
+ if switches.first.kind_of?(String)
46
+ switches = switches.join.split
47
+ switches.map!{ |switch| switch = [switch] }
48
+ end
49
+
50
+ # Set our list of valid switches, and proper types for each switch
51
+ switches.each{ |switch|
52
+ valid.push(switch[0]) # Set valid long switches
53
+
54
+ # Set type for long switch, default to BOOLEAN.
55
+ if switch[1].kind_of?(Fixnum)
56
+ switch[2] = switch[1]
57
+ types[switch[0]] = switch[2]
58
+ switch[1] = switch[0][1..2]
59
+ else
60
+ switch[2] ||= BOOLEAN
61
+ types[switch[0]] = switch[2]
62
+ switch[1] ||= switch[0][1..2]
63
+ end
64
+
65
+ # Create synonym hash. Default to first char of long switch for
66
+ # short switch, e.g. "--verbose" creates a "-v" synonym. The same
67
+ # synonym can only be used once - first one wins.
68
+ syns[switch[0]] = switch[1] unless syns[switch[1]]
69
+ syns[switch[1]] = switch[0] unless syns[switch[1]]
70
+
71
+ switch[1].each{ |char|
72
+ types[char] = switch[2] # Set type for short switch
73
+ valid.push(char) # Set valid short switches
74
+ }
75
+ }
76
+
77
+ re_long = /^(--\w+[-\w+]*)?$/
78
+ re_short = /^(-\w)$/
79
+ re_long_eq = /^(--\w+[-\w+]*)?=(.*?)$|(-\w?)=(.*?)$/
80
+ re_short_sq = /^(-\w)(\S+?)$/
81
+
82
+ ARGV.each_with_index{ |opt, index|
83
+
84
+ # Allow either -x -v or -xv style for single char args
85
+ if re_short_sq.match(opt)
86
+ chars = opt.split("")[1..-1].map{ |s| s = "-#{s}" }
87
+
88
+ chars.each_with_index{ |char, i|
89
+ unless valid.include?(char)
90
+ raise Error, "invalid switch '#{char}'"
91
+ end
92
+
93
+ # Grab the next arg if the switch takes a required arg
94
+ if types[char] == REQUIRED
95
+ # Deal with a argument squished up against switch
96
+ if chars[i+1]
97
+ arg = chars[i+1..-1].join.tr("-","")
98
+ ARGV.push(char, arg)
99
+ break
100
+ else
101
+ arg = ARGV.delete_at(index+1)
102
+ if arg.nil? || valid.include?(arg) # Minor cheat here
103
+ err = "no value provided for required argument '#{char}'"
104
+ raise Error, err
105
+ end
106
+ ARGV.push(char, arg)
107
+ end
108
+ elsif types[char] == OPTIONAL
109
+ if chars[i+1] && !valid.include?(chars[i+1])
110
+ arg = chars[i+1..-1].join.tr("-","")
111
+ ARGV.push(char, arg)
112
+ break
113
+ elsif
114
+ if ARGV[index+1] && !valid.include?(ARGV[index+1])
115
+ arg = ARGV.delete_at(index+1)
116
+ ARGV.push(char, arg)
117
+ end
118
+ else
119
+ ARGV.push(char)
120
+ end
121
+ else
122
+ ARGV.push(char)
123
+ end
124
+ }
125
+ next
126
+ end
127
+
128
+ if match = re_long.match(opt) || match = re_short.match(opt)
129
+ switch = match.captures.first
130
+ end
131
+
132
+ if match = re_long_eq.match(opt)
133
+ switch, value = match.captures.compact
134
+ ARGV.push(switch, value)
135
+ next
136
+ end
137
+
138
+ # Make sure that all the switches are valid. If 'switch' isn't
139
+ # defined at this point, it means an option was passed without
140
+ # a preceding switch, e.g. --option foo bar.
141
+ unless valid.include?(switch)
142
+ switch ||= opt
143
+ raise Error, "invalid switch '#{switch}'"
144
+ end
145
+
146
+ # Required arguments
147
+ if types[switch] == REQUIRED
148
+ nextval = ARGV[index+1]
149
+
150
+ # Make sure there's a value for mandatory arguments
151
+ if nextval.nil?
152
+ err = "no value provided for required argument '#{switch}'"
153
+ raise Error, err
154
+ end
155
+
156
+ # If there is a value, make sure it's not another switch
157
+ if valid.include?(nextval)
158
+ err = "cannot pass switch '#{nextval}' as an argument"
159
+ raise Error, err
160
+ end
161
+
162
+ # If the same option appears more than once, put the values
163
+ # in array.
164
+ if hash[switch]
165
+ hash[switch] = [hash[switch], nextval].flatten
166
+ else
167
+ hash[switch] = nextval
168
+ end
169
+ ARGV.delete_at(index+1)
170
+ end
171
+
172
+ # For boolean arguments set the switch's value to true.
173
+ if types[switch] == BOOLEAN
174
+ if hash.has_key?(switch)
175
+ raise Error, "boolean switch already set"
176
+ end
177
+ hash[switch] = true
178
+ end
179
+
180
+ # For increment arguments, set the switch's value to 0, or
181
+ # increment it by one if it already exists.
182
+ if types[switch] == INCREMENT
183
+ if hash.has_key?(switch)
184
+ hash[switch] += 1
185
+ else
186
+ hash[switch] = 1
187
+ end
188
+ end
189
+
190
+ # For optional argument, there may be an argument. If so, it
191
+ # cannot be another switch. If not, it is set to true.
192
+ if types[switch] == OPTIONAL
193
+ nextval = ARGV[index+1]
194
+ if valid.include?(nextval)
195
+ hash[switch] = true
196
+ else
197
+ hash[switch] = nextval
198
+ ARGV.delete_at(index+1)
199
+ end
200
+ end
201
+ }
202
+
203
+ # Set synonymous switches to the same value, e.g. if -t is a synonym
204
+ # for --test, and the user passes "--test", then set "-t" to the same
205
+ # value that "--test" was set to.
206
+ #
207
+ # This allows users to refer to the long or short switch and get
208
+ # the same value
209
+ hash.each{ |switch, val|
210
+ if syns.keys.include?(switch)
211
+ syns[switch].each{ |key|
212
+ hash[key] = val
213
+ }
214
+ end
215
+ }
216
+
217
+ # Get rid of leading "--" and "-" to make it easier to reference
218
+ hash.each{ |key, value|
219
+ if key =~ /^-/
220
+ if key[0,2] == '--'
221
+ nkey = key.sub('--', '')
222
+ else
223
+ nkey = key.sub('-', '')
224
+ end
225
+ hash.delete(key)
226
+ hash[nkey] = value
227
+ end
228
+ }
229
+
230
+ hash
231
+ end
232
+
233
+ end
234
+ end