getopt-declare 1.09.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+