getopt-declare 1.09.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/Getopt/Declare.rb +1638 -0
- data/lib/Getopt/DelimScanner.rb +1278 -0
- data/samples/cmdline_array.rb +25 -0
- data/samples/cmdline_basic.rb +31 -0
- data/samples/cmdline_code.rb +31 -0
- data/samples/cmdline_defer.rb +23 -0
- data/samples/cmdline_file.rb +38 -0
- data/samples/cmdline_inlines.rb +24 -0
- data/samples/cmdline_mid.rb +39 -0
- data/samples/cmdline_noargv.rb +29 -0
- data/samples/cmdline_parameters.rb +23 -0
- data/samples/cmdline_pvtype.rb +20 -0
- data/samples/cmdline_pvtype2.rb +20 -0
- data/samples/cmdline_regex.rb +27 -0
- data/samples/cmdline_singles.rb +28 -0
- data/samples/demo_cmdline.rb +70 -0
- data/samples/demo_csv.rb +49 -0
- data/samples/demo_interp.rb +44 -0
- data/samples/demo_shell.rb +37 -0
- metadata +55 -0
@@ -0,0 +1,1638 @@
|
|
1
|
+
#
|
2
|
+
# Getopt::Declare - Declaratively Expressed Command-Line Arguments via Regular Expressions
|
3
|
+
#
|
4
|
+
# Ruby port of Perl's Getopt::Declare, version 1.09,
|
5
|
+
# released May 21, 1999.
|
6
|
+
#
|
7
|
+
# $Release Version: 1.10 $
|
8
|
+
# $Date: 2004/01/25 07:02:48 $
|
9
|
+
# by Gonzalo Garramu�o
|
10
|
+
#
|
11
|
+
# For detailed instructions, see Declare.rdoc file
|
12
|
+
#
|
13
|
+
# Ruby Port:
|
14
|
+
# Copyright (c) 2004, Gonzalo Garramuno. All Rights Reserved.
|
15
|
+
# This package is free software. It may be used, redistributed
|
16
|
+
# and/or modified under the terms of the Perl Artistic License
|
17
|
+
# (see http://www.perl.com/perl/misc/Artistic.html)
|
18
|
+
#
|
19
|
+
# Original Perl Implementation:
|
20
|
+
# Damian Conway (damian@conway.org)
|
21
|
+
# Copyright (c) 1997-2000, Damian Conway. All Rights Reserved.
|
22
|
+
# This package is free software. It may be used, redistributed
|
23
|
+
# and/or modified under the terms of the Perl Artistic License
|
24
|
+
# (see http://www.perl.com/perl/misc/Artistic.html)
|
25
|
+
#
|
26
|
+
|
27
|
+
require "Getopt/DelimScanner"
|
28
|
+
|
29
|
+
# Verifies that code is valid Ruby code. returns false if not
|
30
|
+
def valid_syntax?(code, fname = 'parser_code')
|
31
|
+
eval("BEGIN {return true}\n#{code}", nil, fname, 0)
|
32
|
+
rescue Exception
|
33
|
+
false
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# An add-on to the String class
|
38
|
+
class String
|
39
|
+
# Expand all tabs to spaces
|
40
|
+
def expand_tabs!( tabstop = 8 )
|
41
|
+
while self.sub!(/(^|\n)([^\t\n]*)(\t+)/sex) { |f|
|
42
|
+
val = ( tabstop * "#$3".length() - ("#$2".length() % tabstop) )
|
43
|
+
"#$1#$2" + (" " * val)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return new string with all tabs set to spaces
|
50
|
+
def expand_tabs( tabstop = 8 )
|
51
|
+
h = self.dup
|
52
|
+
while h.sub!(/(^|\n)([^\t\n]*)(\t+)/sex) { |f|
|
53
|
+
val = ( tabstop * "#$3".length() - ("#$2".length() % tabstop) )
|
54
|
+
"#$1#$2" + (" " * val)
|
55
|
+
}
|
56
|
+
end
|
57
|
+
h
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# Regex for removing bracket directives
|
63
|
+
BracketDirectives =
|
64
|
+
/\[(?:ditto|tight|repeatable|required|mutex:.*|implies:.*|excludes:.*|requires:.*)\]/
|
65
|
+
|
66
|
+
|
67
|
+
module Getopt
|
68
|
+
|
69
|
+
# Main Class
|
70
|
+
class Declare
|
71
|
+
|
72
|
+
VERSION = '1.09.7'
|
73
|
+
|
74
|
+
# For debugging, set to 1 and it will output the ruby code as .CODE.rb
|
75
|
+
@@Declare_debug = 0
|
76
|
+
|
77
|
+
# Class used to handle the beginning of options
|
78
|
+
class StartOpt
|
79
|
+
|
80
|
+
# Returns regex used matching start of options
|
81
|
+
def matcher(g)
|
82
|
+
'(?:()'
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns code used
|
86
|
+
def code(*t)
|
87
|
+
''
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns how to cache code in class
|
91
|
+
def cachecode(a,b)
|
92
|
+
''
|
93
|
+
end
|
94
|
+
|
95
|
+
# Helps build regex that matches parameters of flags
|
96
|
+
def trailer
|
97
|
+
nil
|
98
|
+
end
|
99
|
+
|
100
|
+
# Helps build regex that matches parameters of flags
|
101
|
+
def ows(g)
|
102
|
+
g
|
103
|
+
end
|
104
|
+
end # StartOpt
|
105
|
+
|
106
|
+
|
107
|
+
# Class used to handle the ending of options
|
108
|
+
class EndOpt < StartOpt
|
109
|
+
# Returns regex used matching end of options
|
110
|
+
def matcher(g)
|
111
|
+
'())?' # for emacs coloring we need a '
|
112
|
+
end
|
113
|
+
end # EndOpt
|
114
|
+
|
115
|
+
|
116
|
+
# Class used to handle scalar (ie.non-array) parameters
|
117
|
+
class ScalarArg
|
118
|
+
|
119
|
+
@@stdtype = {}
|
120
|
+
|
121
|
+
# (re)set standard types
|
122
|
+
def ScalarArg._reset_stdtype()
|
123
|
+
@@stdtype = {
|
124
|
+
':i' => { 'pattern' => '(?:(?:%T[+-]?)%D+)' },
|
125
|
+
':n' => { 'pattern' => '(?:(?:%T[+-]?)(?:%D+(?:%T\.%D*)?' +
|
126
|
+
'(?:%T[eE]%D+)?|%T\.%D+(?:%T[eE]%D+)?))'
|
127
|
+
},
|
128
|
+
':s' => { 'pattern' => '(?:%T(?:\S|\0))+' },
|
129
|
+
':qs' => { 'pattern' => %q#"(?:\\"|[^"])*"|'(?:\\'|[^"])*|(?:%T(?:\S|\0))+#
|
130
|
+
},
|
131
|
+
':id' => { 'pattern' => '%T[a-zA-Z_](?:%T\w)*' },
|
132
|
+
':d' => { 'pattern' => '(?:%T(?:\S|\0))+',
|
133
|
+
'action' => %q%
|
134
|
+
reject( (_VAL_.nil? || !test(?d, _VAL_) ),
|
135
|
+
"in parameter '#{_PARAM_}' (\"#{_VAL_}\" is not a directory)")%
|
136
|
+
},
|
137
|
+
':if' => { 'pattern' => '%F(?:%T(?:\S|\0))+',
|
138
|
+
'action' => %q%
|
139
|
+
reject( (_VAL_.nil? || _VAL_ != "-" && !test(?r, _VAL_) ),
|
140
|
+
"in parameter '#{_PARAM_}' (file \"#{_VAL_}\" is not readable)")%
|
141
|
+
},
|
142
|
+
':of' => { 'pattern' => '%F(?:%T(?:\S|\0))+',
|
143
|
+
'action' => %q%
|
144
|
+
reject( (_VAL_.nil? || _VAL_ != "-" &&
|
145
|
+
test(?r, _VAL_) && !test(?w, _VAL_)),
|
146
|
+
"in parameter '#{_PARAM_}' (file \"#{_VAL_}\" is not writable)")%
|
147
|
+
},
|
148
|
+
'' => { 'pattern' => ':s', 'ind' => 1 },
|
149
|
+
|
150
|
+
':+i' => { 'pattern' => ':i',
|
151
|
+
'action' => %q%reject( _VAL_ <= 0,
|
152
|
+
"in parameter '#{_PARAM_}' (#{_VAL_} must be an integer greater than zero)")%,
|
153
|
+
'ind' => 1
|
154
|
+
},
|
155
|
+
|
156
|
+
':+n' => { 'pattern' => ':n',
|
157
|
+
'action' => %q%reject( _VAL_ <= 0.0),
|
158
|
+
"in parameter '#{_PARAM_}' (#{_VAL_} must be a number greater than zero)")%,
|
159
|
+
'ind' => 1
|
160
|
+
},
|
161
|
+
|
162
|
+
':0+i' => { 'pattern' => ':i',
|
163
|
+
'action' => %q%reject( _VAL_ < 0,
|
164
|
+
"in parameter '#{_PARAM_}' (#{_VAL_} must be an positive integer)")%,
|
165
|
+
'ind' => 1
|
166
|
+
},
|
167
|
+
|
168
|
+
':0+n' => {
|
169
|
+
'pattern' => ':n',
|
170
|
+
'action' => %q%reject( _VAL_ < 0,
|
171
|
+
"in parameter '#{_PARAM_}' (#{_VAL_} must be an positive number)")%,
|
172
|
+
'ind' => 1
|
173
|
+
},
|
174
|
+
}
|
175
|
+
|
176
|
+
end # _reset_stdtype
|
177
|
+
|
178
|
+
|
179
|
+
# Given a standard type name, return the corresponding regex
|
180
|
+
# pattern or nil
|
181
|
+
def stdtype(name)
|
182
|
+
seen = {}
|
183
|
+
while (!seen[name] && @@stdtype[name] && @@stdtype[name]['ind'])
|
184
|
+
seen[name] = 1; name = @@stdtype[name]['pattern']
|
185
|
+
end
|
186
|
+
|
187
|
+
return nil if seen[name] || !@@stdtype[name]
|
188
|
+
@@stdtype[name]['pattern']
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
# Given the name of a type, return its corresponding action(s)
|
193
|
+
def ScalarArg.stdactions(name)
|
194
|
+
seen = {}
|
195
|
+
actions = []
|
196
|
+
while (!seen[name] && @@stdtype[name] && @@stdtype[name]['ind'])
|
197
|
+
seen[name] = 1
|
198
|
+
if @@stdtype[name]['action']
|
199
|
+
actions.push( @@stdtype[name]['action'] )
|
200
|
+
end
|
201
|
+
name = @@stdtype[name]['pattern']
|
202
|
+
end
|
203
|
+
|
204
|
+
if @@stdtype[name] && @@stdtype[name]['action']
|
205
|
+
actions.push( @@stdtype[name]['action'] )
|
206
|
+
end
|
207
|
+
|
208
|
+
return actions
|
209
|
+
end
|
210
|
+
|
211
|
+
# Add a new (user defined) type to the standard types
|
212
|
+
def ScalarArg.addtype(abbrev, pattern, action, ref)
|
213
|
+
|
214
|
+
typeid = ":#{abbrev}"
|
215
|
+
unless (pattern =~ /\S/)
|
216
|
+
pattern = ":s"
|
217
|
+
ref = 1
|
218
|
+
end
|
219
|
+
|
220
|
+
@@stdtype[typeid] = {}
|
221
|
+
@@stdtype[typeid]['pattern'] = "(?:#{pattern})" if pattern && !ref
|
222
|
+
@@stdtype[typeid]['pattern'] = ":#{pattern}" if pattern && ref
|
223
|
+
@@stdtype[typeid]['action'] = action if action
|
224
|
+
@@stdtype[typeid]['ind'] = ref
|
225
|
+
|
226
|
+
end
|
227
|
+
|
228
|
+
attr :name
|
229
|
+
attr :type
|
230
|
+
attr :nows
|
231
|
+
|
232
|
+
|
233
|
+
# Constructor
|
234
|
+
def initialize(name, type, nows)
|
235
|
+
@name = name
|
236
|
+
@type = type
|
237
|
+
@nows = nows
|
238
|
+
end
|
239
|
+
|
240
|
+
# Create regexp to match parameter
|
241
|
+
def matcher(g)
|
242
|
+
trailing = g ? '(?!'+Regexp::quote(g)+')' : ''
|
243
|
+
|
244
|
+
# Find type in list of standard (and user) types
|
245
|
+
stdtype = stdtype(@type)
|
246
|
+
|
247
|
+
# Handle stdtypes that are specified as regex in parameter
|
248
|
+
if (!stdtype && @type =~ %r"\A:/([^/]+)/\Z" )
|
249
|
+
stdtype = "#$1"
|
250
|
+
end
|
251
|
+
|
252
|
+
if stdtype.nil?
|
253
|
+
raise "Error: bad type in Getopt::Declare parameter variable specification near '<#{@name}#{@type}>'\nValid types are:\n" + @@stdtype.keys.inspect
|
254
|
+
end
|
255
|
+
|
256
|
+
stdtype = stdtype.dup # make a copy, as we'll change it in place
|
257
|
+
stdtype.gsub!(/\%D/,"(?:#{trailing}\\d)")
|
258
|
+
stdtype.gsub!(/\%T/,trailing)
|
259
|
+
unless ( stdtype.sub!("\%F","") )
|
260
|
+
stdtype = Getopt::Declare::Arg::negflagpat() + stdtype
|
261
|
+
end
|
262
|
+
"(?:#{stdtype})"
|
263
|
+
end
|
264
|
+
|
265
|
+
# Return string with code to process parameter
|
266
|
+
def code(*t)
|
267
|
+
|
268
|
+
if t[0]
|
269
|
+
pos1 = t[0].to_s()
|
270
|
+
else
|
271
|
+
pos1 = '0'
|
272
|
+
end
|
273
|
+
|
274
|
+
c = conversion
|
275
|
+
if !c.empty?
|
276
|
+
c = "\n _VAL_ = _VAL_#{c}\n"
|
277
|
+
end
|
278
|
+
|
279
|
+
code = <<-EOS
|
280
|
+
_VAR_ = %q|<#{@name}>|
|
281
|
+
_VAL_ = @@m[#{pos1}]
|
282
|
+
_VAL_.tr!("\\0"," ") if _VAL_#{c}
|
283
|
+
EOS
|
284
|
+
|
285
|
+
actions = Getopt::Declare::ScalarArg::stdactions(@type)
|
286
|
+
|
287
|
+
for i in actions
|
288
|
+
next if i.nil?
|
289
|
+
# i.sub!(/(\s*\{)/, '\1 module '+t[1])
|
290
|
+
code << "
|
291
|
+
begin
|
292
|
+
#{i}
|
293
|
+
end
|
294
|
+
"
|
295
|
+
end
|
296
|
+
|
297
|
+
code << " #{@name} = _VAL_\n"
|
298
|
+
end
|
299
|
+
|
300
|
+
# Based on parameter type, default conversion to apply
|
301
|
+
def conversion
|
302
|
+
case @type
|
303
|
+
when /^\:(0)?(\+)?i$/
|
304
|
+
'.to_i'
|
305
|
+
when /^\:(0)?(\+)?n$/
|
306
|
+
'.to_f'
|
307
|
+
else
|
308
|
+
''
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# Return string with code to cache argument in Getopt::Declare's cache
|
313
|
+
def cachecode(ownerflag, itemcount)
|
314
|
+
if itemcount > 1
|
315
|
+
" @cache['#{ownerflag}']['<#{@name}>'] = #{@name}\n"
|
316
|
+
else
|
317
|
+
" @cache['#{ownerflag}'] = #{@name}\n"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Helps build regex that matches parameters of flags
|
322
|
+
def trailer
|
323
|
+
nil # MEANS TRAILING PARAMETER VARIABLE (in Perl,was '')
|
324
|
+
end
|
325
|
+
|
326
|
+
# Helps build regex that matches parameters of flags
|
327
|
+
# Wraps parameter passed for #$1, etc. matching
|
328
|
+
def ows(g)
|
329
|
+
return '(?:\s|\0)*(' + g + ')' unless @nows
|
330
|
+
'('+ g +')'
|
331
|
+
end
|
332
|
+
|
333
|
+
end # ScalarArg
|
334
|
+
|
335
|
+
|
336
|
+
# Class used to handle array arguments
|
337
|
+
class ArrayArg < ScalarArg
|
338
|
+
|
339
|
+
# Create regexp to match array
|
340
|
+
def matcher(g)
|
341
|
+
suffix = !g.nil? ? '\s+' : ''
|
342
|
+
scalar = super(g) # contains regex to match a scalar element
|
343
|
+
# we match one obligatory element, and one or more optionals ')*'
|
344
|
+
scalar + '(?:\s+' + scalar + ')*' + suffix
|
345
|
+
end
|
346
|
+
|
347
|
+
# Return string with code to process array parameter
|
348
|
+
def code(*t)
|
349
|
+
|
350
|
+
if t[0]
|
351
|
+
pos1 = t[0].to_s()
|
352
|
+
else
|
353
|
+
pos1 = '0'
|
354
|
+
end
|
355
|
+
|
356
|
+
code = <<-EOS
|
357
|
+
_VAR_ = %q|<#{@name}>|
|
358
|
+
_VAL_ = nil
|
359
|
+
#{@name} = (@@m[#{pos1}]||'').split(' ').map { |i|
|
360
|
+
i.tr("\\0", " ") }
|
361
|
+
EOS
|
362
|
+
|
363
|
+
# Handle conversion to proper type
|
364
|
+
c = conversion
|
365
|
+
if !c.empty?
|
366
|
+
code << " #{@name}.map! { |i| i#{c} }\n"
|
367
|
+
end
|
368
|
+
|
369
|
+
actions = Getopt::Declare::ScalarArg::stdactions(@type)
|
370
|
+
if actions.size > 0
|
371
|
+
code << " for _VAL_ in #{@name}\n"
|
372
|
+
for i in actions
|
373
|
+
code << " #{i}\n"
|
374
|
+
end
|
375
|
+
code << " end\n\n"
|
376
|
+
end
|
377
|
+
code
|
378
|
+
end
|
379
|
+
|
380
|
+
# Return string with code to cache array in Getopt::Declare's cache
|
381
|
+
def cachecode(ownerflag, itemcount)
|
382
|
+
if ( itemcount > 1 )
|
383
|
+
" @cache['#{ownerflag}']['<#{@name}>'] = [] unless @cache['#{ownerflag}']['<#{@name}>']
|
384
|
+
@cache['#{ownerflag}']['<#{@name}>'] = #{@name}\n"
|
385
|
+
else
|
386
|
+
" @cache['#{ownerflag}'] = #{@name}\n"
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end # ArrayArg
|
390
|
+
|
391
|
+
|
392
|
+
# Class used to handle punctuations (., -, etc.)
|
393
|
+
class Punctuator
|
394
|
+
|
395
|
+
# Constructor
|
396
|
+
def initialize(text, nows)
|
397
|
+
@text = text
|
398
|
+
@nows = nows
|
399
|
+
end
|
400
|
+
|
401
|
+
# Return regex that matches this punctuation
|
402
|
+
def matcher(g)
|
403
|
+
Arg::negflagpat() + Regexp::quote(@text)
|
404
|
+
end
|
405
|
+
|
406
|
+
# Return string with code to process punctuation
|
407
|
+
def code(*t)
|
408
|
+
|
409
|
+
if t[0]
|
410
|
+
pos1 = t[0].to_s()
|
411
|
+
else
|
412
|
+
pos1 = '0'
|
413
|
+
end
|
414
|
+
" if @@m[#{pos1}] && !@@m[#{pos1}].empty?
|
415
|
+
_PUNCT_['#{@text}'] = @@m[#{pos1}]
|
416
|
+
end
|
417
|
+
"
|
418
|
+
end
|
419
|
+
|
420
|
+
# Return string with code to cache punctuation in Getopt::Declare's cache
|
421
|
+
def cachecode(ownerflag, itemcount)
|
422
|
+
if itemcount > 1
|
423
|
+
" @cache['#{ownerflag}']['#{@text}'] = _PUNCT_['#{@text}']\n"
|
424
|
+
else
|
425
|
+
" @cache['#{ownerflag}'] = _PUNCT_['#{@text}'] || 1\n"
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
# Helps build regex that matches parameters of flags
|
430
|
+
def trailer
|
431
|
+
@text
|
432
|
+
end
|
433
|
+
|
434
|
+
# Helps build regex that matches parameters of flags
|
435
|
+
# Wraps parameter passed for #$1, etc. matching
|
436
|
+
def ows(g)
|
437
|
+
return '(?:\s|\0)*(' + g + ')' unless @nows
|
438
|
+
'(' + g + ')'
|
439
|
+
end #ows
|
440
|
+
|
441
|
+
end # Punctuator
|
442
|
+
|
443
|
+
|
444
|
+
# Class used to handle other arguments (flags, etc)
|
445
|
+
class Arg
|
446
|
+
|
447
|
+
@@nextid = 0
|
448
|
+
|
449
|
+
|
450
|
+
Helpcmd = %w( -help --help -Help --Help -HELP --HELP -h -H )
|
451
|
+
|
452
|
+
@@helpcmdH = {}
|
453
|
+
for i in Helpcmd; @@helpcmdH[i] = 1; end
|
454
|
+
|
455
|
+
def Arg.besthelp
|
456
|
+
for i in Helpcmd; return i if @@helpcmdH[i]; end
|
457
|
+
end
|
458
|
+
|
459
|
+
# Create regex of help flags based on help shortcuts left
|
460
|
+
def Arg.helppat
|
461
|
+
@@helpcmdH.keys().join('|')
|
462
|
+
end
|
463
|
+
|
464
|
+
|
465
|
+
Versioncmd = %w( -version --version -Version --Version
|
466
|
+
-VERSION --VERSION -v -V )
|
467
|
+
@@versioncmdH = {}
|
468
|
+
for i in Versioncmd; @@versioncmdH[i] = 1; end
|
469
|
+
|
470
|
+
def Arg.bestversion
|
471
|
+
for i in Versioncmd; return i if @@versioncmdH[i]; end
|
472
|
+
end
|
473
|
+
|
474
|
+
# Create regex of version flags based on help shortcuts left
|
475
|
+
def Arg.versionpat
|
476
|
+
@@versioncmdH.keys().join('|')
|
477
|
+
end
|
478
|
+
|
479
|
+
@@flags = []
|
480
|
+
@@posflagpat = nil
|
481
|
+
@@negflagpat = nil
|
482
|
+
|
483
|
+
# Return string with regex that avoids all flags in declaration
|
484
|
+
def Arg.negflagpat(*t)
|
485
|
+
if !@@negflagpat && @@flags
|
486
|
+
@@negflagpat = ( @@flags.map { |i|
|
487
|
+
"(?!" + Regexp::quote(i) + ")" } ).join('')
|
488
|
+
else
|
489
|
+
@@negflagpat
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
# Return string with regex that matches any of the flags in declaration
|
494
|
+
def Arg.posflagpat(*t)
|
495
|
+
if !@@posflagpat && @@flags
|
496
|
+
@@posflagpat = '(?:' + ( @@flags.map { |i|
|
497
|
+
Regexp::quote(i) } ).join('|') + ')'
|
498
|
+
else
|
499
|
+
@@posflagpat
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
attr_accessor :flag
|
504
|
+
attr_accessor :args
|
505
|
+
attr_accessor :actions
|
506
|
+
attr_accessor :ditto
|
507
|
+
attr_accessor :required
|
508
|
+
attr_accessor :requires
|
509
|
+
attr_accessor :id
|
510
|
+
attr_accessor :repeatable
|
511
|
+
attr_accessor :desc
|
512
|
+
|
513
|
+
|
514
|
+
|
515
|
+
# Constructor
|
516
|
+
def initialize(spec, desc, dittoflag)
|
517
|
+
first = 1
|
518
|
+
|
519
|
+
|
520
|
+
@@nextid += 1
|
521
|
+
@flag = ''
|
522
|
+
@args = []
|
523
|
+
@actions = []
|
524
|
+
@ditto = dittoflag
|
525
|
+
@required = 0
|
526
|
+
@requires = nil
|
527
|
+
@id = @@nextid
|
528
|
+
@desc = spec.dup
|
529
|
+
@items = 0
|
530
|
+
|
531
|
+
@desc.sub!(/\A\s*(.*?)\s*\Z/,'\1')
|
532
|
+
|
533
|
+
while spec && spec != ''
|
534
|
+
begin
|
535
|
+
|
536
|
+
# OPTIONAL
|
537
|
+
if spec.sub!( /\A(\s*)\[/, '\1' )
|
538
|
+
@args.push( StartOpt.new )
|
539
|
+
next
|
540
|
+
elsif spec.sub!(/\A\s*\]/,"")
|
541
|
+
@args.push( EndOpt.new )
|
542
|
+
next
|
543
|
+
end
|
544
|
+
|
545
|
+
# ARG
|
546
|
+
|
547
|
+
se = DelimScanner::new( spec )
|
548
|
+
tmp = se.scanBracketed('<>')
|
549
|
+
|
550
|
+
arg = nows = nil
|
551
|
+
arg, spec, nows = tmp[:match], tmp[:suffix], tmp[:prefix] if tmp
|
552
|
+
|
553
|
+
|
554
|
+
if arg
|
555
|
+
arg =~ /\A(\s*)(<)([a-zA-Z]\w*)(:[^>]+|)>/ or
|
556
|
+
raise "Error: bad Getopt::Declare parameter variable specification near '#{arg}'\n"
|
557
|
+
|
558
|
+
details = [ "#$3", "#$4", !first && !nows.length ] # NAME,TYPE,NOW
|
559
|
+
|
560
|
+
if spec && spec.sub!( /\A\.\.\./, "") # ARRAY ARG
|
561
|
+
@args.push( ArrayArg.new(*details) )
|
562
|
+
else # SCALAR ARG
|
563
|
+
@args.push( ScalarArg.new(*details) )
|
564
|
+
end
|
565
|
+
@items += 1
|
566
|
+
next
|
567
|
+
|
568
|
+
# PUNCTUATION
|
569
|
+
elsif spec.sub!( /\A(\s*)((\\.|[^\] \t\n\[<])+)/, "" )
|
570
|
+
ows, punct = [ "#$1", "#$2" ]
|
571
|
+
punct.gsub!( /\\(?!\\)(.)/, '\1' )
|
572
|
+
|
573
|
+
if first == 1
|
574
|
+
@flag = punct
|
575
|
+
@@flags.push( punct )
|
576
|
+
else
|
577
|
+
@args.push( Punctuator.new(punct,!ows.length()) )
|
578
|
+
@items += 1
|
579
|
+
end
|
580
|
+
|
581
|
+
else
|
582
|
+
break
|
583
|
+
end # if arg/spec.sub
|
584
|
+
ensure
|
585
|
+
first = 0
|
586
|
+
end
|
587
|
+
|
588
|
+
end # while
|
589
|
+
|
590
|
+
|
591
|
+
@@helpcmdH.delete(@flag) if @@helpcmdH.key?(@flag)
|
592
|
+
@@versioncmdH.delete(@flag) if @@versioncmdH.key?(@flag)
|
593
|
+
end # initialize
|
594
|
+
|
595
|
+
|
596
|
+
|
597
|
+
# Return String with code to parse this argument (ie. flag)
|
598
|
+
def code(*t)
|
599
|
+
|
600
|
+
owner = t[0]
|
601
|
+
mod = t[1]
|
602
|
+
|
603
|
+
|
604
|
+
code = "\n"
|
605
|
+
flag = @flag
|
606
|
+
clump = owner.clump
|
607
|
+
i = 0
|
608
|
+
nocase = (Getopt::Declare::_nocase() || @nocase ? 'i' : '')
|
609
|
+
|
610
|
+
code << " catch(:paramout) do\n"
|
611
|
+
code += (!@repeatable) ? " while !_FOUND_['" + name() + "']" :
|
612
|
+
" while 1"
|
613
|
+
if (flag && (clump==1 && flag !~ /\A[^a-z0-9]+[a-z0-9]\Z/i ||
|
614
|
+
(clump<3 && @args )))
|
615
|
+
code << " && !_lastprefix"
|
616
|
+
end
|
617
|
+
|
618
|
+
code << '
|
619
|
+
begin
|
620
|
+
catch(:param) do
|
621
|
+
_pos = _nextpos if _args
|
622
|
+
_PUNCT_ = {}
|
623
|
+
'
|
624
|
+
|
625
|
+
if flag
|
626
|
+
code << '
|
627
|
+
_args && _pos = gindex( _args, /\G(?:\s|\0)*' +
|
628
|
+
Regexp::quote(flag) + '/' + nocase + ", _pos) or throw(:paramout)
|
629
|
+
unless @_errormsg
|
630
|
+
@_errormsg = %q|incorrect specification of '" + flag + "' parameter|
|
631
|
+
end
|
632
|
+
"
|
633
|
+
elsif ( ScalarArg::stdtype(@args[0]['type'])||'') !~ /\%F/
|
634
|
+
code << %q%\n throw(:paramout) if @_errormsg\n%
|
635
|
+
end
|
636
|
+
|
637
|
+
|
638
|
+
code << "\n _PARAM_ = '" + self.name() + "'\n"
|
639
|
+
|
640
|
+
|
641
|
+
trailer = []
|
642
|
+
i = argcount = @args.length()-1
|
643
|
+
while i > 0
|
644
|
+
trailer[i-1] = @args[i].trailer()
|
645
|
+
trailer[i-1] = trailer[i] unless trailer[i-1]
|
646
|
+
i -= 1
|
647
|
+
end # while i
|
648
|
+
|
649
|
+
|
650
|
+
if @args
|
651
|
+
code << "\n"+' _args && _pos = gindex( _args, /\G'
|
652
|
+
|
653
|
+
0.upto( argcount ) { |i|
|
654
|
+
code << @args[i].ows(@args[i].matcher(trailer[i]))
|
655
|
+
}
|
656
|
+
|
657
|
+
code << '/x' + nocase + ", _pos ) or throw(:paramout)\n"
|
658
|
+
end # if @args
|
659
|
+
|
660
|
+
0.upto( argcount ) { |i|
|
661
|
+
code << @args[i].code(i,mod) #, $flag ????
|
662
|
+
}
|
663
|
+
|
664
|
+
if flag
|
665
|
+
mutexlist = owner.mutex[flag] ?
|
666
|
+
( owner.mutex[flag].map {|i| "'#{i}'"} ).join(',') : ''
|
667
|
+
|
668
|
+
code << "
|
669
|
+
if _invalid.has_key?('#{flag}')
|
670
|
+
@_errormsg = %q|parameter '#{flag}' not allowed with parameter '| + _invalid['#{flag}'] + %q|'|
|
671
|
+
throw(:paramout)
|
672
|
+
else
|
673
|
+
for i in [#{mutexlist}]
|
674
|
+
_invalid[i] = '#{flag}'
|
675
|
+
end
|
676
|
+
end #if/then
|
677
|
+
|
678
|
+
"
|
679
|
+
end
|
680
|
+
|
681
|
+
|
682
|
+
|
683
|
+
for action in @actions
|
684
|
+
#action.sub!( /(\s*\{)/, '\1 module '+mod ) # @TODO
|
685
|
+
code << "\n " + action + "\n"
|
686
|
+
end
|
687
|
+
|
688
|
+
if (flag && @items==0)
|
689
|
+
code << "\n @cache['#{flag}'] = '#{flag}'\n"
|
690
|
+
end
|
691
|
+
|
692
|
+
if @items > 1
|
693
|
+
code << " @cache['#{self.name}'] = {} unless @cache['#{self.name}']\n"
|
694
|
+
end
|
695
|
+
|
696
|
+
for subarg in @args
|
697
|
+
code << subarg.cachecode(self.name(),@items)
|
698
|
+
end
|
699
|
+
|
700
|
+
if flag =~ /\A([^a-z0-9]+)/i
|
701
|
+
code << ' _lastprefix = "'+ Regexp::quote("#$1") + '"' + "\n"
|
702
|
+
else
|
703
|
+
code << " _lastprefix = nil\n"
|
704
|
+
end
|
705
|
+
|
706
|
+
code << "
|
707
|
+
_FOUND_['"+ self.name() + "'] = 1
|
708
|
+
throw :arg if _pos > 0
|
709
|
+
_nextpos = _args.length()
|
710
|
+
throw :alldone
|
711
|
+
end # catch(:param)
|
712
|
+
end # begin
|
713
|
+
end # while
|
714
|
+
end # catch(:paramout)
|
715
|
+
"
|
716
|
+
|
717
|
+
code
|
718
|
+
end
|
719
|
+
|
720
|
+
# Return name of argument, which can be flag's name or variable's name
|
721
|
+
def name
|
722
|
+
if flag.nil? || flag.empty?
|
723
|
+
i = 0
|
724
|
+
i += 1 while !defined?(args[i].name)
|
725
|
+
"<#{(@args[i]).name}>"
|
726
|
+
else
|
727
|
+
@flag
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
end # Arg
|
732
|
+
|
733
|
+
|
734
|
+
private
|
735
|
+
|
736
|
+
# Check if global ignore case in regex is defined
|
737
|
+
def Declare._nocase(*t)
|
738
|
+
@@_nocase = t[0] if t[0]
|
739
|
+
@@_nocase
|
740
|
+
end
|
741
|
+
|
742
|
+
#
|
743
|
+
# This is an additional function added to the class to simulate Perl's
|
744
|
+
# pos() \G behavior and m///g
|
745
|
+
#
|
746
|
+
# It performs a regex match, and returns the last index position of the
|
747
|
+
# match or nil. On successive invocations, it allows doing regex matches
|
748
|
+
# NOT from the beginning of the string easily.
|
749
|
+
#
|
750
|
+
# Class Array @@m stores the list of matches, as #$1 and similar
|
751
|
+
# variables have short lifespan in ruby, unlike perl.
|
752
|
+
#
|
753
|
+
def gindex(str, re, pos)
|
754
|
+
@@m.clear()
|
755
|
+
if pos = str.index( re, pos )
|
756
|
+
l = $&.length() # length of match
|
757
|
+
if l > 0
|
758
|
+
@@m[0] = "#$1"
|
759
|
+
@@m[1] = "#$2"
|
760
|
+
@@m[2] = "#$3"
|
761
|
+
@@m[3] = "#$4"
|
762
|
+
@@m[4] = "#$5"
|
763
|
+
@@m[5] = "#$6"
|
764
|
+
@@m[6] = "#$7"
|
765
|
+
@@m[7] = "#$8"
|
766
|
+
@@m[8] = "#$9"
|
767
|
+
pos += l
|
768
|
+
end
|
769
|
+
end
|
770
|
+
pos
|
771
|
+
end
|
772
|
+
|
773
|
+
# Given an array or hash, flatten them to a string
|
774
|
+
def flatten(val, nested = nil)
|
775
|
+
case val
|
776
|
+
when Array
|
777
|
+
return val.map{ |i| flatten(i,1) }.join(" ")
|
778
|
+
when Hash
|
779
|
+
return val.keys().map{ |i| nested ||
|
780
|
+
i =~ /^-/ ? [i, flatten(val[i],1)] :
|
781
|
+
[flatten(val[i],1)] }.join(" ")
|
782
|
+
else
|
783
|
+
return val
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
# Read the next line from stdin
|
788
|
+
def _get_nextline
|
789
|
+
$stdin.readline
|
790
|
+
end
|
791
|
+
|
792
|
+
# For each file provided and found, read it in
|
793
|
+
def _load_sources( _get_nextline, files )
|
794
|
+
text = ''
|
795
|
+
found = []
|
796
|
+
|
797
|
+
for i in files
|
798
|
+
begin
|
799
|
+
f = File.open(i,"r")
|
800
|
+
rescue
|
801
|
+
next
|
802
|
+
end
|
803
|
+
|
804
|
+
if f.tty?
|
805
|
+
found.push( '<STDIN>' )
|
806
|
+
_get_nextline = method(:_get_nextline)
|
807
|
+
else
|
808
|
+
found.push( i );
|
809
|
+
t = f.readlines.join(' ')
|
810
|
+
t.tr!('\t\n',' ')
|
811
|
+
text += t
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
return nil unless found.size > 0
|
816
|
+
text = $stdin.readline if text.empty?
|
817
|
+
return [text, found.join(' or ')]
|
818
|
+
end
|
819
|
+
|
820
|
+
# Turn boolean flags into their ascii equivalents (have lower precedence)
|
821
|
+
def _enbool(expr)
|
822
|
+
expr.gsub!(/\s*\|\|\s*/," or ")
|
823
|
+
expr.gsub!(/\s*&&\s*/," and ")
|
824
|
+
expr.gsub!(/\s*!\s*/," not ")
|
825
|
+
expr
|
826
|
+
end
|
827
|
+
|
828
|
+
#
|
829
|
+
def _enfound(original)
|
830
|
+
expr = original.gsub(/((?:&&|\|\|)?\s*(?:[!(]\s*)*)([^ \t\n|&\)]+)/x,
|
831
|
+
'\1_FOUND_[\'\2\']')
|
832
|
+
|
833
|
+
if !valid_syntax?( expr )
|
834
|
+
raise "Error: bad condition in [requires: #{original}]\n"
|
835
|
+
end
|
836
|
+
expr
|
837
|
+
end
|
838
|
+
|
839
|
+
# Check parameter description for special options
|
840
|
+
def _infer(desc, arg, mutex)
|
841
|
+
_mutex(mutex, "#$1".split(' ')) while desc.sub!(/\[mutex:\s*(.*?)\]/i,"")
|
842
|
+
|
843
|
+
if desc =~ /\[no\s*case\]/i
|
844
|
+
if arg
|
845
|
+
arg.nocase = 1 #should it be _nocase
|
846
|
+
else
|
847
|
+
_nocase( 1 )
|
848
|
+
end
|
849
|
+
end
|
850
|
+
|
851
|
+
if !arg.nil?
|
852
|
+
if desc =~ /.*\[excludes:\s*(.*?)\]/i
|
853
|
+
_exclude(mutex, arg.name, ("#$1".split(' ')))
|
854
|
+
end
|
855
|
+
|
856
|
+
if desc =~ /.*\[requires:\s*(.*?)\]/i
|
857
|
+
arg.requires = "#$1"
|
858
|
+
end
|
859
|
+
|
860
|
+
arg.required = ( desc =~ /\[required\]/i )
|
861
|
+
|
862
|
+
arg.repeatable = ( desc =~ /\[repeatable\]/i )
|
863
|
+
end
|
864
|
+
|
865
|
+
_typedef(desc) while desc.sub!(/.*?\[pvtype:\s*/,"")
|
866
|
+
|
867
|
+
end
|
868
|
+
|
869
|
+
|
870
|
+
|
871
|
+
# Extract a new type from the description and add it to the list
|
872
|
+
# of standard types
|
873
|
+
def _typedef(desc)
|
874
|
+
se = DelimScanner::new( desc )
|
875
|
+
tmp = se.scanQuotelike()
|
876
|
+
|
877
|
+
name = nil
|
878
|
+
name, desc = tmp[:delimText], tmp[:suffix] if tmp
|
879
|
+
|
880
|
+
unless name
|
881
|
+
desc.sub!(/\A\s*([^\] \t\n]+)/,"") and name = "#$1"
|
882
|
+
end
|
883
|
+
|
884
|
+
raise "Error: bad type directive (missing type name): [pvtype: " +
|
885
|
+
desc[0,desc.index(']')||20] + "....\n" unless name
|
886
|
+
|
887
|
+
se = DelimScanner::new( desc )
|
888
|
+
tmp = se.scanQuotelike('\s*:?\s*')
|
889
|
+
|
890
|
+
# @TODO What is element 2 of extract_quotelike? :trail is a fake here
|
891
|
+
# pat,desc,ind = (extract_quotelike(desc,'\s*:?\s*'))[5,1,2]
|
892
|
+
pat = ind = nil
|
893
|
+
pat, desc, ind = tmp[:match], tmp[:suffix], tmp[:prefix] if tmp
|
894
|
+
pat = pat[1..-2] if pat
|
895
|
+
|
896
|
+
unless pat
|
897
|
+
desc.sub!(/\A\s*(:?)\s*([^\] \t\n]+)/,"") and pat = "#$2" and ind = "#$1"
|
898
|
+
end
|
899
|
+
|
900
|
+
pat = '' unless pat
|
901
|
+
|
902
|
+
|
903
|
+
se = DelimScanner::new( desc )
|
904
|
+
action = se.extractCodeblock() || ''
|
905
|
+
|
906
|
+
desc.sub!( Regexp::quote(action).to_re, '' )
|
907
|
+
action = action[1..-2]
|
908
|
+
|
909
|
+
raise "Error: bad type directive (expected closing ']' but found " +
|
910
|
+
"'#$1' instead): [pvtype: $name " + (pat ? "/$#{pat}/" : '') +
|
911
|
+
" $#{action} #$1#$2....\n" if desc =~ /\A\s*([^\] \t\n])(\S*)/
|
912
|
+
|
913
|
+
|
914
|
+
Getopt::Declare::ScalarArg::addtype(name,pat,action,ind=~/:/)
|
915
|
+
end
|
916
|
+
|
917
|
+
# Handle quote replacements for [ditto] flag
|
918
|
+
def _ditto(originalflag, originaldesc, extra)
|
919
|
+
if originaldesc =~ /\n.*\n/
|
920
|
+
originaldesc = "Same as #{originalflag} "
|
921
|
+
else
|
922
|
+
originaldesc.chomp()
|
923
|
+
originaldesc.gsub!(/\S/,'"')
|
924
|
+
while originaldesc.gsub!(/"("+)"/,' \1 ')
|
925
|
+
end
|
926
|
+
originaldesc.gsub!(/""/,'" ')
|
927
|
+
end
|
928
|
+
|
929
|
+
"#{originaldesc}#{extra}"
|
930
|
+
end
|
931
|
+
|
932
|
+
# Check mutex conditions
|
933
|
+
def _mutex(mref, mutexlist)
|
934
|
+
for flag in mutexlist
|
935
|
+
mref[flag] = [] unless mref[flag]
|
936
|
+
for otherflag in mutexlist
|
937
|
+
next if flag == otherflag
|
938
|
+
mref[flag].push( otherflag )
|
939
|
+
end
|
940
|
+
end
|
941
|
+
end
|
942
|
+
|
943
|
+
# Check exclude conditions
|
944
|
+
def _exclude(mref, excluded, mutexlist)
|
945
|
+
for flag in mutexlist
|
946
|
+
unless flag == excluded
|
947
|
+
mref[flag] = [] unless mref[flag]
|
948
|
+
mref[excluded] = [] unless mref[excluded]
|
949
|
+
mref[excluded].push( flag )
|
950
|
+
mref[flag].push( excluded )
|
951
|
+
end
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
public
|
956
|
+
|
957
|
+
# Constructor
|
958
|
+
def initialize(*opts)
|
959
|
+
|
960
|
+
# HANDLE SHORT-CIRCUITS
|
961
|
+
return if opts.length()==2 && (!opts[1] || opts[1] == '-SKIP')
|
962
|
+
|
963
|
+
grammar, source = opts
|
964
|
+
|
965
|
+
if grammar.nil?
|
966
|
+
raise "Error: No grammar description provided."
|
967
|
+
end
|
968
|
+
|
969
|
+
### REMOVED PREDEF GRAMMAR AS IT WAS NOT DOCUMENTED NOR
|
970
|
+
### WORKING IN PERL'S Declare.pm VERSION.
|
971
|
+
|
972
|
+
# PRESERVE ESCAPED '['s
|
973
|
+
grammar.gsub!(/\\\[/,"\255")
|
974
|
+
|
975
|
+
# MAKE SURE GRAMMAR ENDS WITH A NEWLINE.
|
976
|
+
grammar.sub!(/([^\n])\Z/,'\1'+"\n")
|
977
|
+
|
978
|
+
@usage = grammar.dup
|
979
|
+
|
980
|
+
# SET-UP
|
981
|
+
i = grammar
|
982
|
+
_args = []
|
983
|
+
_mutex = {}
|
984
|
+
_strict = false
|
985
|
+
_all_repeatable = false
|
986
|
+
_lastdesc = nil
|
987
|
+
Getopt::Declare::_nocase( 0 )
|
988
|
+
Getopt::Declare::ScalarArg::_reset_stdtype()
|
989
|
+
|
990
|
+
# CONSTRUCT GRAMMAR
|
991
|
+
while i.length > 0
|
992
|
+
|
993
|
+
# COMMENT:
|
994
|
+
i.sub!(/\A[ \t]*#.*\n/,"") and next
|
995
|
+
|
996
|
+
# TYPE DIRECTIVE:
|
997
|
+
se = DelimScanner::new( i )
|
998
|
+
|
999
|
+
if i =~ /\A\s*\[pvtype:/
|
1000
|
+
_action = se.extractBracketed("[")
|
1001
|
+
if _action
|
1002
|
+
i.sub!( Regexp::quote( _action ).to_re, "" ) ### @GGA: added
|
1003
|
+
i.sub!(/\A[ \t]*\n/,"") ### @GGA: added
|
1004
|
+
_action.sub!(/.*?\[pvtype:\s*/,"")
|
1005
|
+
_typedef(_action)
|
1006
|
+
next
|
1007
|
+
end # if
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
# ACTION
|
1011
|
+
codeblockDelimiters = {
|
1012
|
+
'{' => '}',
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
|
1016
|
+
_action = se.extractCodeblock(codeblockDelimiters)
|
1017
|
+
if _action
|
1018
|
+
i.sub!( Regexp::quote(_action ).to_re, "" )
|
1019
|
+
i.sub!(/\A[ \t]*\n/,"")
|
1020
|
+
_action = _action[1..-2]
|
1021
|
+
|
1022
|
+
if !valid_syntax?( _action )
|
1023
|
+
raise "Error: bad action in Getopt::Declare specification:" +
|
1024
|
+
"\n\n#{_action}\n\n\n"
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
if _args.length() == 0
|
1028
|
+
raise "Error: unattached action in Getopt::Declare specification:\n#{_action}\n" +
|
1029
|
+
"\t(did you forget the tab after the preceding parameter specification?)\n"
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
_args.last().actions.push( _action )
|
1033
|
+
next
|
1034
|
+
elsif i =~ /\A(\s*[{].*)/
|
1035
|
+
raise "Error: incomplete action in Getopt::Declare specification:\n$1.....\n" +
|
1036
|
+
"\t(did you forget a closing '}'?)\n"
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
# ARG + DESC:
|
1040
|
+
if i.sub!(/\A(.*?\S.*?)(\t.*\n)/,"")
|
1041
|
+
spec = "#$1"
|
1042
|
+
desc = "#$2"
|
1043
|
+
_strict ||= desc =~ /\[strict\]/
|
1044
|
+
|
1045
|
+
desc += "#$1" while i.sub!(/\A((?![ \t]*(\{|\n)|.*?\S.*?\t.*?\S).*?\S.*\n)/,"")
|
1046
|
+
|
1047
|
+
ditto = nil
|
1048
|
+
ditto = 1 if _lastdesc and desc.sub!(/\A\s*\[ditto\]/,_lastdesc)
|
1049
|
+
_lastdesc = desc
|
1050
|
+
|
1051
|
+
arg = Arg.new(spec,desc,ditto)
|
1052
|
+
_args.push( arg )
|
1053
|
+
|
1054
|
+
_infer(desc, arg, _mutex)
|
1055
|
+
next
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
# OTHERWISE: DECORATION
|
1059
|
+
i.sub!(/((?:(?!\[pvtype:).)*)(\n|(?=\[pvtype:))/,"")
|
1060
|
+
decorator = "#$1"
|
1061
|
+
_strict ||= decorator =~ /\[strict\]/
|
1062
|
+
_infer(decorator, nil, _mutex)
|
1063
|
+
|
1064
|
+
_all_repeatable = true if decorator =~ /\[repeatable\]/
|
1065
|
+
|
1066
|
+
end # while i.length
|
1067
|
+
|
1068
|
+
|
1069
|
+
_lastactions = nil
|
1070
|
+
for i in _args
|
1071
|
+
if _lastactions && i.ditto && i.actions.size == 0
|
1072
|
+
i.actions = _lastactions
|
1073
|
+
else
|
1074
|
+
_lastactions = i.actions
|
1075
|
+
end
|
1076
|
+
|
1077
|
+
if _all_repeatable
|
1078
|
+
i.repeatable = 1
|
1079
|
+
end
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
# Sort flags based on criteria described in docs
|
1083
|
+
_args = _args.sort() { |a,b|
|
1084
|
+
( b.flag.length() <=> a.flag.length() ) or
|
1085
|
+
( b.flag == a.flag and ( b.args.length() <=> a.args.length() ) ) or
|
1086
|
+
( a.id <=> b.id )
|
1087
|
+
}
|
1088
|
+
|
1089
|
+
# Handle clump
|
1090
|
+
clump = (@usage =~ /\[cluster:\s*none\s*\]/i) ? 0 :
|
1091
|
+
(@usage =~ /\[cluster:\s*singles?\s*\]/i) ? 1 :
|
1092
|
+
(@usage =~ /\[cluster:\s*flags?\s*\]/i) ? 2 :
|
1093
|
+
(@usage =~ /\[cluster:\s*any\s*\]/i) ? 3 :
|
1094
|
+
(@usage =~ /\[cluster:(.*)\s*\]/i) ? "r" : 3
|
1095
|
+
raise "Error: unknown clustering mode: [cluster:#$1]\n" if clump == "r"
|
1096
|
+
|
1097
|
+
# CONSTRUCT OBJECT ITSELF
|
1098
|
+
@args = _args
|
1099
|
+
@mutex = _mutex
|
1100
|
+
@helppat = Arg::helppat()
|
1101
|
+
@verspat = Arg::versionpat()
|
1102
|
+
|
1103
|
+
@strict = _strict
|
1104
|
+
@clump = clump
|
1105
|
+
@source = ''
|
1106
|
+
@tight = @usage =~ /\[tight\]/i
|
1107
|
+
@caller = caller()
|
1108
|
+
|
1109
|
+
# VESTIGAL DEBUGGING CODE
|
1110
|
+
if @@Declare_debug > 0
|
1111
|
+
f = File.new(".CODE.rb","w") and
|
1112
|
+
f.puts( code() ) and
|
1113
|
+
f.close()
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
# DO THE PARSE (IF APPROPRIATE)
|
1117
|
+
if (opts.length() == 2)
|
1118
|
+
return nil unless parse(opts[1])
|
1119
|
+
else
|
1120
|
+
return nil unless parse()
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
end # initialize
|
1124
|
+
|
1125
|
+
|
1126
|
+
# Parse the parameter description and in some cases,
|
1127
|
+
# optionally eval it, too.
|
1128
|
+
def parse(*opts)
|
1129
|
+
source = opts[0]
|
1130
|
+
_args = ()
|
1131
|
+
_get_nextline = proc { nil }
|
1132
|
+
|
1133
|
+
if source
|
1134
|
+
case source
|
1135
|
+
when Method
|
1136
|
+
_get_nextline = source
|
1137
|
+
_args = _get_nextline.call(self)
|
1138
|
+
source = '[METHOD]'
|
1139
|
+
when Proc
|
1140
|
+
_get_nextline = source
|
1141
|
+
_args = _get_nextline.call(self)
|
1142
|
+
source = '[PROC]'
|
1143
|
+
when IO
|
1144
|
+
if source.fileno > 0 && source.tty?
|
1145
|
+
_get_nextline = method(:_get_nextline)
|
1146
|
+
_args = $stdin.readline
|
1147
|
+
source = '<STDIN>'
|
1148
|
+
else
|
1149
|
+
_args = source.readlines.join(' ')
|
1150
|
+
_args.tr!('\t\n',' ')
|
1151
|
+
end
|
1152
|
+
when Array
|
1153
|
+
if source.length() == 1 && !source[0] ||
|
1154
|
+
source[0] == "-BUILD" ||
|
1155
|
+
source[0] == "-SKIP"
|
1156
|
+
return 0
|
1157
|
+
elsif source.length() == 2 && source[0] == "-ARGV"
|
1158
|
+
if !source[1] or !source[1] === Array
|
1159
|
+
raise 'Error: parse(["-ARGV"]) not passed an array as second parameter.'
|
1160
|
+
end
|
1161
|
+
_args = source[1].map { |i| i.tr( " \t\n", "\0\0\0" ) }.join(' ')
|
1162
|
+
source = '<ARRAY>'
|
1163
|
+
elsif source.length() == 1 && source[0] == "-STDIN"
|
1164
|
+
_get_nextline = method(:_get_nextline)
|
1165
|
+
_args = $stdin.readline
|
1166
|
+
source = '<STDIN>'
|
1167
|
+
elsif source.length() == 1 && source[0] == '-CONFIG'
|
1168
|
+
progname = "#{$0}rc"
|
1169
|
+
progname.sub!(%r#.*/#,'')
|
1170
|
+
home = ENV['HOME'] || ''
|
1171
|
+
_args, source = _load_sources( _get_nextline,
|
1172
|
+
[ home+"/.#{progname}",
|
1173
|
+
".#{progname}" ] )
|
1174
|
+
else
|
1175
|
+
# Bunch of files to load passed to parse()
|
1176
|
+
_args, source = _load_sources( _get_nextline, source )
|
1177
|
+
end
|
1178
|
+
else # else/case LITERAL STRING TO PARSE
|
1179
|
+
_args = source.dup
|
1180
|
+
source[7] = '...' if source && source.length() > 7
|
1181
|
+
source = "\"#{source[0..9]}\""
|
1182
|
+
end # case
|
1183
|
+
return 0 unless _args
|
1184
|
+
source = " (in #{source})"
|
1185
|
+
else
|
1186
|
+
_args = ARGV.map { |i| i.tr( " \t\n", "\0\0\0" ) }.join(' ')
|
1187
|
+
source = ''
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
@source = source
|
1191
|
+
|
1192
|
+
begin
|
1193
|
+
err = eval( code(@caller) )
|
1194
|
+
if $@
|
1195
|
+
# oops, something wrong... exit
|
1196
|
+
puts "#{$!} #{$@}"
|
1197
|
+
exit(1)
|
1198
|
+
end
|
1199
|
+
if !err
|
1200
|
+
exit(1)
|
1201
|
+
end
|
1202
|
+
rescue
|
1203
|
+
raise
|
1204
|
+
end
|
1205
|
+
|
1206
|
+
|
1207
|
+
true
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
def type(*t)
|
1211
|
+
Getopt::Declare::ScalarArg::addtype(t)
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
|
1215
|
+
@@_nocase = 0
|
1216
|
+
|
1217
|
+
|
1218
|
+
# Print out version information and maybe exit
|
1219
|
+
def version(*t)
|
1220
|
+
prog = "#{$0}"
|
1221
|
+
filedate = File.stat( prog ).mtime.localtime()
|
1222
|
+
prog.sub!(%r#.*/#,'')
|
1223
|
+
if defined?($VERSION)
|
1224
|
+
puts "\n#{prog}: version #{$VERSION} (#{filedate})\n\n"
|
1225
|
+
else
|
1226
|
+
puts "\n#{prog}: version dated #{filedate}\n\n"
|
1227
|
+
end
|
1228
|
+
exit t[0] if !t[0].nil?
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
# Print out usage information
|
1232
|
+
def usage(*opt)
|
1233
|
+
|
1234
|
+
t = @usage
|
1235
|
+
|
1236
|
+
lastflag = nil
|
1237
|
+
lastdesc = nil
|
1238
|
+
usage = ''
|
1239
|
+
|
1240
|
+
while t.length() > 0
|
1241
|
+
|
1242
|
+
# COMMENT:
|
1243
|
+
t.sub!(/\A[ \t]*#.*\n/,".") and next
|
1244
|
+
|
1245
|
+
# TYPE DIRECTIVE:
|
1246
|
+
se = DelimScanner::new( t )
|
1247
|
+
|
1248
|
+
if t =~ /\A\s*\[pvtype:/
|
1249
|
+
if action = se.extractBracketed("[")
|
1250
|
+
t.sub!(Regexp::quote( action ).to_re,'')
|
1251
|
+
t.sub!(/\A[ \t]*\n/,"")
|
1252
|
+
next
|
1253
|
+
end
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
# ACTION
|
1257
|
+
codeblockDelimiters = {
|
1258
|
+
'{' => '}'
|
1259
|
+
}
|
1260
|
+
se = DelimScanner::new( t )
|
1261
|
+
if action = se.extractCodeblock(codeblockDelimiters)
|
1262
|
+
t.sub!(Regexp::quote( action ).to_re,'')
|
1263
|
+
t.sub!(/\A[ \t]*\n/,"")
|
1264
|
+
decfirst = 0 unless !decfirst.nil?
|
1265
|
+
next
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
|
1269
|
+
# ARG + DESC:
|
1270
|
+
if t.sub!(/\A(.*?\S.*?\t+)(.*?\n)/,"")
|
1271
|
+
decfirst = 0 unless !decfirst.nil?
|
1272
|
+
spec = "#$1".expand_tabs!()
|
1273
|
+
desc = "#$2".expand_tabs!()
|
1274
|
+
|
1275
|
+
t.gsub!(/\A((?![ \t]*(\{|\n)|.*?\S.*?\t.*?\S).*?\S.*\n)/) { |i|
|
1276
|
+
desc += "#$1".expand_tabs!()
|
1277
|
+
""
|
1278
|
+
}
|
1279
|
+
|
1280
|
+
next if desc =~ /\[undocumented\]/i
|
1281
|
+
|
1282
|
+
uoff = 0
|
1283
|
+
spec.gsub!(/(<[a-zA-Z]\w*):([^>]+)>/e) { |i|
|
1284
|
+
uoff += 1 + "#$2".length() and "#$1>"
|
1285
|
+
}
|
1286
|
+
spec.gsub!(/\t/,"=")
|
1287
|
+
|
1288
|
+
ditto = desc =~ /\A\s*\[ditto\]/
|
1289
|
+
desc.gsub!(/^\s*\[.*?\]\s*\n/m,"")
|
1290
|
+
desc.gsub!(BracketDirectives,'')
|
1291
|
+
#desc.gsub!(/\[.*?\]/,"")
|
1292
|
+
|
1293
|
+
|
1294
|
+
if ditto
|
1295
|
+
desc = (lastdesc ? _ditto(lastflag,lastdesc,desc) : "" )
|
1296
|
+
elsif desc =~ /\A\s*\Z/
|
1297
|
+
next
|
1298
|
+
else
|
1299
|
+
lastdesc = desc
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
spec =~ /\A\s*(\S+)/ and lastflag = "#$1"
|
1303
|
+
|
1304
|
+
usage += spec + ' ' * uoff + desc
|
1305
|
+
next
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
|
1309
|
+
|
1310
|
+
# OTHERWISE, DECORATION
|
1311
|
+
if t.sub!(/((?:(?!\[pvtype:).)*)(\n|(?=\[pvtype:))/,"")
|
1312
|
+
desc = "#$1"+("#$2"||'')
|
1313
|
+
#desc.gsub!(/^(\s*\[.*?\])+\s*\n/m,'')
|
1314
|
+
#desc.gsub!(/\[.*?\]/,'') # eliminates anything in brackets
|
1315
|
+
if @tight || desc !~ /\A\s*\Z/
|
1316
|
+
desc.gsub!(BracketDirectives,'')
|
1317
|
+
next if desc =~ /\A\s*\Z/
|
1318
|
+
end
|
1319
|
+
decfirst = 1 unless !decfirst.nil? or desc =~ /\A\s*\Z/
|
1320
|
+
usage += desc
|
1321
|
+
end
|
1322
|
+
|
1323
|
+
end #while
|
1324
|
+
|
1325
|
+
required = ''
|
1326
|
+
|
1327
|
+
for arg in @args
|
1328
|
+
required += ' ' + arg.desc + ' ' if arg.required
|
1329
|
+
end
|
1330
|
+
|
1331
|
+
usage.gsub!(/\255/,"[/") # REINSTATE ESCAPED '['s
|
1332
|
+
|
1333
|
+
required.gsub!(/<([a-zA-Z]\w*):[^>]+>/,'<\1>')
|
1334
|
+
|
1335
|
+
helpcmd = Getopt::Declare::Arg::besthelp
|
1336
|
+
versioncmd = Getopt::Declare::Arg::bestversion
|
1337
|
+
|
1338
|
+
pager = $stdout
|
1339
|
+
|
1340
|
+
#begin
|
1341
|
+
# eval('require "IO/Pager";')
|
1342
|
+
# pager = IO::Pager.new()
|
1343
|
+
#rescue
|
1344
|
+
#end
|
1345
|
+
|
1346
|
+
unless @source.nil?
|
1347
|
+
version()
|
1348
|
+
prog = "#{$0}"
|
1349
|
+
prog.sub!(%r#.*/#,'')
|
1350
|
+
pager.print "Usage: #{prog} [options]#{required}\n"
|
1351
|
+
pager.print " #{prog} #{helpcmd}\n" if helpcmd
|
1352
|
+
pager.print " #{prog} #{versioncmd}\n" if versioncmd
|
1353
|
+
pager.print "\n" unless decfirst && decfirst == 1 && usage =~ /\A[ \t]*\n/
|
1354
|
+
end
|
1355
|
+
|
1356
|
+
pager.print "Options:\n" unless decfirst && decfirst == 1
|
1357
|
+
pager.print usage
|
1358
|
+
|
1359
|
+
exit(opt[0]) if opt[0]
|
1360
|
+
end
|
1361
|
+
|
1362
|
+
attr_accessor :unused
|
1363
|
+
|
1364
|
+
|
1365
|
+
# Return list of used parameters (after parsing)
|
1366
|
+
def used()
|
1367
|
+
used = @cache.each_key { |i| [ i, @cache[i] ] }
|
1368
|
+
return (used.map { |i| flatten(i) } ).join(' ')
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
@@m = []
|
1372
|
+
|
1373
|
+
# Main method to generate code to be evaluated for parsing.
|
1374
|
+
def code(*t)
|
1375
|
+
package = t[0] || ''
|
1376
|
+
code = %q%
|
1377
|
+
|
1378
|
+
|
1379
|
+
@_deferred = []
|
1380
|
+
@_errormsg = nil
|
1381
|
+
@_finished = nil
|
1382
|
+
|
1383
|
+
begin
|
1384
|
+
|
1385
|
+
def defer(&i)
|
1386
|
+
@_deferred.push( i )
|
1387
|
+
end
|
1388
|
+
|
1389
|
+
def reject(*i)
|
1390
|
+
if !i || i[0]
|
1391
|
+
@_errormsg = i[1] if i[1]
|
1392
|
+
throw :paramout
|
1393
|
+
end
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
def finish(i)
|
1397
|
+
if !i || i[0]
|
1398
|
+
@_finished = 1
|
1399
|
+
end
|
1400
|
+
end
|
1401
|
+
|
1402
|
+
@unused = []
|
1403
|
+
@cache = {}
|
1404
|
+
_FOUND_ = {}
|
1405
|
+
_errors = 0
|
1406
|
+
_invalid = {}
|
1407
|
+
_lastprefix = nil
|
1408
|
+
|
1409
|
+
_pos = 0 # current position to match from
|
1410
|
+
_nextpos = 0 # next position to match from
|
1411
|
+
|
1412
|
+
catch(:alldone) do
|
1413
|
+
while !@_finished
|
1414
|
+
begin
|
1415
|
+
catch(:arg) do
|
1416
|
+
@_errormsg = nil
|
1417
|
+
|
1418
|
+
# This is used for clustering of flags
|
1419
|
+
while _lastprefix
|
1420
|
+
substr = _args[_nextpos..-1]
|
1421
|
+
substr =~ /^(?!\s|\0|\Z)% +
|
1422
|
+
Getopt::Declare::Arg::negflagpat() + %q%/ or
|
1423
|
+
begin
|
1424
|
+
_lastprefix=nil
|
1425
|
+
break
|
1426
|
+
end
|
1427
|
+
"#{_lastprefix}#{substr}" =~ /^(% +
|
1428
|
+
Getopt::Declare::Arg::posflagpat() + %q%)/ or
|
1429
|
+
begin
|
1430
|
+
_lastprefix=nil
|
1431
|
+
break
|
1432
|
+
end
|
1433
|
+
_args = _args[0.._nextpos-1] + _lastprefix + _args[_nextpos..-1]
|
1434
|
+
break
|
1435
|
+
end # while _lastprefix
|
1436
|
+
|
1437
|
+
% + '' + %q%
|
1438
|
+
_pos = _nextpos if _args
|
1439
|
+
|
1440
|
+
usage(0) if _args && gindex(_args,/\G(% + @helppat + %q%)(\s|\0|\Z)/,_pos)
|
1441
|
+
version(0) if _args && _args =~ /(% + @verspat + %q%)(\s|\0|\Z)/
|
1442
|
+
%
|
1443
|
+
|
1444
|
+
for arg in @args
|
1445
|
+
code << arg.code(self,package)
|
1446
|
+
end
|
1447
|
+
|
1448
|
+
code << %q%
|
1449
|
+
|
1450
|
+
if _lastprefix
|
1451
|
+
_pos = _nextpos + _lastprefix.length()
|
1452
|
+
_lastprefix = nil
|
1453
|
+
next
|
1454
|
+
end
|
1455
|
+
|
1456
|
+
_pos = _nextpos
|
1457
|
+
|
1458
|
+
_args && _pos = gindex( _args, /\G(?:\s|\0)*(\S+)/, _pos ) or throw(:alldone)
|
1459
|
+
|
1460
|
+
if @_errormsg
|
1461
|
+
$stderr.puts( "Error#{source}: #{@_errormsg}\n" )
|
1462
|
+
else
|
1463
|
+
@unused.push( @@m[0] )
|
1464
|
+
end
|
1465
|
+
|
1466
|
+
_errors += 1 if @_errormsg
|
1467
|
+
|
1468
|
+
end # catch(:arg)
|
1469
|
+
|
1470
|
+
ensure # begin
|
1471
|
+
_pos = 0 if _pos.nil?
|
1472
|
+
_nextpos = _pos if _args
|
1473
|
+
if _args and _args.index( /\G(\s|\0)*\Z/, _pos )
|
1474
|
+
_args = _get_nextline.call(self) if !@_finished
|
1475
|
+
throw(:alldone) unless _args
|
1476
|
+
_pos = _nextpos = 0
|
1477
|
+
_lastprefix = ''
|
1478
|
+
end # if
|
1479
|
+
end # begin/ensure
|
1480
|
+
end # while @_finished
|
1481
|
+
end # catch(:alldone)
|
1482
|
+
end # begin
|
1483
|
+
|
1484
|
+
%
|
1485
|
+
|
1486
|
+
|
1487
|
+
################################
|
1488
|
+
# Check for required arguments #
|
1489
|
+
################################
|
1490
|
+
for arg in @args
|
1491
|
+
next unless arg.required
|
1492
|
+
|
1493
|
+
code << %q%
|
1494
|
+
unless _FOUND_['% + arg.name + %q%'] %
|
1495
|
+
|
1496
|
+
if @mutex[arg.name]
|
1497
|
+
for m in @mutex[arg.name]
|
1498
|
+
code << %q# or _FOUND_['# + m + %q#']#
|
1499
|
+
end
|
1500
|
+
end
|
1501
|
+
|
1502
|
+
code << %q%
|
1503
|
+
$stderr.puts "Error#{@source}: required parameter '% + arg.name + %q%' not found."
|
1504
|
+
_errors += 1
|
1505
|
+
end
|
1506
|
+
%
|
1507
|
+
|
1508
|
+
end
|
1509
|
+
|
1510
|
+
########################################
|
1511
|
+
# Check for arguments requiring others #
|
1512
|
+
########################################
|
1513
|
+
|
1514
|
+
for arg in @args
|
1515
|
+
next unless arg.requires
|
1516
|
+
|
1517
|
+
code << %q%
|
1518
|
+
if _FOUND_['% + arg.name + %q%'] && !(% + _enfound(arg.requires) +
|
1519
|
+
%q%)
|
1520
|
+
$stderr.puts "Error#{@source}.: parameter '% + arg.name + %q%' can only be specified with '% + _enbool( arg.requires ) + %q%'"
|
1521
|
+
_errors += 1
|
1522
|
+
end
|
1523
|
+
%
|
1524
|
+
end
|
1525
|
+
|
1526
|
+
code << %q%
|
1527
|
+
#################### Add unused arguments
|
1528
|
+
if _args && _nextpos > 0 && _args.length() > 0
|
1529
|
+
@unused.push( _args[_nextpos..-1].split(' ' ) )
|
1530
|
+
end
|
1531
|
+
|
1532
|
+
for i in @unused
|
1533
|
+
i.tr!( "\0", " " )
|
1534
|
+
end
|
1535
|
+
|
1536
|
+
%
|
1537
|
+
|
1538
|
+
if @strict
|
1539
|
+
code << %q%
|
1540
|
+
#################### Handle strict flag
|
1541
|
+
unless _nextpos < ( _args ? _args.length : 0 )
|
1542
|
+
for i in @unused
|
1543
|
+
$stderr.puts "Error#{@source}: unrecognizable argument ('#{i}')"
|
1544
|
+
_errors += 1
|
1545
|
+
end
|
1546
|
+
end
|
1547
|
+
%
|
1548
|
+
end
|
1549
|
+
|
1550
|
+
code << %q%
|
1551
|
+
#################### Print help hint
|
1552
|
+
if _errors > 0 && !@source.nil?
|
1553
|
+
$stderr.puts "\n(try '#$0 % + Getopt::Declare::Arg::besthelp + %q%' for more information)"
|
1554
|
+
end
|
1555
|
+
|
1556
|
+
## cannot just assign unused to ARGV in ruby
|
1557
|
+
unless @source != ''
|
1558
|
+
ARGV.clear
|
1559
|
+
@unused.map { |i| ARGV.push(i) }
|
1560
|
+
end
|
1561
|
+
|
1562
|
+
unless _errors > 0
|
1563
|
+
for i in @_deferred
|
1564
|
+
begin
|
1565
|
+
i.call()
|
1566
|
+
rescue => e
|
1567
|
+
raise "Error: action in Getopt::Declare specification produced:\n" + e
|
1568
|
+
end
|
1569
|
+
end
|
1570
|
+
end
|
1571
|
+
|
1572
|
+
!(_errors>0) # return true or false (false for errors)
|
1573
|
+
|
1574
|
+
%
|
1575
|
+
return code
|
1576
|
+
end
|
1577
|
+
|
1578
|
+
|
1579
|
+
# Inspect cache (not the declare object)
|
1580
|
+
def inspect()
|
1581
|
+
return nil if !@cache
|
1582
|
+
t = ''
|
1583
|
+
|
1584
|
+
@cache.each { |a,b|
|
1585
|
+
t << a + " => "
|
1586
|
+
case b
|
1587
|
+
when Hash
|
1588
|
+
t << "{"
|
1589
|
+
i = []
|
1590
|
+
b.each { |c,d|
|
1591
|
+
i.push( " '#{c}' => " + d.inspect )
|
1592
|
+
}
|
1593
|
+
t << i.join(',')
|
1594
|
+
t << " }"
|
1595
|
+
else
|
1596
|
+
t << b.inspect
|
1597
|
+
end
|
1598
|
+
t << "\n"
|
1599
|
+
}
|
1600
|
+
t << "Unused: " + unused.join(', ')
|
1601
|
+
end
|
1602
|
+
|
1603
|
+
# Iterator for Getopt::Declare (travels thru all cache keys)
|
1604
|
+
def each(&t)
|
1605
|
+
@cache.each(&t)
|
1606
|
+
end
|
1607
|
+
|
1608
|
+
# Operator to easily create new value in of Getopt::Declare
|
1609
|
+
def []=(name,val)
|
1610
|
+
@cache = {} unless @cache
|
1611
|
+
@cache[name] = val
|
1612
|
+
end
|
1613
|
+
|
1614
|
+
# Operator to easily return cache of Getopt::Declare
|
1615
|
+
def [](name)
|
1616
|
+
if @cache
|
1617
|
+
return @cache[name]
|
1618
|
+
else
|
1619
|
+
return nil
|
1620
|
+
end
|
1621
|
+
end
|
1622
|
+
|
1623
|
+
attr :mutex
|
1624
|
+
attr :helppat
|
1625
|
+
attr :verspat
|
1626
|
+
attr :strict
|
1627
|
+
attr :clump
|
1628
|
+
attr :source
|
1629
|
+
|
1630
|
+
end # class Declare
|
1631
|
+
|
1632
|
+
end # module Getopt
|
1633
|
+
|
1634
|
+
|
1635
|
+
|
1636
|
+
|
1637
|
+
__END__
|
1638
|
+
|