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.
@@ -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
+