timetrap 1.8.5 → 1.8.6

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