getopt-declare 1.28 → 1.29

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