yaggo 1.5.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2729fc31583ff7592035aba2520244e2f75aad7b
4
+ data.tar.gz: 9b94b60df60cd15b396811b062884230657af54b
5
+ SHA512:
6
+ metadata.gz: 4f74b6efeaca8b74f1e4dc5149d3d01d0df3daca7f40fa5635aa815f72b35298bfbee07a6b2b990ca45a31cc3a4f018437cfc47339ee7c71190e1008ed8a1913
7
+ data.tar.gz: aee821d6b32ebec7af2e94091341f4ef7dccfdccad5d9c59445285f116075cf15d0da67ee3f1e63e4a5b68881d9db7850878f99fcff675018e2aa9e909443b38
@@ -0,0 +1,107 @@
1
+ # What is yaggo?
2
+
3
+ Yaggo is a tool to generate command line parsers for C++. Yaggo stands
4
+ for "Yet Another GenGetOpt" and is inspired by [GNU Gengetopt](https://www.gnu.org/software/gengetopt/gengetopt.html).
5
+
6
+ It reads a configuration file describing the switches and argument for
7
+ a C++ program and it generates one header file that parses the command
8
+ line using getopt_long(3). See the Example section below for more details.
9
+
10
+ # Installation
11
+
12
+ ## Quick and easy
13
+
14
+ Download the standalone script from the [release](https://github.com/gmarcais/yaggo/releases)
15
+ and copy it into a directory in your PATH (e.g. `~/bin`)
16
+
17
+ From the source tree, the same is achieved with:
18
+
19
+ ```Shell
20
+ make DEST=$HOME/bin
21
+ ```
22
+
23
+ ## As a gem
24
+
25
+ Download the gem from the [release](https://github.com/gmarcais/yaggo/releases) and install it
26
+ with `sudo gem install ./yaggo-1.5.8.gem` (adjust the version!).
27
+
28
+ Similarly, from the source tree, first generate the gem
29
+ and then install it. For example here with version 1.5.3:
30
+
31
+ ```Shell
32
+ rake gem
33
+ sudo gem install ./pkg/yaggo-1.5.3.gem
34
+ ```
35
+
36
+ # Documentation
37
+
38
+ After installation, documentation is available with `yaggo --man`.
39
+
40
+ # Simple example
41
+
42
+ Given the following configuration file 'parser.yaggo':
43
+
44
+ ```Ruby
45
+ purpose "Demonstrate yaggo capabilities"
46
+ description "This simple configuration file shows some of the capabilities of yaggo.
47
+ This is supposed to be a longer description of the program.
48
+ "
49
+
50
+ option("f", "flag") {
51
+ description "This is a flag"
52
+ off
53
+ }
54
+ option("i", "int") {
55
+ description "This take an integer"
56
+ int
57
+ default 20
58
+ }
59
+ arg("path") {
60
+ description "Path to file"
61
+ c_string
62
+ }
63
+ ```
64
+
65
+ The following C++ program ('parser.cc') does switch parsing, generate
66
+ appropriate errors and has an automatically generated help (accessible
67
+ with '-h' or '--help').
68
+
69
+ ```C
70
+ #include <iostream>
71
+ #include "parser.hpp"
72
+
73
+ int main(int argc, char* argv[]) {
74
+ parser args(argc, argv); // Does all the parsing
75
+
76
+ std::cout << "--flag " << (args.flag_flag ? "not passed" : "passed") << "\n"
77
+ << "--int: " << args.int_arg << "\n"
78
+ << "path: " << args.path_arg << "\n";
79
+
80
+ return 0;
81
+ }
82
+ ```
83
+
84
+ All of this is compiled with:
85
+
86
+ ```Shell
87
+ yaggo parser.yaggo
88
+ g++ -o parser parser.cc
89
+ ```
90
+
91
+ Then, './parser --help' returns:
92
+
93
+ ```
94
+ Usage: parser [options] path:string
95
+
96
+ Demonstrate yaggo capabilities
97
+
98
+ This simple configuration file shows some of the capabilities of yaggo.
99
+ This is supposed to be a longer description of the program.
100
+
101
+ Options (default value in (), *required):
102
+ -f, --flag This is a flag (false)
103
+ -i, --int=int This take an integer (20)
104
+ -U, --usage Usage
105
+ -h, --help This message
106
+ -V, --version Version
107
+ ```
@@ -0,0 +1,45 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ def create_binary src, dest
4
+ to_load = []
5
+ loaded = {}
6
+ open(dest, "w", 0755) do |wfd|
7
+ wfd.puts(<<'EOS')
8
+ #! /usr/bin/env ruby
9
+
10
+ if !$load_self
11
+ $load_self = true
12
+ load(__FILE__)
13
+ main
14
+ exit(0)
15
+ end
16
+
17
+ EOS
18
+
19
+ open(src, "r") do |rfd|
20
+ rfd.each_line { |l|
21
+ if l =~ /^\s*require\s+['"]yaggo\/(\w+)['"]\s*$/
22
+ to_load << $1
23
+ else
24
+ wfd.print(l)
25
+ end
26
+ }
27
+ end
28
+
29
+ to_load.each { |f|
30
+ next if loaded[f]
31
+ wfd.puts("", "# Loading yaggo/#{f}", "")
32
+ open(File.join("lib", "yaggo", f + ".rb"), "r") { |nfd|
33
+ nfd.each_line { |l|
34
+ wfd.print(l) unless l =~ /^\s*require\s+['"]yaggo\/(\w+)['"]\s*$/
35
+ }
36
+ }
37
+ loaded[f] = true
38
+ }
39
+ end
40
+ end
41
+
42
+ if __FILE__ == $0
43
+ dest = ARGV.shift || "yaggo"
44
+ create_binary("lib/yaggo/main.rb", dest)
45
+ end
@@ -0,0 +1,22 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ # Yaggo. Yet Another GenGetOpt. Generate command line switch parsers
4
+ # using getopt_long.
5
+ # Copyright (C) 2011 Guillaume Marcais.
6
+ #
7
+ # This program is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as
9
+ # published by the Free Software Foundation, either version 3 of the
10
+ # License, or (at your option) any later version.
11
+ #
12
+ # This program is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
+ # General Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License
18
+ # along with this program. If not, see
19
+ # <http://www.gnu.org/licenses/>.
20
+
21
+ require 'yaggo/main.rb'
22
+ main
@@ -0,0 +1,573 @@
1
+ # This file is part of Yaggo.
2
+
3
+ # Yaggo is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+
8
+ # Yaggo is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with Yaggo. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+
17
+ ##############################
18
+ # Process an input files. Define the Domain Specific Language.
19
+ ##############################
20
+ $options = []
21
+ $opt_hash = {}
22
+ $args = []
23
+
24
+ class NoTarget
25
+ def description str; $description = str; end
26
+ def description= str; $description = str; end
27
+ def method_missing(m, *args)
28
+ raise "'#{m}' used outside of option or arg description"
29
+ end
30
+ end
31
+ $target = NoTarget.new
32
+
33
+ def output str; $output = str; end
34
+ def name str; $klass = str; end
35
+ def purpose str; $purpose = str; end
36
+ def package str; $package = str; end
37
+ def usage str; $usage = str; end
38
+ def text str; $after_text = str; end
39
+ def description str; $target.description = str; end
40
+ def version str; $version = str; end
41
+ def posix *args; $posix = true; end
42
+ def license str; $license = str; end
43
+ $global_variables = [:output, :name, :purpose, :package,
44
+ :description, :version, :license, :posix]
45
+ output = name = purpose = package = description = version = license = nil
46
+
47
+ # def set_type t
48
+ # raise "More than 1 type specified: '#{$target.type}' and '#{t}'" unless $target.type.nil?
49
+ # $target.type = t
50
+ # end
51
+
52
+ def int32; $target.type = :int32; end
53
+ def int64; $target.type = :int64; end
54
+ def uint32; $target.type = :uint32; end
55
+ def uint64; $target.type = :uint64; end
56
+ def int; $target.type = :int; end
57
+ def long; $target.type = :long; end
58
+ def double; $target.type = :double; end
59
+ def string; $target.type = :string; end
60
+ def c_string; $target.type = :c_string; end
61
+ def flag; $target.type = :flag; end
62
+ def enum(*argv); $target.type = :enum; $target.enum = argv; end
63
+
64
+ def suffix; $target.suffix = true; end
65
+ def required; $target.required = true; end
66
+ def hidden; $target.hidden = true; end
67
+ def secret; $target.secret = true; end
68
+ def on; $target.on; end
69
+ def off; $target.off; end
70
+ def no; $target.no; end
71
+ def default str; $target.default = str; end
72
+ def typestr str; $target.typestr = str; end
73
+ def multiple; $target.multiple = true; end
74
+ def at_least n; $target.at_least = n; end
75
+ def conflict *a; $target.conflict= a; end
76
+ def imply *a; $target.imply= a; end
77
+ def access *types; $target.access= types; end
78
+ # Define the following local variables and check their value after
79
+ # yielding the block to catch syntax such as default="value".
80
+ $option_variables = [:default, :typestr, :at_least]
81
+ default = typestr = at_least = nil
82
+ $main_binding = binding
83
+
84
+ def default_val(val, type, *argv)
85
+ case type
86
+ when :string, :c_string
87
+ "\"#{val || $type_default[type]}\""
88
+ when :uint32, :uint64, :int32, :int64, :int, :long, :double
89
+ val ? "(#{$type_to_C_type[type]})#{val}" : $type_default[type]
90
+ else
91
+ val.to_s || $type_default[type]
92
+ end
93
+ end
94
+
95
+ class BaseOptArg
96
+ def at_least=(n)
97
+ multiple = true
98
+ nb = case n
99
+ when Integer
100
+ n
101
+ when String
102
+ n =~ /^\d+$/ ? n.to_i : nil
103
+ else
104
+ nil
105
+ end
106
+ raise "Invalid minimum number for at_least (#{n})" if nb.nil?
107
+ self.multiple = true
108
+ @at_least = nb
109
+ end
110
+
111
+ def type=(t)
112
+ raise "More than 1 type specified: '#{type}' and '#{t}'" unless @type.nil? || @type == t
113
+ @type = t
114
+ end
115
+
116
+ def suffix=(t)
117
+ case type
118
+ when nil
119
+ raise "A numerical type must be specify before suffix"
120
+ when :flag, :string, :c_string
121
+ raise "Suffix is meaningless with the type #{type}"
122
+ end
123
+ @suffix = t
124
+ end
125
+
126
+ def access=(types)
127
+ types.all? { |t| ["read", "write", "exec"].include?(t) } or
128
+ raise "Invalid access type(s): #{types.join(", ")}"
129
+ @access_types = types
130
+ end
131
+
132
+ def check
133
+ if !@access_types.empty? && @type != :c_string
134
+ raise "Access checking is valid only with a path (a c_string)"
135
+ end
136
+ end
137
+ end
138
+
139
+ class Option < BaseOptArg
140
+ attr_accessor :description, :required, :typestr
141
+ attr_accessor :hidden, :secret, :conflict, :multiple, :access_types, :noflag
142
+ attr_reader :long, :short, :var, :type, :at_least, :default, :suffix, :enum
143
+ attr_reader :imply
144
+
145
+ def initialize(long, short)
146
+ @long, @short = long, short
147
+ @var = (@long || @short).gsub(/[^a-zA-Z0-9_]/, "_")
148
+ @type = nil
149
+ @no = false # Also generate the --noswitch for a flag
150
+ @default = nil
151
+ @suffix = false
152
+ @at_least = nil
153
+ @conflict = []
154
+ @enum = []
155
+ @imply = []
156
+ @access_types = []
157
+ end
158
+
159
+ def on
160
+ self.type = :flag
161
+ self.default = "true"
162
+ end
163
+
164
+ def off
165
+ self.type = :flag
166
+ self.default = "false"
167
+ end
168
+
169
+ def no
170
+ self.type = :flag
171
+ self.noflag = true
172
+ end
173
+
174
+ def tf_to_on_off v
175
+ case v
176
+ when "true"
177
+ "on"
178
+ when "false"
179
+ "off"
180
+ else
181
+ v
182
+ end
183
+ end
184
+
185
+ def convert_int(x, signed = true)
186
+ x =~ /^([+-]?\d+)([kMGTPE]?)$/ or return nil
187
+ v = $1.to_i
188
+ return nil if v < 0 && !signed
189
+ case $2
190
+ when "k"
191
+ v *= 1000
192
+ when "M"
193
+ v *= 1000_000
194
+ when "G"
195
+ v *= 1000_000_000
196
+ when "T"
197
+ v *= 1000_000_000_000
198
+ when "P"
199
+ v *= 1000_000_000_000_000
200
+ when "E"
201
+ v *= 1000_000_000_000_000_000
202
+ end
203
+ return v
204
+ end
205
+
206
+ def convert_double(x)
207
+ x =~ /^([+-]?[\d]+(?:\.\d*))?(?:([afpnumkMGTPE])|([eE][+-]?\d+))?$/ or return nil
208
+ v = "#{$1}#{$3}".to_f
209
+ case $2
210
+ when "a"
211
+ v *= 1e-18
212
+ when "f"
213
+ v *= 1e-15
214
+ when "p"
215
+ v *= 1e-12
216
+ when "n"
217
+ v *= 1e-9
218
+ when "u"
219
+ v *= 1e-6
220
+ when "m"
221
+ v *= 1e-3
222
+ when "k"
223
+ v *= 1e3
224
+ when "M"
225
+ v *= 1e6
226
+ when "G"
227
+ v *= 1e9
228
+ when "T"
229
+ v *= 1e12
230
+ when "P"
231
+ v *= 1e15
232
+ when "E"
233
+ v *= 1e18
234
+ end
235
+ return v
236
+ end
237
+
238
+ def default=(v)
239
+ type.nil? and raise "A type must be specified before defining a default value"
240
+ unless default.nil?
241
+ if type == :flag
242
+ v1, v2 = tf_to_on_off(default), tf_to_on_off(v)
243
+ else
244
+ v1, v2 = default, v
245
+ end
246
+ raise "More than 1 default value specified: '#{v1}' and '#{v2}'"
247
+ end
248
+ pref = "Option #{long || ""}|#{short || ""}:"
249
+ bv = v # Backup v for display
250
+ case @type
251
+ when nil
252
+ raise "#{pref} No type specified"
253
+ when :uint32, :uint64
254
+ (Integer === v && v >= 0) || (String === v && v = convert_int(v, false)) or
255
+ raise "#{pref} Invalid unsigned integer '#{bv}'"
256
+ when :int32, :int64, :int, :long
257
+ (Integer === v) || (String === v && v = convert_int(v, true)) or
258
+ raise "#{pref} Invalid integer #{bv}"
259
+ when :double
260
+ (Float === v) || (String === v && v = convert_double(v)) or
261
+ raise "#{pref} Invalid double #{bv}"
262
+ when :enum
263
+ v = v.to_i if v =~ /^\d+$/
264
+ case v
265
+ when Integer
266
+ (v >= 0 && v < @enum.size) or
267
+ raise "Default is out of range [0, #{@enum.size-1}]"
268
+ when String
269
+ nv = @enum.index(v) or
270
+ raise "Unknown constant '#{v}'. Should be one of { #{@enum.join(", ")} }"
271
+ v = nv
272
+ else
273
+ raise "Expected an Integer or a String"
274
+ end
275
+ end
276
+ @default = v
277
+ end
278
+
279
+ def enum=(*argv)
280
+ @type == :enum or raise "#{pref} Enum valid only for enum types."
281
+ @enum = argv.flatten
282
+ end
283
+
284
+ def conflict= a; @conflict += a.map { |x| x.gsub(/^-+/, "") }; end
285
+ def imply= a; @imply += a.map { |x| x.gsub(/^-+/, "") }; end
286
+
287
+ def check
288
+ pref = "Option #{long || ""}|#{short || ""}:"
289
+ raise "#{pref} No type specified" if type.nil?
290
+
291
+ if multiple
292
+ raise "#{pref} Multiple is meaningless with a flag" if type == :flag
293
+ raise "#{pref} An option marked multiple cannot have a default value" unless default.nil?
294
+ raise "#{pref} Multiple is incompatible with enum type" if type == :enum
295
+ end
296
+
297
+ if @type == :flag && noflag && !short.nil?
298
+ raise "#{pref} flag with 'no' option cannot have a short switch"
299
+ end
300
+
301
+ super
302
+
303
+ # case @type
304
+ # when nil
305
+ # raise "#{pref} No type specified"
306
+ # when :uint32, :uint64
307
+ # @default.nil? || @default =~ /^\d+$/ or
308
+ # raise "#{pref} Invalid unsigned integer #{@default}"
309
+ # when :int32, :int64, :int, :long
310
+ # @default.nil? || @default =~ /^[+-]?\d+$/ or
311
+ # raise "#{pref} Invalid integer #{@default}"
312
+ # when :double
313
+ # @default.nil? || @default =~ /^[+-]?[\d.]+([eE][+-]?\d+)?$/ or
314
+ # raise "#{pref} Invalid double #{@default}"
315
+ # when :flag
316
+ # raise "#{pref} A flag cannot be declared multiple" if @multiple
317
+ # raise "#{pref} Suffix is meaningless for a flag" if @suffix
318
+ # end
319
+ end
320
+
321
+ def static_decl
322
+ a = []
323
+ if @type == :enum
324
+ a << "struct #{@var} {"
325
+ a << " enum { #{@enum.map { |x| x.gsub(/[^a-zA-Z0-9_]/, "_") }.join(", ")} };"
326
+ a << " static const char* const strs[#{@enum.size + 1}];"
327
+ a << "};"
328
+ end
329
+ a
330
+ end
331
+
332
+ def var_decl
333
+ if @type == :flag
334
+ ["#{"bool".ljust($typejust)} #{@var}_flag;"]
335
+ else
336
+ a = []
337
+ if @multiple
338
+ c_type = "::std::vector<#{$type_to_C_type[@type]}>"
339
+ a << (c_type.ljust($typejust) + " #{@var}_arg;")
340
+ a << ("typedef #{c_type}::iterator #{@var}_arg_it;")
341
+ a << ("typedef #{c_type}::const_iterator #{@var}_arg_const_it;")
342
+ else
343
+ a << "#{$type_to_C_type[@type].ljust($typejust)} #{@var}_arg;"
344
+ end
345
+ a << "#{"bool".ljust($typejust)} #{@var}_given;"
346
+ end
347
+ end
348
+
349
+ def init
350
+ s = "#{@var}_#{@type == :flag ? "flag" : "arg"}("
351
+ s += default_val(@default, @type, @enum) unless @multiple
352
+ s += ")"
353
+ unless @type == :flag
354
+ s += ", #{@var}_given(false)"
355
+ end
356
+ s
357
+ end
358
+
359
+ def long_enum
360
+ return nil if !@short.nil?
361
+ res = [@var.upcase + "_OPT"]
362
+ if @type == :flag && noflag
363
+ res << "NO#{@var.upcase}_OPT"
364
+ end
365
+ res
366
+ end
367
+
368
+ def struct
369
+ res = ["{\"#{long}\", #{@type == :flag ? 0 : 1}, 0, #{@short ? "'" + @short + "'" : long_enum[0]}}"]
370
+ if @type == :flag && noflag
371
+ res << "{\"no#{long}\", 0, 0, #{long_enum()[1]}}"
372
+ end
373
+ res
374
+ end
375
+ def short_str
376
+ return nil if @short.nil?
377
+ @short + (@type == :flag ? "" : ":")
378
+ end
379
+ def switches
380
+ s = @short.nil? ? " " : "-#{@short}"
381
+ s += ", " unless @short.nil? || @long.nil?
382
+ unless @long.nil?
383
+ if @type == :flag && @noflag
384
+ s += "--[no]#{@long}"
385
+ else
386
+ s += "--#{@long}"
387
+ end
388
+ s += "=#{@typestr || dflt_typestr(@type, @enum)}" unless @type == :flag
389
+ end
390
+ s
391
+ end
392
+
393
+ def default_str
394
+ return @default unless @type == :enum
395
+ @enum[@default || 0]
396
+ end
397
+
398
+ def help
399
+ s = @required ? "*" : " "
400
+ @description ||= "Switch #{switches}"
401
+ s += @description.gsub(/"/, '\"') || ""
402
+ default = default_str
403
+ s += " (#{default})" unless default.nil?
404
+ s
405
+ end
406
+
407
+ def dump
408
+ case @type
409
+ when :flag
410
+ ["\"#{@var}_flag:\"", "#{@var}_flag"]
411
+ when :enum
412
+ ["\"#{@var}_given:\"", "#{@var}_given",
413
+ "\" #{@var}_arg:\"", "#{@var}_arg", '"|"', "#{@var}::strs[#{@var}_arg]"]
414
+ else
415
+ ["\"#{@var}_given:\"", "#{@var}_given",
416
+ "\" #{@var}_arg:\"", @multiple ? "vec_str(#{@var}_arg)" : "#{@var}_arg"]
417
+ end
418
+ end
419
+
420
+ def parse_arg(no = false)
421
+ a = @imply.map { |ios| "#{$opt_hash[ios].var}_flag = true;" }
422
+ a << "#{@var}_given = true;" unless @type == :flag
423
+ case @type
424
+ when :flag
425
+ if @noflag
426
+ a << ["#{@var}_flag = #{no ? "false" : "true"};"]
427
+ else
428
+ a << ["#{@var}_flag = #{@default == "true" ? "false" : "true"};"]
429
+ end
430
+ when :string
431
+ a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, false)});" : "#{@var}_arg.assign(optarg);")
432
+ when :c_string
433
+ a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, false)});" : "#{@var}_arg = optarg;")
434
+ when :uint32, :uint64, :int32, :int64, :int, :long, :double
435
+ a << (@multiple ? "#{@var}_arg.push_back(#{str_conv("optarg", @type, @suffix)});" : "#{@var}_arg = #{str_conv("optarg", @type, @suffix)};")
436
+ a << "CHECK_ERR(#{@type}_t, optarg, \"#{switches}\")"
437
+ when :enum
438
+ a << "#{@var}_arg = #{str_conv("optarg", @type, "#{@var}::strs")};"
439
+ a << "CHECK_ERR(#{@type}, optarg, \"#{switches}\")"
440
+ end
441
+ a
442
+ end
443
+ end
444
+
445
+ class Arg < BaseOptArg
446
+ attr_accessor :description, :type, :typestr, :multiple, :access_types
447
+ attr_reader :name, :at_least, :suffix, :var
448
+ def initialize(str)
449
+ @name = str
450
+ @var = @name.gsub(/[^a-zA-Z0-9_]/, "_")
451
+ @type = nil
452
+ @at_least = 0
453
+ @suffix = false
454
+ @access_types = []
455
+ end
456
+
457
+ def type=(t)
458
+ super
459
+ raise "An arg cannot be of type '#{t}'" if t == :flag
460
+ end
461
+
462
+ def on; raise "An arg cannot be a flag with default value on"; end
463
+ def off; raise "An arg cannot be a flag with default value off"; end
464
+
465
+ def default=(*args)
466
+ raise "An arg cannot have a default value (#{args[0]})"
467
+ end
468
+
469
+ def hidden=(*args)
470
+ raise "An arg cannot be marked hidden"
471
+ end
472
+
473
+ def secret=(*args)
474
+ raise "An arg cannot be marked secret"
475
+ end
476
+
477
+ def required=(*args)
478
+ raise "An arg cannot be marked required"
479
+ end
480
+
481
+ def check
482
+ super
483
+
484
+ pref = "Arg #{name}:"
485
+ raise "#{pref} No type specified" if type.nil?
486
+ end
487
+
488
+ def var_decl
489
+ if @multiple
490
+ c_type = "::std::vector<#{$type_to_C_type[@type]}>"
491
+ [c_type.ljust($typejust) + " #{@var}_arg;",
492
+ "typedef #{c_type}::iterator #{@var}_arg_it;",
493
+ "typedef #{c_type}::const_iterator #{@var}_arg_const_it;"]
494
+ else
495
+ ["#{$type_to_C_type[@type]}".ljust($typejust) + " #{@var}_arg;"]
496
+ end
497
+ end
498
+
499
+ def init
500
+ s = "#{@var}_arg("
501
+ s += default_val(@default, @type) unless @multiple
502
+ s += ")"
503
+ s
504
+ end
505
+
506
+ def dump
507
+ ["\"#{@var}_arg:\"",
508
+ @multiple ? "vec_str(#{@var}_arg)" : "#{@var}_arg"]
509
+ end
510
+
511
+ def parse_arg
512
+ a = []
513
+ off = ""
514
+ if @multiple
515
+ a << "for( ; optind < argc; ++optind) {"
516
+ a << " #{@var}_arg.push_back(#{str_conv("argv[optind]", @type, @suffix)});"
517
+ off = " "
518
+ else
519
+ a << "#{@var}_arg = #{str_conv("argv[optind]", @type, @suffix)};"
520
+ end
521
+ unless @type == :string || @type == :c_string
522
+ a << (off + "CHECK_ERR(#{@type}_t, argv[optind], \"#{@var}\")")
523
+ end
524
+ a << (@multiple ? "}" : "++optind;")
525
+ a
526
+ end
527
+ end
528
+
529
+ def option(name1, name2 = nil, &b)
530
+ long = short = nil
531
+ if name1 =~ /^--/ || name1.length >= 2
532
+ long, short = name1, name2
533
+ elsif !name2.nil? && (name2 =~ /^--/ || name2.length >= 2)
534
+ long, short = name2, name1
535
+ else
536
+ long, short = nil, name1
537
+ end
538
+
539
+ long.gsub!(/^--/, "") unless long.nil?
540
+ short.gsub!(/^-/, "") unless short.nil?
541
+ o = Option.new(long, short)
542
+ $options.each { |lo|
543
+ if (!long.nil? && lo.long == long) || (!short.nil? && lo.short == short)
544
+ raise "#{b.source_location.join(":")}: Option #{long}|#{short} conflicts with existing option #{lo.long}|#{lo.short}"
545
+ end
546
+ }
547
+ $options << o
548
+ $target = o
549
+ name = "Option #{long || ""}|#{short || ""}"
550
+ run_block(name, b)
551
+ $target = NoTarget.new
552
+ begin
553
+ o.check
554
+ rescue => e
555
+ raise "#{b.source_location.join(":")}: #{e.message}"
556
+ end
557
+ end
558
+
559
+ def arg(name, &b)
560
+ a = Arg.new(name)
561
+ $args.any? { |la| la.name == name } and
562
+ raise "#{b.source_location.join(":")}: Arg '#{name}' already exists"
563
+ $args << a
564
+ $target = a
565
+ name = "Arg #{name}"
566
+ run_block(name, b)
567
+ $target = NoTarget.new
568
+ begin
569
+ a.check
570
+ rescue => e
571
+ raise "#{b.source_location.join(":")}: #{e.message}"
572
+ end
573
+ end