alib 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/alib-0.3.1.rb +3872 -0
  2. data/lib/alib.rb +3872 -0
  3. metadata +40 -0
@@ -0,0 +1,3872 @@
1
+ #
2
+ # the ALib module is a namespace for things i've found useful
3
+ #
4
+ module ALib
5
+ #--{{{
6
+ VERSION = '0.3.1'
7
+
8
+ require 'pathname'
9
+ require 'socket'
10
+ require 'tmpdir'
11
+ require 'rbconfig'
12
+ require 'logger'
13
+ require 'yaml'
14
+ require 'drb/drb'
15
+ require 'fileutils'
16
+ require 'tempfile'
17
+ require 'optparse'
18
+ require 'sync'
19
+ require 'time'
20
+
21
+ def ALib::export e
22
+ #--{{{
23
+ (@__exported ||= []) << e
24
+ @__exported.uniq!
25
+ @__exported
26
+ #--}}}
27
+ end
28
+
29
+ #
30
+ # the utility module is a namespace for things which otherwise wouldn't have a
31
+ # home. the methods of Util can be used as module methods or included into a
32
+ # class and then used as instance OR class methods
33
+ #
34
+ module Util
35
+ #--{{{
36
+ class << self
37
+ def export(*syms)
38
+ #--{{{
39
+ syms.each do |sym|
40
+ sym = "#{ sym }".intern
41
+ module_function sym
42
+ public sym
43
+ end
44
+ #--}}}
45
+ end
46
+ def append_features c
47
+ #--{{{
48
+ super
49
+ c.extend self
50
+ #--}}}
51
+ end
52
+ end
53
+ #
54
+ # requires a certain version of ruby or higher
55
+ # require_version '1.8.0'
56
+ #
57
+ def require_version version
58
+ #--{{{
59
+ major, minor, teeny = "#{ version }".split(%r/\./o).map{|n| Integer(n)}
60
+ required = "#{ major }.#{ minor }.#{ teeny }"
61
+ _major, _minor, _teeny =
62
+ %w( MAJOR MINOR TEENY ).map{|k| Integer(::Config::CONFIG[k])}
63
+ actual = "#{ _major }.#{ _minor }.#{ _teeny }"
64
+ unless _major > major or _major == major and _minor >= minor
65
+ STDERR.puts("=" * 79)
66
+ STDERR.puts "this program requires a ruby version >= <#{ required }>"
67
+ STDERR.puts
68
+ STDERR.puts "you are currenlty running ruby version <#{ actual }>"
69
+ STDERR.puts
70
+ STDERR.puts "possible problems which could cause this are:"
71
+ STDERR.puts " - improper PATH environment variable setting"
72
+ STDERR.puts " - ruby > <#{ required }> has not been installed"
73
+ STDERR.puts("=" * 79)
74
+ exit 1
75
+ end
76
+ #--}}}
77
+ end
78
+ #
79
+ # the basename of $0
80
+ #
81
+ def prognam
82
+ #--{{{
83
+ File::basename $0
84
+ #--}}}
85
+ end
86
+ export 'prognam'
87
+ #
88
+ # marshal'd 'deep' copy of obj
89
+ #
90
+ def mcp obj
91
+ #--{{{
92
+ Marshal.load(Marshal.dump(obj))
93
+ #--}}}
94
+ end
95
+ export 'mcp'
96
+ #
97
+ # self.class
98
+ #
99
+ def klass
100
+ #--{{{
101
+ self.class
102
+ #--}}}
103
+ end
104
+ export 'klass'
105
+ #
106
+ # File::expand_path + link resolution
107
+ #
108
+ def realpath path
109
+ #--{{{
110
+ path = File::expand_path "#{ path }"
111
+ begin
112
+ Pathname.new(path).realpath.to_s
113
+ rescue Errno::ENOENT, Errno::ENOTDIR
114
+ path
115
+ end
116
+ #--}}}
117
+ end
118
+ export 'realpath'
119
+ #
120
+ # collect n hashed into one hash - later keys overried earlier keys
121
+ #
122
+ def hashify(*hashes)
123
+ #--{{{
124
+ hashes.inject(accum={}){|accum,hash| accum.update hash}
125
+ #--}}}
126
+ end
127
+ export 'hashify'
128
+ #
129
+ # look up key in hash as key, then string of key, then intern of key -
130
+ # returning the value or, if key is not found, nil or default
131
+ #
132
+ def getopt opt, hash, default = nil
133
+ #--{{{
134
+ keys = opt.respond_to?('each') ? opt : [opt]
135
+
136
+ keys.each do |key|
137
+ return hash[key] if hash.has_key? key
138
+ key = "#{ key }"
139
+ return hash[key] if hash.has_key? key
140
+ key = key.intern
141
+ return hash[key] if hash.has_key? key
142
+ end
143
+
144
+ return default
145
+ #--}}}
146
+ end
147
+ alias get_opt getopt
148
+ export 'getopt'
149
+ export 'get_opt'
150
+ #
151
+ # determine if a key, key.to_s, or key.to_s.intern is in hash
152
+ # see getopt
153
+ #
154
+ def hasopt opt, hash, default = false
155
+ #--{{{
156
+ keys = opt.respond_to?('each') ? opt : [opt]
157
+
158
+ keys.each do |key|
159
+ return key if hash.has_key? key
160
+ key = "#{ key }"
161
+ return key if hash.has_key? key
162
+ key = key.intern
163
+ return key if hash.has_key? key
164
+ end
165
+
166
+ return default
167
+ #--}}}
168
+ end
169
+ alias has_opt hasopt
170
+ alias hasopt? hasopt
171
+ alias has_opt? hasopt
172
+ export 'hasopt'
173
+ export 'hasopt?'
174
+ export 'has_opt'
175
+ export 'has_opt?'
176
+ #
177
+ # delete key in hash as key, then string of key, then intern of key -
178
+ # returning the value or, if key is not found, nil or default
179
+ #
180
+ def delopt opt, hash, default = nil
181
+ #--{{{
182
+ keys = opt.respond_to?('each') ? opt : [opt]
183
+
184
+ keys.each do |key|
185
+ return hash.delete(key) if hash.has_key? key
186
+ key = "#{ key }"
187
+ return hash.delete(key) if hash.has_key? key
188
+ key = key.intern
189
+ return hash.delete(key) if hash.has_key? key
190
+ end
191
+
192
+ return default
193
+ #--}}}
194
+ end
195
+ alias delete_opt delopt
196
+ alias extract_opt delopt
197
+ alias extractopt delopt
198
+ alias xopt delopt
199
+ export 'delete_opt'
200
+ export 'extract_opt'
201
+ export 'extractopt'
202
+ export 'xopt'
203
+ export 'delopt'
204
+ #
205
+ # returns true if pid is running, false otherwise
206
+ #
207
+ def alive pid
208
+ #--{{{
209
+ pid = Integer("#{ pid }")
210
+ begin
211
+ Process::kill 0, pid
212
+ true
213
+ rescue Errno::ESRCH
214
+ false
215
+ end
216
+ #--}}}
217
+ end
218
+ alias alive? alive
219
+ export 'alive', 'alive?'
220
+ #
221
+ # brutally shut down a process. opts can contain the keys 'signals' which
222
+ # should be a list of signals used to send to the process and the key
223
+ # 'suspend' which is the amount of time to wait after firing each signal
224
+ # before seeing if the process is dead. the defaults are %w(TERM QUIT KILL)
225
+ # and 4 respectively
226
+ #
227
+ def maim(pid, opts = {})
228
+ #--{{{
229
+ sigs = Util::getopt 'signals', opts, %w(SIGTERM SIGQUIT SIGKILL)
230
+ suspend = Util::getopt 'suspend', opts, 4
231
+ pid = Integer("#{ pid }")
232
+ existed = false
233
+ sigs.each do |sig|
234
+ begin
235
+ Process::kill(sig, pid)
236
+ existed = true
237
+ rescue Errno::ESRCH
238
+ unless existed
239
+ return nil
240
+ else
241
+ return true
242
+ end
243
+ end
244
+ return true unless alive?(pid)
245
+ sleep suspend
246
+ return true unless alive?(pid)
247
+ end
248
+ return(not alive?(pid))
249
+ #--}}}
250
+ end
251
+ export 'maim'
252
+ #
253
+ # YYYY-MM-DD hh:mm:ss.uuuuuu representation of time. if the option
254
+ # 'nospace' is true spaces are replaced with underscores/or the value of the
255
+ # nospace option - useful for constructing filenames
256
+ #
257
+ def timestamp arg = Time::now
258
+ #--{{{
259
+ if Time === arg
260
+ arg.iso8601 2
261
+ else
262
+ opts =
263
+ case arg
264
+ when Hash
265
+ opts = arg
266
+ when Time
267
+ {'time' => arg}
268
+ else
269
+ raise ArgumentError, "#{ arg.inspect } (#{ arg.class })"
270
+ end
271
+ time = Util::getopt 'time', opts, Time::now
272
+ local = Util::getopt 'local', opts, false
273
+ nospace = Util::getopt('nospace', opts, Util::getopt('no_space', opts, false))
274
+ dateonly = Util::getopt('dateonly', opts, Util::getopt('date_only', opts, false))
275
+ time = time.utc unless local
276
+ usec = "#{ time.usec }"
277
+ usec << ('0' * (6 - usec.size)) if usec.size < 6
278
+ stamp =
279
+ unless dateonly
280
+ time.strftime('%Y-%m-%d %H:%M:%S.') << usec
281
+ else
282
+ time.strftime('%Y-%m-%d')
283
+ end
284
+ if nospace
285
+ spc = TrueClass === nospace ? 'T' : "#{ nospace }"
286
+ stamp.gsub! %r/\s+/, spc
287
+ end
288
+ stamp
289
+ end
290
+ #--}}}
291
+ end
292
+ export 'timestamp'
293
+ #
294
+ # inverse of timestamp. the option 'local' determines whether the timestamp
295
+ # is interpreted as a local or utc time.
296
+ #
297
+ # TODO - review hack to fix Time::parse bug with ms and tz
298
+ # TODO - review hack to fix Time::parse bug with ms and tz
299
+ # TODO - review hack to fix Time::parse bug with ms and tz
300
+ def stamptime string, opts = {}
301
+ #--{{{
302
+ tz = string[ %r/-\d\d:\d\d\s*$/ ]
303
+ time = nil
304
+ if opts.empty? or tz
305
+ u = string[%r/\.\d+/]
306
+ string[%r/\.\d+/] = '' if u
307
+ time = Time::parse string
308
+ time += u.to_f if u
309
+ else
310
+ local = Util::getopt 'local', opts, false
311
+ string = "#{ string }"
312
+ pat = %r/^\s*(\d\d\d\d)-(\d\d)-(\d\d)[\s_tT]+(\d\d):(\d\d):(\d\d)(?:.(\d+))?\s*$/o
313
+ match = pat.match string
314
+ raise ArgumentError, "<#{ string.inspect }>" unless match
315
+ yyyy,mm,dd,h,m,s,u = match.to_a[1..-1].map{|m| (m || 0).to_i}
316
+ if local
317
+ time = Time::local yyyy,mm,dd,h,m,s,u
318
+ else
319
+ time = Time::gm yyyy,mm,dd,h,m,s,u
320
+ end
321
+ end
322
+ return time
323
+ #--}}}
324
+ end
325
+ export 'stamptime'
326
+ #
327
+ # escapes any occurances of char in s with esc modifying s inplace
328
+ #
329
+ def escape! s, char, esc
330
+ #--{{{
331
+ # re = %r/([#{ 0x5c.chr << esc }]*)#{ char }/
332
+ re = %r/([#{ Regexp::quote esc }]*)#{ Regexp::quote char }/
333
+ s.gsub!(re) do
334
+ (($1.size % 2 == 0) ? ($1 << esc) : $1) + char
335
+ end
336
+ #--}}}
337
+ end
338
+ export 'escape!'
339
+ #
340
+ # new copy of s with any occurances of char escaped with esc
341
+ #
342
+ def escape s, char, esc
343
+ #--{{{
344
+ escape! "#{ s }", char, esc
345
+ #--}}}
346
+ end
347
+ export 'escape'
348
+ #
349
+ # a quiet fork
350
+ #
351
+ def fork(*args, &block)
352
+ #--{{{
353
+ begin
354
+ verbose = $VERBOSE
355
+ $VERBOSE = nil
356
+ Process::fork(*args, &block)
357
+ ensure
358
+ $VERBOSE = verbose
359
+ end
360
+ #--}}}
361
+ end
362
+ export 'fork'
363
+ #
364
+ # an quiet exec
365
+ #
366
+ def exec(*args, &block)
367
+ #--{{{
368
+ begin
369
+ verbose = $VERBOSE
370
+ $VERBOSE = nil
371
+ Kernel::exec(*args, &block)
372
+ ensure
373
+ $VERBOSE = verbose
374
+ end
375
+ #--}}}
376
+ end
377
+ export 'exec'
378
+ #
379
+ # a quiet system
380
+ #
381
+ def system(*args, &block)
382
+ #--{{{
383
+ begin
384
+ verbose = $VERBOSE
385
+ $VERBOSE = nil
386
+ Kernel::system(*args, &block)
387
+ ensure
388
+ $VERBOSE = verbose
389
+ end
390
+ #--}}}
391
+ end
392
+ export 'system'
393
+ #
394
+ # a quiet system which succeeds or throws errors
395
+ #
396
+ def spawn cmd, opts = {}
397
+ #--{{{
398
+ exitstatus = Util::getopt(%w( exitstatus exit_status status ), opts, 0)
399
+ Util::system cmd
400
+ status = $?.exitstatus
401
+ raise "cmd <#{ cmd }> failed with <#{ status }>" unless
402
+ status == exitstatus
403
+ status
404
+ #--}}}
405
+ end
406
+ export 'spawn'
407
+ #
408
+ # lookup, and cache, the hostname
409
+ #
410
+ def hostname
411
+ #--{{{
412
+ @__hostname__ ||= Socket::gethostname
413
+ #--}}}
414
+ end
415
+ export 'hostname'
416
+ #
417
+ # lookup, and cache, the host (first bit of hostname quad)
418
+ #
419
+ def host
420
+ #--{{{
421
+ @__host__ ||= hostname.gsub(%r/\..*$/o,'')
422
+ #--}}}
423
+ end
424
+ export 'host'
425
+ #
426
+ # format exception as Logger class does - no backtrace
427
+ #
428
+ def emsg e
429
+ #--{{{
430
+ "#{ e.message } - (#{ e.class })"
431
+ #--}}}
432
+ end
433
+ export 'emsg'
434
+ #
435
+ # format exception backtrace as string
436
+ #
437
+ def btrace e
438
+ #--{{{
439
+ (e.backtrace or []).join("\n")
440
+ #--}}}
441
+ end
442
+ export 'btrace'
443
+ #
444
+ # format exception as Logger class does - with backtrace
445
+ #
446
+ def errmsg e
447
+ #--{{{
448
+ emsg(e) << "\n" << btrace(e)
449
+ #--}}}
450
+ end
451
+ export 'errmsg'
452
+ #
453
+ # determine equality of two exceptions
454
+ #
455
+ def erreq a, b
456
+ #--{{{
457
+ a.class == b.class and
458
+ a.message == b.message and
459
+ a.backtrace == b.backtrace
460
+ #--}}}
461
+ end
462
+ export 'erreq'
463
+ #
464
+ # generate a temporary filename for the directory dir using seed as a
465
+ # basename
466
+ #
467
+ def tmpnam opts = {}
468
+ #--{{{
469
+ dir = Util::getopt 'dir', opts, Dir::tmpdir
470
+ seed = Util::getopt 'seed', opts, Util::prognam
471
+ path =
472
+ "%s_%s_%s_%s_%d" % [
473
+ Util::hostname,
474
+ seed,
475
+ Process::pid,
476
+ Util::timestamp('nospace' => true),
477
+ rand(101010)
478
+ ]
479
+ dirname, basename = File::dirname(path), File::basename(path)
480
+ tn = File::join(dir, dirname, basename.gsub(%r/[^0-9a-zA-Z]/,'_')).gsub(%r/\s+/, '_')
481
+ File::expand_path tn
482
+ #--}}}
483
+ end
484
+ export 'tmpnam'
485
+ def tmpdir(*argv)
486
+ #--{{{
487
+ args, opts = Util::optfilter argv
488
+ retries = Util::getopt 'retries', opts, 42
489
+ opts['dir'] ||= (args.first || Util::getopt('base', opts) || '.')
490
+ d = nil
491
+ retries.times do
492
+ td = Util::tmpnam(opts)
493
+ break if ((d = FileUtils::mkdir_p td rescue nil))
494
+ end
495
+ raise "surpassed max retries <#{ retries }>" unless d
496
+ d = File::expand_path d
497
+ if block_given?
498
+ cwd = Dir::pwd
499
+ begin
500
+ Dir::chdir d
501
+ yield [cwd,d]
502
+ ensure
503
+ Dir::chdir cwd if cwd
504
+ Dir[File::join(d, "**")].each{|e| FileUtils::rm_rf e}
505
+ FileUtils::rm_rf d
506
+ end
507
+ else
508
+ at_exit{ FileUtils::rm_rf d }
509
+ return d
510
+ end
511
+ #--}}}
512
+ end
513
+ export 'tmpdir'
514
+ #
515
+ # make best effort to invalidate any inode caching done by nfs clients
516
+ #
517
+ def uncache file
518
+ #--{{{
519
+ refresh = nil
520
+ begin
521
+ is_a_file = File === file
522
+ path = (is_a_file ? file.path : file.to_s)
523
+ stat = (is_a_file ? file.stat : File::stat(file.to_s))
524
+ refresh = tmpnam(File::dirname(path))
525
+ File::link path, refresh rescue File::symlink path, refresh
526
+ File::chmod stat.mode, path
527
+ File::utime stat.atime, stat.mtime, path
528
+ open(File::dirname(path)){|d| d.fsync rescue nil}
529
+ ensure
530
+ begin
531
+ File::unlink refresh if refresh
532
+ rescue Errno::ENOENT
533
+ end
534
+ end
535
+ #--}}}
536
+ end
537
+ export 'uncache'
538
+ #
539
+ # wrap a string using options width (default 80) and indent using the indent
540
+ # option (default 0)
541
+ #
542
+ def columnize buf, opts = {}
543
+ #--{{{
544
+ width = Util::getopt 'width', opts, 80
545
+ indent = Util::getopt 'indent', opts
546
+ indent = Fixnum === indent ? (' ' * indent) : "#{ indent }"
547
+ column = []
548
+ words = buf.split %r/\s+/o
549
+ row = "#{ indent }"
550
+ while((word = words.shift))
551
+ if((row.size + word.size) < (width - 1))
552
+ row << word
553
+ else
554
+ column << row
555
+ row = "#{ indent }"
556
+ row << word
557
+ end
558
+ row << ' ' unless row.size == (width - 1)
559
+ end
560
+ column << row unless row.strip.empty?
561
+ column.join "\n"
562
+ #--}}}
563
+ end
564
+ export 'columnize'
565
+ #
566
+ # search for a default value for 'var' using
567
+ # * DEFAULT_VAR
568
+ # * self.class.var
569
+ # returning the option default, or nil
570
+ #
571
+ def defval var, opts = {}
572
+ #--{{{
573
+ default = Util::getopt 'default', opts, nil
574
+ v = "#{ var }"
575
+ c0, c1 = "DEFAULT_#{ v }".upcase, "#{ v }".upcase
576
+ begin
577
+ klass.send(v) || klass.const_get(c0) || klass.const_get(c1)
578
+ rescue NameError
579
+ default
580
+ end
581
+ #--}}}
582
+ end
583
+ export 'defval'
584
+ #
585
+ # initiates a catch block to do 'something'. if the method try_again! is
586
+ # called the block is done over - if the method give_up! is called the block
587
+ # is aborted. calls to attempt may be nested.
588
+ #
589
+ def attempt label = 'attempt'
590
+ #--{{{
591
+ ret = nil
592
+ n_attempts = 0
593
+ loop{ break unless catch("#{ label }"){ ret = yield(n_attempts += 1) } == 'try_again' }
594
+ ret
595
+ #--}}}
596
+ end
597
+ export 'attempt'
598
+ #
599
+ # see attempt
600
+ #
601
+ def try_again! label = 'attempt'
602
+ #--{{{
603
+ throw "#{ label }", 'try_again'
604
+ #--}}}
605
+ end
606
+ alias try_again try_again!
607
+ alias again! try_again!
608
+ export 'try_again!', 'again!', 'try_again'
609
+ #
610
+ # see attempt
611
+ #
612
+ def give_up! label = 'attempt'
613
+ #--{{{
614
+ throw "#{ label }", 'give_up'
615
+ #--}}}
616
+ end
617
+ alias giveup! give_up!
618
+ alias give_up give_up!
619
+ export 'give_up!', 'giveup!', 'give_up'
620
+ #
621
+ # creates a class by class name
622
+ #
623
+ def klass_stamp(hierachy, *a, &b)
624
+ #--{{{
625
+ ancestors = hierachy.split(%r/::/)
626
+ parent = Object
627
+ while((child = ancestors.shift))
628
+ klass = parent.const_get child
629
+ parent = klass
630
+ end
631
+ klass::new(*a, &b)
632
+ #--}}}
633
+ end
634
+ export 'klass_stamp'
635
+ #
636
+ # shortcut to Find2
637
+ #
638
+ def find(*a, &b)
639
+ #--{{{
640
+ a << '.' if a.empty?
641
+ Find2::find(*a, &b)
642
+ #--}}}
643
+ end
644
+ export 'find'
645
+ #
646
+ # shortcut to Find2
647
+ #
648
+ def find2(*a, &b)
649
+ #--{{{
650
+ a << '.' if a.empty?
651
+ Find2::find2(*a, &b)
652
+ #--}}}
653
+ end
654
+ export 'find2'
655
+ #
656
+ # shortcut to Find2.prune
657
+ #
658
+ def prune
659
+ #--{{{
660
+ Find2.prune
661
+ #--}}}
662
+ end
663
+ export 'prune'
664
+ #
665
+ # pull out options from arglist
666
+ #
667
+ def optfilter(*list)
668
+ #--{{{
669
+ args, opts = [ list ].flatten.partition{|item| not Hash === item}
670
+ [args, Util::hashify(*opts)]
671
+ #--}}}
672
+ end
673
+ export 'optfilter'
674
+ #
675
+ # pop of options from an arglist
676
+ #
677
+ def argv_split(argv)
678
+ #--{{{
679
+ args = argv
680
+ opts = args.pop if Hash === args.last
681
+ puts "args : #{ args.inspect }"
682
+ puts "opts : #{ opts.inspect }"
683
+ [args, opts]
684
+ #--}}}
685
+ end
686
+ export 'argv_split'
687
+ #
688
+ # split a path into dirname, basename, extension
689
+ #
690
+ def splitpath path
691
+ #--{{{
692
+ path = "#{ path }"
693
+ dirname, basename = File::split path
694
+ [ dirname, (%r/^([^\.]*)(.*)$/).match(basename)[1,2] ].flatten
695
+ #--}}}
696
+ end
697
+ export 'splitpath'
698
+ #
699
+ # handle a pathname after unzipping (iff needed)
700
+ #
701
+ def unzipped path, z_pat = %r/\.(?:z|gz)$/io
702
+ #--{{{
703
+ zipped = zipped?(path, z_pat)
704
+ unless zipped
705
+ yield path
706
+ else
707
+ unzipped = unzip path
708
+ begin
709
+ yield unzipped
710
+ ensure
711
+ zip unzipped
712
+ end
713
+ end
714
+ #--}}}
715
+ end
716
+ export 'unzipped'
717
+ #
718
+ # zip a file - return zipped name
719
+ #
720
+ def zip path, z_ext = nil
721
+ #--{{{
722
+ z_ext ||= '.gz'
723
+ z_ext.gsub! %r/^\s*\.+/, '.'
724
+ Util::spawn "gzip --suffix #{ z_ext } --force #{ path }"
725
+ zipped = "#{ path }#{ z_ext }"
726
+ raise "could not create <#{ zipped }>" unless test ?e, zipped
727
+ if block_given?
728
+ yield zipped
729
+ else
730
+ zipped
731
+ end
732
+ #--}}}
733
+ end
734
+ alias gzip zip
735
+ export 'gzip'
736
+ export 'zip'
737
+ #
738
+ # return the zipped name of a path
739
+ #
740
+ def zipped_name path, z_ext = nil
741
+ #--{{{
742
+ z_ext ||= '.gz'
743
+ z_ext.gsub! %r/^\s*\.+/, '.'
744
+ "#{ path }#{ z_ext }"
745
+ #--}}}
746
+ end
747
+ export 'zipped_name'
748
+ #
749
+ # unzip a file - return unzipped name
750
+ #
751
+ def unzip path, z_pat = nil
752
+ #--{{{
753
+ z_pat ||= %r/\.(?:z|gz)$/io
754
+ Util::spawn "gzip --force --decompress #{ path }" if zipped?(path, z_pat)
755
+ #Util::spawn "gzip --force --decompress #{ path }"
756
+ uzn = Util::unzipped_name path, z_pat
757
+ if block_given?
758
+ yield uzn
759
+ else
760
+ uzn
761
+ end
762
+ #--}}}
763
+ end
764
+ alias gunzip unzip
765
+ export 'gunzip'
766
+ export 'unzip'
767
+ #
768
+ # return the unzipped name of a path
769
+ #
770
+ def unzipped_name path, z_pat = nil
771
+ #--{{{
772
+ z_pat ||= %r/\.(?:z|gz)$/io
773
+ path.gsub z_pat, ''
774
+ #--}}}
775
+ end
776
+ export 'unzipped_name'
777
+ #
778
+ # guess if a file is zipped based on pathname
779
+ #
780
+ def zipped? path, z_pat = %r/\.(?:z|gz)$/io
781
+ #--{{{
782
+ path =~ z_pat
783
+ #--}}}
784
+ end
785
+ alias gzipped? zipped?
786
+ export 'gzipped?'
787
+ export 'zipped?'
788
+ #
789
+ # convert a string to an integer of any base
790
+ #
791
+ def strtod(s, opts = {})
792
+ #--{{{
793
+ base = Util::getopt 'base', opts, 10
794
+ case base
795
+ when 2
796
+ s = "0b#{ s }" unless s =~ %r/^0b\d/
797
+ when 8
798
+ s = "0#{ s }" unless s =~ %r/^0\d/
799
+ when 16
800
+ s = "0x#{ s }" unless s =~ %r/^0x\d/
801
+ end
802
+ Integer s
803
+ #--}}}
804
+ end
805
+ export 'strtod'
806
+ #
807
+ # convert a string to an integer
808
+ #
809
+ def atoi(s, opts = {})
810
+ #--{{{
811
+ strtod("#{ s }".gsub(%r/^0+/,''), 'base' => 10)
812
+ #--}}}
813
+ end
814
+ export 'atoi'
815
+ #
816
+ # bin a integer into a int range using modulo logic
817
+ #
818
+ def rangemod n, ir
819
+ #--{{{
820
+ a, b = Integer(ir.first), Integer(ir.last)
821
+ a, b = b, a if a >= b
822
+ exclusive = ir.exclude_end?
823
+ size = b - a
824
+ size += 1 unless exclusive
825
+ offset = a - 0
826
+ x = (n + offset).modulo size
827
+ x += offset
828
+ #--}}}
829
+ end
830
+ #
831
+ # bin a integer into a int range using modulo logic
832
+ #
833
+ def rangemod n, ir
834
+ #--{{{
835
+ a, b = [ir.first.to_i, ir.last.to_i].sort
836
+ b += 1 if ir.exclude_end?
837
+ size = b - a
838
+ if n < a
839
+ v = n - a
840
+ d = v.abs
841
+ r = d % size
842
+ n = b - r
843
+ elsif n > b
844
+ v = n - b
845
+ d = v.abs
846
+ r = d % size
847
+ n = a + r
848
+ end
849
+ raise "failed to rangemod #{ n } => #{ ir }" unless
850
+ ir.include? n
851
+ n
852
+ #--}}}
853
+ end
854
+ export 'rangemod'
855
+ #
856
+ # explode a
857
+ #
858
+ def hms seconds
859
+ #--{{{
860
+ [
861
+ Integer(seconds / 3600),
862
+ Integer((seconds % 3600) / 60),
863
+ Float((seconds % 3600) / 60.0)
864
+ ]
865
+ #--}}}
866
+ end
867
+ export 'hms'
868
+ #
869
+ # expand variables in a string destructively (to the string)
870
+ #
871
+ def expand! string, vars = {}
872
+ #--{{{
873
+ loop do
874
+ changed = false
875
+ vars.each do |var, value|
876
+ var.gsub! %r/[^a-zA-Z0-9_]/, ''
877
+ [
878
+ %r/\$#{ var }\b/,
879
+ %r/\@#{ var }\b/,
880
+ %r/\${\s*#{ var }\s*}/,
881
+ %r/\@{\s*#{ var }\s*}/
882
+ ].each do |pat|
883
+ changed = string.gsub! pat, "#{ value }"
884
+ end
885
+ end
886
+ break unless changed
887
+ end
888
+ string
889
+ #--}}}
890
+ end
891
+ export 'expand!'
892
+ def expand string, opts = {}
893
+ #--{{{
894
+ Util::expand! string.dup, opts
895
+ #--}}}
896
+ end
897
+ export 'expand'
898
+ #
899
+ # determine path of current ruby interpreter (argv[0] in c)
900
+ #
901
+ def which_ruby
902
+ #--{{{
903
+ c = ::Config::CONFIG
904
+ File::join(c['bindir'], c['ruby_install_name']) << c['EXEEXT']
905
+ #--}}}
906
+ end
907
+ export 'which_ruby'
908
+ #
909
+ # declare multi-dimensional arrays as in md[2,3,4]
910
+ #
911
+ def md
912
+ #--{{{
913
+ @__md__ ||=
914
+ lambda{|*ds| Array::new(ds.shift||0).map{md[*ds] unless ds.empty?}}
915
+ #--}}}
916
+ end
917
+ export 'md'
918
+ #
919
+ # find natural left margin of a here-doc and un-indent it
920
+ #
921
+ def unindent! s
922
+ #--{{{
923
+ indent = nil
924
+ s.each do |line|
925
+ next if line =~ %r/^\s*$/
926
+ indent = line[%r/^\s*/] and break
927
+ end
928
+ s.gsub! %r/^#{ indent }/, "" if indent
929
+ indent ? s : nil
930
+ #--}}}
931
+ end
932
+ export 'unindent!'
933
+ def unindent s
934
+ #--{{{
935
+ s = "#{ s }"
936
+ Util::unindent! s
937
+ s
938
+ #--}}}
939
+ end
940
+ export 'unindent'
941
+
942
+
943
+
944
+ #--}}}
945
+ end # class Util
946
+ ALib::export Util
947
+
948
+ #
949
+ # the logging module extends classes (both at instance and class level) with
950
+ # many methods useful for logging. it relies on the builtin Logger class
951
+ #
952
+ module Logging
953
+ #--{{{
954
+ #
955
+ # a module that adds an accessor to Logging objects in ored to fix a bug where
956
+ # not all logging devices are put into sync mode, resulting in improper log
957
+ # rolling. this is a hack.
958
+ #
959
+ module LoggerExt
960
+ #--{{{
961
+ attr :logdev
962
+ #--}}}
963
+ end # module LoggerExt
964
+ #
965
+ # implementations of the methods shared by both classes and objects of classes
966
+ # which include Logging
967
+ #
968
+ module LogMethods
969
+ #--{{{
970
+ def __logger_mutex
971
+ #--{{{
972
+ unless defined?(@__logger_mutex) and @__logger_mutex
973
+ begin
974
+ Thread.critical = true
975
+ @__logger_mutex = Sync::new unless defined?(@__logger_mutex) and @__logger_mutex
976
+ ensure
977
+ Thread.critical = false
978
+ end
979
+ end
980
+ @__logger_mutex
981
+ #--}}}
982
+ end
983
+ def __logger_sync
984
+ #--{{{
985
+ __logger_mutex.synchronize{ yield }
986
+ #--}}}
987
+ end
988
+ def logger
989
+ #--{{{
990
+ return @logger if defined?(@logger)
991
+ __logger_sync do
992
+ unless defined?(@logger)
993
+ klass = Class === self ? self : self::class
994
+ self.logger = klass::default_logger
995
+ end
996
+ end
997
+ @logger
998
+ #--}}}
999
+ end
1000
+ def logger= log
1001
+ #--{{{
1002
+ __logger_sync do
1003
+ @logger = log
1004
+ @logger.extend LoggerExt
1005
+ @logger.logdev.dev.sync = true rescue nil
1006
+ @logger
1007
+ end
1008
+ #--}}}
1009
+ end
1010
+ %w( debug info warn error fatal ).each do |meth|
1011
+ module_eval <<-code
1012
+ def #{ meth }(*a, &b)
1013
+ __logger_sync{ logger.#{ meth }(*a, &b) }
1014
+ end
1015
+ code
1016
+ end
1017
+ def log_err e
1018
+ #--{{{
1019
+ if logger.debug?
1020
+ error{ errmsg e }
1021
+ else
1022
+ error{ emsg e }
1023
+ end
1024
+ #--}}}
1025
+ end
1026
+ def emsg e
1027
+ #--{{{
1028
+ "#{ e.message } - (#{ e.class })"
1029
+ #--}}}
1030
+ end
1031
+ def btrace e
1032
+ #--{{{
1033
+ e.backtrace.join("\n")
1034
+ #--}}}
1035
+ end
1036
+ def errmsg e
1037
+ #--{{{
1038
+ emsg(e) << "\n" << btrace(e)
1039
+ #--}}}
1040
+ end
1041
+ #--}}}
1042
+ end # module LogMethods
1043
+
1044
+ module LogClassMethods
1045
+ #--{{{
1046
+ def default_logger
1047
+ #--{{{
1048
+ return @default_logger if defined?(@default_logger)
1049
+ __logger_sync do
1050
+ unless defined?(@default_logger)
1051
+ self.default_logger = Logger.new STDERR
1052
+ # @default_logger.warn{ "<#{ self }> using default logger"}
1053
+ end
1054
+ end
1055
+ @default_logger
1056
+ #--}}}
1057
+ end
1058
+ def default_logger= log
1059
+ #--{{{
1060
+ __logger_sync do
1061
+ @default_logger = (Logger === log ? log : Logger::new(log))
1062
+ @default_logger.extend LoggerExt
1063
+ @default_logger.logdev.dev.sync = true rescue nil
1064
+ @default_logger
1065
+ end
1066
+ #--}}}
1067
+ end
1068
+ #--}}}
1069
+ end
1070
+
1071
+ EOL = "\n"
1072
+ DIV0 = ("." * 79) << EOL
1073
+ DIV1 = ("-" * 79) << EOL
1074
+ DIV2 = ("=" * 79) << EOL
1075
+ DIV3 = ("#" * 79) << EOL
1076
+ SEC0 = ("." * 16) << EOL
1077
+ SEC1 = ("-" * 16) << EOL
1078
+ SEC2 = ("=" * 16) << EOL
1079
+ SEC3 = ("#" * 16) << EOL
1080
+
1081
+ class << self
1082
+ #--{{{
1083
+ def append_features c
1084
+ #--{{{
1085
+ ret = super
1086
+ c.extend LogMethods
1087
+ c.extend LogClassMethods
1088
+ ret
1089
+ #--}}}
1090
+ end
1091
+ #--}}}
1092
+ end
1093
+ include LogMethods
1094
+ #--}}}
1095
+ end # module Logging
1096
+ ALib::export Logging
1097
+
1098
+ #
1099
+ # yaml configfile class
1100
+ #
1101
+ class ConfigFile < ::Hash
1102
+ #--{{{
1103
+ DEFAULT_CONIFG = {}
1104
+ DEFAULT_SEARCH_PATH = %w(. ~ /usr/local/etc /usr/etc /etc)
1105
+ DEFAULT_BASENAME = ".#{ File::basename $0 }.conf"
1106
+
1107
+ class << self
1108
+ #--{{{
1109
+ attr :config, true
1110
+ attr :search_path, true
1111
+ attr :basename, true
1112
+ attr :default_text, true
1113
+ def init
1114
+ #--{{{
1115
+ @config = DEFAULT_CONIFG
1116
+ @search_path = DEFAULT_SEARCH_PATH
1117
+ @basename = DEFAULT_BASENAME
1118
+ #--}}}
1119
+ end
1120
+ def gen_template port = nil
1121
+ #--{{{
1122
+ port ||= STDOUT
1123
+ buf = @default_text || @config.to_yaml
1124
+ if port.respond_to? 'write'
1125
+ port.write buf
1126
+ port.write "\n"
1127
+ else
1128
+ open("#{ port }", 'w'){|f| f.puts buf}
1129
+ end
1130
+ #--}}}
1131
+ end
1132
+ def default= conf
1133
+ #--{{{
1134
+ if conf.respond_to?('read')
1135
+ @default_text = munge conf.read
1136
+ @config = YAML::load @default_text
1137
+ else
1138
+ case conf
1139
+ when Hash
1140
+ @config = conf
1141
+ when Pathname
1142
+ open(conf) do |f|
1143
+ @default_text = munge f.read
1144
+ @config = YAML::load @default_text
1145
+ end
1146
+ when String
1147
+ @default_text = munge conf
1148
+ @config = YAML::load @default_text
1149
+ end
1150
+ end
1151
+ @config
1152
+ #--}}}
1153
+ end
1154
+ alias set_default default=
1155
+ def default(*a)
1156
+ #--{{{
1157
+ if a.empty?
1158
+ @config ||= {}
1159
+ else
1160
+ self.default= a.first
1161
+ end
1162
+ #--}}}
1163
+ end
1164
+ def any(basename = @basename, *dirnames)
1165
+ #--{{{
1166
+ config = nil
1167
+ dirnames = @search_path if dirnames.empty?
1168
+ dirnames.each do |dirname|
1169
+ path = File::join dirname, basename
1170
+ path = File::expand_path path
1171
+ if test ?e, path
1172
+ config = self::new path
1173
+ break
1174
+ end
1175
+ end
1176
+ config || self::new('default')
1177
+ #--}}}
1178
+ end
1179
+ def munge buf
1180
+ #--{{{
1181
+ buf.gsub(%r/\t/o,' ')
1182
+ #--}}}
1183
+ end
1184
+ #--}}}
1185
+ end
1186
+ self.init
1187
+
1188
+ attr :path
1189
+ def initialize path = 'default'
1190
+ #--{{{
1191
+ @path = nil
1192
+ yaml = nil
1193
+ if path.nil? or path and path =~ /^\s*default/io
1194
+ yaml = self.class.default
1195
+ @path = 'DEFAULT'
1196
+ else path
1197
+ yaml = YAML::load(self.class.munge(open(path).read))
1198
+ @path = path
1199
+ end
1200
+ self.update yaml
1201
+ #--}}}
1202
+ end
1203
+ def to_hash
1204
+ #--{{{
1205
+ {}.update self
1206
+ #--}}}
1207
+ end
1208
+ #--}}}
1209
+ end # class ConfigFile
1210
+ Configfile = ConfigFile
1211
+ ALib::export ConfigFile
1212
+ ALib::export Configfile
1213
+
1214
+ #
1215
+ # file of lines, comments and blank lines are ignored
1216
+ #
1217
+ class ListFile < ::Array
1218
+ #--{{{
1219
+ class << self
1220
+ #--{{{
1221
+ def parse arg, &b
1222
+ #--{{{
1223
+ arg.respond_to?('gets') ? loadio(arg, &b) : open("#{ path }"){|f| loadio(f, &b)}
1224
+ #--}}}
1225
+ end
1226
+ def loadio io
1227
+ #--{{{
1228
+ while((line = io.gets))
1229
+ line.gsub!(%r/#.*/o,'')
1230
+ next if line =~ %r/^[^\S]+$/o
1231
+ line.gsub!(%r/^[^\S]+|[^\S]+$/o,'')
1232
+ yield line
1233
+ end
1234
+ #--}}}
1235
+ end
1236
+ #--}}}
1237
+ end
1238
+ attr :path
1239
+ def initialize arg
1240
+ #--{{{
1241
+ case arg
1242
+ when Pathname, String
1243
+ @path = path
1244
+ self.class.parse(arg){|line| self << line}
1245
+ when IO
1246
+ @path = arg.respond_to?('path') ? arg.path : arg.to_s
1247
+ self.class.loadio(arg){|line| self << line}
1248
+ else
1249
+ raise "cannot initialize from <#{ arg.inspect }>"
1250
+ end
1251
+ #--}}}
1252
+ end
1253
+ #--}}}
1254
+ end # class ListFile
1255
+ Listfile = ListFile
1256
+ ALib::export ListFile
1257
+ ALib::export Listfile
1258
+
1259
+ #
1260
+ # auto vivifying hash that dumps as yaml nicely
1261
+ #
1262
+ class AutoHash < ::Hash
1263
+ #--{{{
1264
+ def initialize(*args)
1265
+ #--{{{
1266
+ super(*args){|a,k| a[k] = AutoHash::new(*args)}
1267
+ #--}}}
1268
+ end
1269
+ def class
1270
+ #--{{{
1271
+ Hash
1272
+ #--}}}
1273
+ end
1274
+ #--}}}
1275
+ end # class AutoHash
1276
+ ALib::export AutoHash
1277
+
1278
+ # AUTHOR
1279
+ # jan molic /mig/at/1984/dot/cz/
1280
+ #
1281
+ # DESCRIPTION
1282
+ # Hash with preserved order and some array-like extensions
1283
+ # Public domain.
1284
+ #
1285
+ # THANKS
1286
+ # Andrew Johnson for his suggestions and fixes of Hash[],
1287
+ # merge, to_a, inspect and shift
1288
+ class OrderedHash < Hash
1289
+ #--{{{
1290
+ attr_accessor :order
1291
+
1292
+ class << self
1293
+ #--{{{
1294
+ def [] *args
1295
+ #--{{{
1296
+ hsh = OrderedHash.new
1297
+ if Hash === args[0]
1298
+ hsh.replace args[0]
1299
+ elsif (args.size % 2) != 0
1300
+ raise ArgumentError, "odd number of elements for Hash"
1301
+ else
1302
+ hsh[args.shift] = args.shift while args.size > 0
1303
+ end
1304
+ hsh
1305
+ #--}}}
1306
+ end
1307
+ #--}}}
1308
+ end
1309
+ # def initialize
1310
+ ##--{{{
1311
+ # @order = []
1312
+ ##--}}}
1313
+ # end
1314
+ def initialize(*a, &b)
1315
+ #--{{{
1316
+ super
1317
+ @order = []
1318
+ #--}}}
1319
+ end
1320
+ def store_only a,b
1321
+ #--{{{
1322
+ store a,b
1323
+ #--}}}
1324
+ end
1325
+ alias orig_store store
1326
+ def store a,b
1327
+ #--{{{
1328
+ @order.push a unless has_key? a
1329
+ super a,b
1330
+ #--}}}
1331
+ end
1332
+ alias []= store
1333
+ def == hsh2
1334
+ #--{{{
1335
+ return false if @order != hsh2.order
1336
+ super hsh2
1337
+ #--}}}
1338
+ end
1339
+ def clear
1340
+ #--{{{
1341
+ @order = []
1342
+ super
1343
+ #--}}}
1344
+ end
1345
+ def delete key
1346
+ #--{{{
1347
+ @order.delete key
1348
+ super
1349
+ #--}}}
1350
+ end
1351
+ def each_key
1352
+ #--{{{
1353
+ @order.each { |k| yield k }
1354
+ self
1355
+ #--}}}
1356
+ end
1357
+ def each_value
1358
+ #--{{{
1359
+ @order.each { |k| yield self[k] }
1360
+ self
1361
+ #--}}}
1362
+ end
1363
+ def each
1364
+ #--{{{
1365
+ @order.each { |k| yield k,self[k] }
1366
+ self
1367
+ #--}}}
1368
+ end
1369
+ alias each_pair each
1370
+ def delete_if
1371
+ #--{{{
1372
+ @order.clone.each { |k|
1373
+ delete k if yield
1374
+ }
1375
+ self
1376
+ #--}}}
1377
+ end
1378
+ def values
1379
+ #--{{{
1380
+ ary = []
1381
+ @order.each { |k| ary.push self[k] }
1382
+ ary
1383
+ #--}}}
1384
+ end
1385
+ def keys
1386
+ #--{{{
1387
+ @order
1388
+ #--}}}
1389
+ end
1390
+ def invert
1391
+ #--{{{
1392
+ hsh2 = Hash.new
1393
+ @order.each { |k| hsh2[self[k]] = k }
1394
+ hsh2
1395
+ #--}}}
1396
+ end
1397
+ def reject &block
1398
+ #--{{{
1399
+ self.dup.delete_if &block
1400
+ #--}}}
1401
+ end
1402
+ def reject! &block
1403
+ #--{{{
1404
+ hsh2 = reject &block
1405
+ self == hsh2 ? nil : hsh2
1406
+ #--}}}
1407
+ end
1408
+ def replace hsh2
1409
+ #--{{{
1410
+ @order = hsh2.keys
1411
+ super hsh2
1412
+ #--}}}
1413
+ end
1414
+ def shift
1415
+ #--{{{
1416
+ key = @order.first
1417
+ key ? [key,delete(key)] : super
1418
+ #--}}}
1419
+ end
1420
+ def unshift k,v
1421
+ #--{{{
1422
+ unless self.include? k
1423
+ @order.unshift k
1424
+ orig_store(k,v)
1425
+ true
1426
+ else
1427
+ false
1428
+ end
1429
+ #--}}}
1430
+ end
1431
+ def push k,v
1432
+ #--{{{
1433
+ unless self.include? k
1434
+ @order.push k
1435
+ orig_store(k,v)
1436
+ true
1437
+ else
1438
+ false
1439
+ end
1440
+ #--}}}
1441
+ end
1442
+ def pop
1443
+ #--{{{
1444
+ key = @order.last
1445
+ key ? [key,delete(key)] : nil
1446
+ #--}}}
1447
+ end
1448
+ def to_a
1449
+ #--{{{
1450
+ ary = []
1451
+ each { |k,v| ary << [k,v] }
1452
+ ary
1453
+ #--}}}
1454
+ end
1455
+ def to_s
1456
+ #--{{{
1457
+ self.to_a.to_s
1458
+ #--}}}
1459
+ end
1460
+ def inspect
1461
+ #--{{{
1462
+ ary = []
1463
+ each {|k,v| ary << k.inspect + "=>" + v.inspect}
1464
+ '{' + ary.join(", ") + '}'
1465
+ #--}}}
1466
+ end
1467
+ def update hsh2
1468
+ #--{{{
1469
+ hsh2.each { |k,v| self[k] = v }
1470
+ self
1471
+ #--}}}
1472
+ end
1473
+ alias :merge! update
1474
+ def merge hsh2
1475
+ #--{{{
1476
+ self.dup update(hsh2)
1477
+ #--}}}
1478
+ end
1479
+ def select
1480
+ #--{{{
1481
+ ary = []
1482
+ each { |k,v| ary << [k,v] if yield k,v }
1483
+ ary
1484
+ #--}}}
1485
+ end
1486
+ def class
1487
+ #--{{{
1488
+ Hash
1489
+ #--}}}
1490
+ end
1491
+ #--}}}
1492
+ end # class OrderedHash
1493
+ ALib::export OrderedHash
1494
+
1495
+ #
1496
+ # auto vivifying ordered hash that dumps as yaml nicely
1497
+ #
1498
+ class AutoOrderedHash < OrderedHash
1499
+ #--{{{
1500
+ def initialize(*args)
1501
+ #--{{{
1502
+ super(*args){|a,k| a[k] = AutoHash::new(*args)}
1503
+ #--}}}
1504
+ end
1505
+ def class
1506
+ #--{{{
1507
+ Hash
1508
+ #--}}}
1509
+ end
1510
+ #--}}}
1511
+ end # class AutoOrderedHash
1512
+ OrderedAutoHash = AutoOrderedHash
1513
+ ALib::export OrderedAutoHash
1514
+ ALib::export AutoOrderedHash
1515
+
1516
+ # -*- Ruby -*-
1517
+ # Copyright (C) 1998, 2001 Motoyuki Kasahara
1518
+ #
1519
+ # This program is free software; you can redistribute it and/or modify
1520
+ # it under the terms of the GNU General Public License as published by
1521
+ # the Free Software Foundation; either version 2, or (at your option)
1522
+ # any later version.
1523
+ #
1524
+ # This program is distributed in the hope that it will be useful,
1525
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
1526
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1527
+ # GNU General Public License for more details.
1528
+ #
1529
+ #
1530
+ # Traverse a file tree, replacement of `find.rb'.
1531
+ #
1532
+ class Find2
1533
+ #--{{{
1534
+ #
1535
+ # Constatnts
1536
+ #
1537
+ FIND1 = 1
1538
+ FIND2 = 2
1539
+ #
1540
+ # Initializer.
1541
+ #
1542
+ def initialize(mode = FIND1)
1543
+ #--{{{
1544
+ @mode = mode
1545
+ @depth = false
1546
+ @follow = false
1547
+ @xdev = false
1548
+
1549
+ @dirname_stats = Array.new
1550
+ @found_files = Array.new
1551
+ @target_device = 0
1552
+
1553
+ @handle = lambda { |file, stat_result, block|
1554
+ case @mode
1555
+ when FIND1
1556
+ block ? block.call(file) : @found_files.push(file)
1557
+ when FIND2
1558
+ block ? block.call(file, stat_result) : @found_files.push([file, stat_result])
1559
+ end
1560
+ }
1561
+ #--}}}
1562
+ end
1563
+ #
1564
+ # Methods for accessing instances.
1565
+ #
1566
+ attr :depth
1567
+ attr :follow
1568
+ attr :xdev
1569
+ #
1570
+ # Find the directory `dirname'. (private)
1571
+ #
1572
+ def find(*arguments, &block)
1573
+ #--{{{
1574
+ #
1575
+ # Parse options if specified.
1576
+ #
1577
+ #if arguments[0].type == Hash
1578
+ #parse_options(arguments.shift)
1579
+ #end
1580
+
1581
+ # hack
1582
+ args = []
1583
+ opts = []
1584
+ arguments.each do |arg|
1585
+ case arg
1586
+ when Hash
1587
+ opts << arg
1588
+ else
1589
+ args << arg
1590
+ end
1591
+ end
1592
+ opts.each{|opt| parse_options opt}
1593
+ files = args.flatten.compact
1594
+
1595
+ #
1596
+ # If a block is not given to the `find' method, found files are
1597
+ # recorded in this array.
1598
+ #
1599
+ @dirname_stats.clear
1600
+ @found_files.clear
1601
+
1602
+ #
1603
+ # Loop for each file in `files'.
1604
+ #
1605
+ files.each do |file|
1606
+ catch(:prune) do
1607
+ @dirname_stats.clear
1608
+
1609
+ #
1610
+ # Get `stat' or `lstat' of `file'.
1611
+ #
1612
+ begin
1613
+ if @follow
1614
+ begin
1615
+ stat_result = File.stat(file)
1616
+ rescue Errno::ENOENT, Errno::EACCES
1617
+ stat_result = File.lstat(file)
1618
+ end
1619
+ else
1620
+ stat_result = File.lstat(file)
1621
+ end
1622
+ rescue Errno::ENOENT, Errno::EACCES
1623
+ next
1624
+ end
1625
+
1626
+ #
1627
+ # Push `file' to the found stack, or yield with it,
1628
+ # if the depth flag is enabled.
1629
+ #
1630
+ @handle[ file, stat_result, block ] if !@depth
1631
+
1632
+ #
1633
+ # If `file' is a directory, find files recursively.
1634
+ #
1635
+ if stat_result.directory?
1636
+ @xdev_device = stat_result.dev if @xdev
1637
+ @dirname_stats.push(stat_result)
1638
+ find_directory(file, block)
1639
+ end
1640
+
1641
+ #
1642
+ # Push `file' to the found stack, or yield with it.
1643
+ # if the depth flag is disabled.
1644
+ #
1645
+ @handle[ file, stat_result, block ] if @depth
1646
+
1647
+ end
1648
+ end
1649
+
1650
+ if block == nil
1651
+ return @found_files
1652
+ else
1653
+ return nil
1654
+ end
1655
+ #--}}}
1656
+ end
1657
+ #
1658
+ # Find the directory `dirname'. (private)
1659
+ #
1660
+ def find_directory(dirname, block)
1661
+ #--{{{
1662
+ #
1663
+ # Open the directory `dirname'.
1664
+ #
1665
+ begin
1666
+ dir = Dir.open(dirname)
1667
+ rescue
1668
+ return
1669
+ end
1670
+
1671
+ #
1672
+ # Read all directory entries except for `.' and `..'.
1673
+ #
1674
+ entries = Array.new
1675
+ loop do
1676
+ begin
1677
+ entry = dir.read
1678
+ rescue
1679
+ break
1680
+ end
1681
+ break if entry == nil
1682
+ next if entry == '.' || entry == '..'
1683
+ entries.push(entry)
1684
+ end
1685
+
1686
+ #
1687
+ # Close `dir'.
1688
+ #
1689
+ dir.close
1690
+
1691
+ #
1692
+ # Loop for each entry in this directory.
1693
+ #
1694
+ catch(:prune) do
1695
+ entries.each do |entry|
1696
+ #
1697
+ # Set `entry_path' to the full path of `entry'.
1698
+ #
1699
+ if dirname[-1, 1] == File::Separator
1700
+ entry_path = dirname + entry
1701
+ else
1702
+ entry_path = dirname + File::Separator + entry
1703
+ end
1704
+
1705
+ #
1706
+ # Get `stat' or `lstat' of `entry_path'.
1707
+ #
1708
+ begin
1709
+ if @follow
1710
+ begin
1711
+ stat_result = File.stat(entry_path)
1712
+ rescue Errno::ENOENT, Errno::EACCES
1713
+ stat_result = File.lstat(entry_path)
1714
+ end
1715
+ else
1716
+ stat_result = File.lstat(entry_path)
1717
+ end
1718
+ rescue Errno::ENOENT, Errno::EACCES
1719
+ next
1720
+ end
1721
+
1722
+ #
1723
+ # Push `entry_path' to the found stack, or yield with it,
1724
+ # if the depth flag is enabled.
1725
+ #
1726
+
1727
+ @handle[ entry_path, stat_result, block ] if !@depth
1728
+
1729
+ #
1730
+ # If `entry_path' is a directory, find recursively.
1731
+ #
1732
+ if stat_result.directory? \
1733
+ && (!@xdev || @xdev_device == stat_result.dev) \
1734
+ && (!@follow || !visited?(stat_result))
1735
+ @dirname_stats.push(stat_result)
1736
+ find_directory(entry_path, block)
1737
+ @dirname_stats.pop
1738
+ end
1739
+
1740
+ #
1741
+ # Push `entry_path' to the found stack, or yield with it,
1742
+ # if the depth flag is disabled.
1743
+ #
1744
+ @handle[ entry_path, stat_result, block ] if @depth
1745
+
1746
+ end
1747
+ end
1748
+ #--}}}
1749
+ end
1750
+ private :find_directory
1751
+ #
1752
+ # Find the directory `dirname'. (class method)
1753
+ #
1754
+ def Find2.find(*arguments, &block)
1755
+ Find2.new.find(*arguments, &block)
1756
+ end
1757
+ #
1758
+ # Find the directory `dirname'. (class method)
1759
+ #
1760
+ def Find2.find2(*arguments, &block)
1761
+ Find2.new(FIND2).find(*arguments, &block)
1762
+ end
1763
+ #
1764
+ # Parse options.
1765
+ #
1766
+ def parse_options(options)
1767
+ #--{{{
1768
+ return if options == nil
1769
+
1770
+ options.each_pair do |key, value|
1771
+ case "#{ key }".strip.downcase
1772
+ when 'depth'
1773
+ @depth = value
1774
+ when 'follow'
1775
+ @follow = value
1776
+ when 'xdev'
1777
+ @xdev = value
1778
+ else
1779
+ raise ArgumentError, "unknown option - #{key}"
1780
+ end
1781
+ end
1782
+ #--}}}
1783
+ end
1784
+ private :parse_options
1785
+ #
1786
+ # Prune the current visited directory.
1787
+ #
1788
+ def Find2.prune
1789
+ #--{{{
1790
+ throw :prune
1791
+ #--}}}
1792
+ end
1793
+ #
1794
+ # Did we visit the directory with the resouce `stat'? (private)
1795
+ #
1796
+ def visited?(stat_result)
1797
+ #--{{{
1798
+ dev = stat_result.dev
1799
+ ino = stat_result.ino
1800
+ @dirname_stats.each do |i|
1801
+ return TRUE if i.dev == dev && i.ino == ino
1802
+ end
1803
+ return FALSE
1804
+ #--}}}
1805
+ end
1806
+ private :visited?
1807
+ #--}}}
1808
+ end # class Find2
1809
+ Find = Find2
1810
+ ALib::export Find
1811
+ ALib::export Find2
1812
+
1813
+ #
1814
+ # Ruby/Bsearch - a binary search library for Ruby.
1815
+ #
1816
+ # Copyright (C) 2001 Satoru Takabayashi <satoru@namazu.org>
1817
+ # All rights reserved.
1818
+ # This is free software with ABSOLUTELY NO WARRANTY.
1819
+ #
1820
+ # You can redistribute it and/or modify it under the terms of
1821
+ # the Ruby's licence.
1822
+ #
1823
+ # Example:
1824
+ #
1825
+ # % irb -r ./bsearch.rb
1826
+ # >> %w(a b c c c d e f).bsearch_first {|x| x <=> "c"}
1827
+ # => 2
1828
+ # >> %w(a b c c c d e f).bsearch_last {|x| x <=> "c"}
1829
+ # => 4
1830
+ # >> %w(a b c e f).bsearch_first {|x| x <=> "c"}
1831
+ # => 2
1832
+ # >> %w(a b e f).bsearch_first {|x| x <=> "c"}
1833
+ # => nil
1834
+ # >> %w(a b e f).bsearch_last {|x| x <=> "c"}
1835
+ # => nil
1836
+ # >> %w(a b e f).bsearch_lower_boundary {|x| x <=> "c"}
1837
+ # => 2
1838
+ # >> %w(a b e f).bsearch_upper_boundary {|x| x <=> "c"}
1839
+ # => 2
1840
+ # >> %w(a b c c c d e f).bsearch_range {|x| x <=> "c"}
1841
+ # => 2...5
1842
+ # >> %w(a b c d e f).bsearch_range {|x| x <=> "c"}
1843
+ # => 2...3
1844
+ # >> %w(a b d e f).bsearch_range {|x| x <=> "c"}
1845
+ # => 2...2
1846
+
1847
+ module Bsearch
1848
+ #--{{{
1849
+ VERSION = '1.5'
1850
+
1851
+ module BsearchMethods
1852
+ #--{{{
1853
+ #
1854
+ # The binary search algorithm is extracted from Jon Bentley's
1855
+ # Programming Pearls 2nd ed. p.93
1856
+ #
1857
+
1858
+ #
1859
+ # Return the lower boundary. (inside)
1860
+ #
1861
+ # old
1862
+ def bsearch_lower_boundary (range = 0 ... self.length, &block)
1863
+ #--{{{
1864
+ lower = range.first() -1
1865
+ upper = if range.exclude_end? then range.last else range.last + 1 end
1866
+ while lower + 1 != upper
1867
+ mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
1868
+ if yield(self[mid]) < 0
1869
+ lower = mid
1870
+ else
1871
+ upper = mid
1872
+ end
1873
+ end
1874
+ return upper
1875
+ #--}}}
1876
+ end
1877
+ # new
1878
+ def bsearch_lower_boundary (arg = nil, &block)
1879
+ #--{{{
1880
+ range = nil
1881
+ if arg and not block
1882
+ block = lambda{|x| x <=> arg}
1883
+ range = 0 ... self.length
1884
+ end
1885
+ range ||= (arg ? arg : (0...self.length))
1886
+
1887
+ lower = range.first() -1
1888
+ upper = if range.exclude_end? then range.last else range.last + 1 end
1889
+ while lower + 1 != upper
1890
+ mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
1891
+ if block[ self[mid] ] < 0
1892
+ lower = mid
1893
+ else
1894
+ upper = mid
1895
+ end
1896
+ end
1897
+ return upper
1898
+ #--}}}
1899
+ end
1900
+
1901
+ #
1902
+ # This method searches the FIRST occurrence which satisfies a
1903
+ # condition given by a block in binary fashion and return the
1904
+ # index of the first occurrence. Return nil if not found.
1905
+ #
1906
+ # old
1907
+ def bsearch_first (range = 0 ... self.length, &block)
1908
+ #--{{{
1909
+ boundary = bsearch_lower_boundary(range, &block)
1910
+ if boundary >= self.length || yield(self[boundary]) != 0
1911
+ return nil
1912
+ else
1913
+ return boundary
1914
+ end
1915
+ #--}}}
1916
+ end
1917
+ # new
1918
+ def bsearch_first (arg = nil, &block)
1919
+ #--{{{
1920
+ range = nil
1921
+ if arg and not block
1922
+ block = lambda{|x| x <=> arg}
1923
+ range = 0 ... self.length
1924
+ end
1925
+ range ||= (arg ? arg : (0...self.length))
1926
+ boundary = bsearch_lower_boundary(range, &block)
1927
+ if boundary >= self.length || block[ self[boundary] ] != 0
1928
+ return nil
1929
+ else
1930
+ return boundary
1931
+ end
1932
+ #--}}}
1933
+ end
1934
+
1935
+ # alias bsearch bsearch_first
1936
+ def bsearch(*a, &b)
1937
+ #--{{{
1938
+ unless b
1939
+ obj = a.first
1940
+ b = lambda{|x| x <=> obj}
1941
+ bsearch_first(&b)
1942
+ else
1943
+ bsearch_first(*a, &b)
1944
+ end
1945
+ #--}}}
1946
+ end
1947
+ #
1948
+ # Return the upper boundary. (outside)
1949
+ #
1950
+ # old
1951
+ def bsearch_upper_boundary (range = 0 ... self.length, &block)
1952
+ #--{{{
1953
+ lower = range.first() -1
1954
+ upper = if range.exclude_end? then range.last else range.last + 1 end
1955
+ while lower + 1 != upper
1956
+ mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
1957
+ if yield(self[mid]) <= 0
1958
+ lower = mid
1959
+ else
1960
+ upper = mid
1961
+ end
1962
+ end
1963
+ return lower + 1 # outside of the matching range.
1964
+ #--}}}
1965
+ end
1966
+ # new
1967
+ def bsearch_upper_boundary (arg = nil, &block)
1968
+ #--{{{
1969
+ range = nil
1970
+ if arg and not block
1971
+ block = lambda{|x| x <=> arg}
1972
+ range = 0 ... self.length
1973
+ end
1974
+ range ||= (arg ? arg : (0...self.length))
1975
+
1976
+ lower = range.first() -1
1977
+ upper = if range.exclude_end? then range.last else range.last + 1 end
1978
+ while lower + 1 != upper
1979
+ mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
1980
+ if block[ self[mid] ] <= 0
1981
+ lower = mid
1982
+ else
1983
+ upper = mid
1984
+ end
1985
+ end
1986
+ return lower + 1 # outside of the matching range.
1987
+ #--}}}
1988
+ end
1989
+ #
1990
+ # This method searches the LAST occurrence which satisfies a
1991
+ # condition given by a block in binary fashion and return the
1992
+ # index of the last occurrence. Return nil if not found.
1993
+ #
1994
+ # old
1995
+ def bsearch_last (range = 0 ... self.length, &block)
1996
+ #--{{{
1997
+ # `- 1' for canceling `lower + 1' in bsearch_upper_boundary.
1998
+ boundary = bsearch_upper_boundary(range, &block) - 1
1999
+
2000
+ if (boundary <= -1 || yield(self[boundary]) != 0)
2001
+ return nil
2002
+ else
2003
+ return boundary
2004
+ end
2005
+ #--}}}
2006
+ end
2007
+ # new
2008
+ def bsearch_last (arg = nil, &block)
2009
+ #--{{{
2010
+ range = nil
2011
+ if arg and not block
2012
+ block = lambda{|x| x <=> arg}
2013
+ range = 0 ... self.length
2014
+ end
2015
+ range ||= (arg ? arg : (0...self.length))
2016
+
2017
+ # `- 1' for canceling `lower + 1' in bsearch_upper_boundary.
2018
+ boundary = bsearch_upper_boundary(range, &block) - 1
2019
+
2020
+ if (boundary <= -1 || block[ self[boundary] ] != 0)
2021
+ return nil
2022
+ else
2023
+ return boundary
2024
+ end
2025
+ #--}}}
2026
+ end
2027
+ #
2028
+ # Return the search result as a Range object.
2029
+ #
2030
+ # old
2031
+ def bsearch_range (range = 0 ... self.length, &block)
2032
+ #--{{{
2033
+ lower = bsearch_lower_boundary(range, &block)
2034
+ upper = bsearch_upper_boundary(range, &block)
2035
+ return lower ... upper
2036
+ #--}}}
2037
+ end
2038
+ # new
2039
+ def bsearch_range (arg = nil, &block)
2040
+ #--{{{
2041
+ range = nil
2042
+ if arg and not block
2043
+ block = lambda{|x| x <=> arg}
2044
+ range = 0 ... self.length
2045
+ end
2046
+ range ||= (arg ? arg : (0...self.length))
2047
+
2048
+ lower = bsearch_lower_boundary(range, &block)
2049
+ upper = bsearch_upper_boundary(range, &block)
2050
+ return lower ... upper
2051
+ #--}}}
2052
+ end
2053
+ #--}}}
2054
+ end
2055
+
2056
+ if false
2057
+ def Bsearch::append_features klass
2058
+ #--{{{
2059
+ ::Array.module_eval{ include BsearchMethods }
2060
+ super
2061
+ #--}}}
2062
+ end
2063
+ end
2064
+ ::Array.module_eval{ include BsearchMethods }
2065
+ #--}}}
2066
+ end # module Bsearch
2067
+ BSearch = Bsearch
2068
+ ALib::export BSearch
2069
+ ALib::export Bsearch
2070
+
2071
+ #
2072
+ # template main building block classes
2073
+ #
2074
+
2075
+ module MainModule
2076
+ #--{{{
2077
+ module Mixins
2078
+ #--{{{
2079
+ include ALib
2080
+ include ALib::Logging
2081
+ #--}}}
2082
+ end
2083
+ module Constants
2084
+ #--{{{
2085
+ EXIT_SUCCESS = 0
2086
+ EXIT_FAILURE = 1
2087
+ #--}}}
2088
+ end
2089
+ module ClassMethods
2090
+ #--{{{
2091
+ include Mixins
2092
+ include Constants
2093
+ def stringlist(*list)
2094
+ #--{{{
2095
+ list.flatten.compact.map{|item| "#{ item }".strip}
2096
+ #--}}}
2097
+ end
2098
+
2099
+ def instance_attributes(*names)
2100
+ #--{{{
2101
+ names = stringlist names
2102
+ names.each do |name|
2103
+ getter = "#{ name }"
2104
+ setter = "#{ name }="
2105
+ code = <<-code
2106
+ def #{ name }(*a)
2107
+ unless a.empty?
2108
+ self.#{ name }= a.shift
2109
+ else
2110
+ @#{ name }
2111
+ end
2112
+ end
2113
+ def #{ name }= value
2114
+ @#{ name } = value
2115
+ end
2116
+ alias #{ name }? #{ name }
2117
+ code
2118
+ module_eval code
2119
+ end
2120
+ #--}}}
2121
+ end
2122
+ alias instance_attribute instance_attributes
2123
+ alias i_attrs instance_attributes
2124
+ alias i_attr instance_attributes
2125
+ alias attribute instance_attributes
2126
+ alias attributes instance_attributes
2127
+
2128
+ def class_attributes(*names)
2129
+ #--{{{
2130
+ names = stringlist names
2131
+ names.each do |name|
2132
+ getter = "#{ name }"
2133
+ setter = "#{ name }="
2134
+ code = <<-code
2135
+ class << self
2136
+ def #{ name }(*a)
2137
+ unless a.empty?
2138
+ self.#{ name }= a.shift
2139
+ else
2140
+ @#{ name }
2141
+ end
2142
+ end
2143
+ def #{ name }= value
2144
+ @#{ name } = value
2145
+ end
2146
+ alias #{ name }? #{ name }
2147
+ end
2148
+ code
2149
+ module_eval code
2150
+ end
2151
+ #--}}}
2152
+ end
2153
+ alias class_attribute class_attributes
2154
+ alias c_attrs class_attributes
2155
+ alias c_attr class_attributes
2156
+
2157
+ %w( version author program optspec ).each do |meth|
2158
+ #--{{{
2159
+ module_eval <<-code
2160
+ def #{ meth }(*a)
2161
+ unless a.empty?
2162
+ self.#{ meth }= a.shift
2163
+ else
2164
+ @#{ meth }
2165
+ end
2166
+ end
2167
+ def #{ meth }= value
2168
+ @#{ meth } = value
2169
+ end
2170
+ def #{ meth }?
2171
+ defined? @#{ meth } and @#{ meth }
2172
+ end
2173
+ code
2174
+ #--}}}
2175
+ end
2176
+ alias prognam program
2177
+ alias prognam= program=
2178
+ alias prognam? program?
2179
+
2180
+ %w( usage examples ).each do |meth|
2181
+ #--{{{
2182
+ module_eval <<-code
2183
+ def #{ meth }(*a)
2184
+ unless a.empty?
2185
+ self.#{ meth }= a.shift
2186
+ else
2187
+ @#{ meth }
2188
+ end
2189
+ end
2190
+ def #{ meth }= msg
2191
+ @#{ meth } = unindent_block msg
2192
+ end
2193
+ def #{ meth }?
2194
+ defined? @#{ meth } and @#{ meth }
2195
+ end
2196
+ code
2197
+ #--}}}
2198
+ end
2199
+
2200
+ def unindent_block buf
2201
+ #--{{{
2202
+ buf = "#{ buf }"
2203
+ lines = buf.to_a
2204
+ indent_pat = %r/^\s*[\s|]/
2205
+ indent = lines.first[ indent_pat ]
2206
+ if indent
2207
+ lines.map do |line|
2208
+ line[ indent.size..-1 ] || "\n"
2209
+ end.join
2210
+ else
2211
+ buf
2212
+ end
2213
+ #--}}}
2214
+ end
2215
+
2216
+ def options(*list)
2217
+ #--{{{
2218
+ @optspec ||= []
2219
+ return @optspec if list.empty?
2220
+ list = [list] unless Array === list.first
2221
+ list.each{|spec| (@optspec ||= []) << spec}
2222
+ #--}}}
2223
+ end
2224
+ alias option options
2225
+
2226
+ def required_arguments(*list)
2227
+ #--{{{
2228
+ @required_arguments ||= []
2229
+ list.flatten.each do |arg|
2230
+ return(optional_argument(arg)) if Hash === arg
2231
+ unless instance_methods.include? "#{ arg }"
2232
+ module_eval <<-code
2233
+ def #{ arg }(*__list)
2234
+ if __list.empty?
2235
+ @#{ arg }
2236
+ else
2237
+ send('#{ arg }=', *__list)
2238
+ end
2239
+ end
2240
+ def #{ arg }=(__arg, *__list)
2241
+ if __list.empty?
2242
+ @#{ arg } = __arg
2243
+ else
2244
+ @#{ arg } = ([__arg] + __list)
2245
+ end
2246
+ end
2247
+ def #{ arg }?
2248
+ defined? @#{ arg } and @#{ arg }
2249
+ end
2250
+ code
2251
+ end
2252
+ @required_arguments << "#{ arg }"
2253
+ end
2254
+ @required_arguments
2255
+ #--}}}
2256
+ end
2257
+ alias required_argument required_arguments
2258
+ alias arguments required_arguments
2259
+ alias argument required_arguments
2260
+
2261
+ def optional_arguments(*list)
2262
+ #--{{{
2263
+ @optional_arguments ||= []
2264
+ list.flatten.each do |arg|
2265
+ arg, default =
2266
+ case arg
2267
+ when Hash
2268
+ arg.to_a.first
2269
+ else
2270
+ [arg, nil]
2271
+ end
2272
+ @do_not_gc ||= []
2273
+ @do_not_gc << default unless @do_not_gc.include? default
2274
+ unless instance_methods.include? "#{ arg }"
2275
+ module_eval <<-code
2276
+ def #{ arg }(*__list)
2277
+ unless @#{ arg }
2278
+ @#{ arg } = ObjectSpace::_id2ref #{ default.object_id }
2279
+ end
2280
+ if __list.empty?
2281
+ @#{ arg }
2282
+ else
2283
+ send('#{ arg }=', *__list)
2284
+ end
2285
+ end
2286
+ def #{ arg }=(__arg, *__list)
2287
+ if __list.empty?
2288
+ @#{ arg } = __arg
2289
+ else
2290
+ @#{ arg } = ([__arg] + __list)
2291
+ end
2292
+ end
2293
+ def #{ arg }?
2294
+ defined? @#{ arg } and @#{ arg }
2295
+ end
2296
+ code
2297
+ end
2298
+ @optional_arguments << "#{ arg }"
2299
+ end
2300
+ @optional_arguments
2301
+ #--}}}
2302
+ end
2303
+ alias optional_argument optional_arguments
2304
+ def inherited klass
2305
+ #--{{{
2306
+ ret = super
2307
+ begin; klass.class_initialize; rescue NoMethodError; end
2308
+ ret
2309
+ #--}}}
2310
+ end
2311
+ #--}}}
2312
+ end
2313
+ module InstanceMethods
2314
+ #--{{{
2315
+ include Mixins
2316
+ include Constants
2317
+ attr :logger
2318
+ attr :program
2319
+ attr :argv
2320
+ attr :env
2321
+ attr :cmdline
2322
+ attr :console
2323
+ attr :options
2324
+ attr :listoptions
2325
+ attr :op
2326
+ attr :logdev
2327
+ attr :verbosity
2328
+
2329
+ alias console? console
2330
+
2331
+ def initialize argv = ARGV, env = ENV
2332
+ #--{{{
2333
+ @argv = Util::mcp(argv.to_a)
2334
+ @env = Util::mcp(env.to_hash)
2335
+ @program = File::expand_path $0
2336
+ @cmdline = ([@program] + @argv).join ' '
2337
+ @console = STDIN.tty?
2338
+ #--}}}
2339
+ end
2340
+ def klass; self.class; end
2341
+ def required_arguments
2342
+ #--{{{
2343
+ klass::required_arguments
2344
+ #--}}}
2345
+ end
2346
+ def optional_arguments
2347
+ #--{{{
2348
+ klass::optional_arguments
2349
+ #--}}}
2350
+ end
2351
+
2352
+ def run
2353
+ #--{{{
2354
+ logcatch do
2355
+ begin
2356
+ pre_run
2357
+ pre_parse_options
2358
+ parse_options
2359
+ post_parse_options
2360
+ if(@options.has_key?('help') or (@argv.size == 1 and @argv.first =~ %r/help/i))
2361
+ usage STDOUT
2362
+ exit EXIT_SUCCESS
2363
+ end
2364
+ pre_parse_argv
2365
+ parse_argv
2366
+ post_parse_argv
2367
+ init_logging
2368
+ pre_main
2369
+ status = main
2370
+ post_main
2371
+ post_run
2372
+ exit(status ? EXIT_SUCCESS : EXIT_FAILURE)
2373
+ rescue Errno::EPIPE
2374
+ STDOUT.tty? ? raise : exit(EXIT_FAILURE)
2375
+ end
2376
+ end
2377
+ #--}}}
2378
+ end
2379
+ def pre_parse_options; end
2380
+ def post_parse_options; end
2381
+ def pre_parse_argv; end
2382
+ def post_parse_argv; end
2383
+ def pre_run; end
2384
+ def post_run; end
2385
+ def pre_main; end
2386
+ def post_main; end
2387
+ def logcatch
2388
+ #--{{{
2389
+ ret = nil
2390
+ @logger ||= Logger::new STDERR
2391
+ begin
2392
+ ret = yield
2393
+ rescue Exception => e
2394
+ unless SystemExit === e
2395
+ fatal{ e }
2396
+ exit EXIT_FAILURE
2397
+ if false
2398
+ if logger.debug?
2399
+ fatal{ e }
2400
+ exit EXIT_FAILURE
2401
+ else
2402
+ fatal{ emsg(e) }
2403
+ exit EXIT_FAILURE
2404
+ end
2405
+ end
2406
+ else
2407
+ exit e.status
2408
+ end
2409
+ end
2410
+ ret
2411
+ #--}}}
2412
+ end
2413
+ def usage port = STDERR
2414
+ #--{{{
2415
+ port << klass::usage << "\n" if klass::usage
2416
+ port << klass::examples << "\n" if klass::examples
2417
+
2418
+ if klass::optspec
2419
+ port << 'OPTIONS' << "\n"
2420
+
2421
+ klass::optspec.each do |os|
2422
+ a, b, c = os
2423
+ long, short, desc = nil
2424
+ [a,b,c].each do |word|
2425
+ next unless word
2426
+ word.strip!
2427
+ case word
2428
+ when %r/^--[^-]/o
2429
+ long = word
2430
+ when %r/^-[^-]/o
2431
+ short = word
2432
+ else
2433
+ desc = word
2434
+ end
2435
+ end
2436
+
2437
+ spec = ((long and short) ? [long, short] : [long])
2438
+
2439
+ if spec
2440
+ port << Util::columnize(spec.join(', '), :width => 80, :indent => 2)
2441
+ port << "\n"
2442
+ end
2443
+
2444
+ if desc
2445
+ port << Util::columnize(desc, :width => 80, :indent => 8)
2446
+ port << "\n"
2447
+ end
2448
+ end
2449
+
2450
+ port << "\n"
2451
+ end
2452
+
2453
+ port
2454
+ #--}}}
2455
+ end
2456
+ def parse_options
2457
+ #--{{{
2458
+ @op = OptionParser::new
2459
+ @options = {}
2460
+ @listoptions = Hash::new{|h,k| h[k] = []}
2461
+ klass::optspec.each do |spec|
2462
+ k = spec.first.gsub(%r/(?:--)|(?:=.*$)|(?:\s+)/o,'')
2463
+ @op.def_option(*spec) do |v|
2464
+ @options[k] = v
2465
+ @listoptions[k] << v
2466
+ end
2467
+ end
2468
+ #begin
2469
+ op.parse! @argv
2470
+ #rescue OptionParser::InvalidOption => e
2471
+ # preverve unknown options
2472
+ #e.recover(argv)
2473
+ #end
2474
+ @options
2475
+ #--}}}
2476
+ end
2477
+ def init_logging opts = @options
2478
+ #--{{{
2479
+ log = Util::getopt 'log', opts
2480
+ log_age = Util::getopt 'log_age', opts
2481
+ log_size = Util::getopt 'log_size', opts
2482
+ verbosity = Util::getopt 'verbosity', opts
2483
+ log_age = Integer log_age rescue nil
2484
+ log_size = Integer log_size rescue nil
2485
+ $logger = @logger = Logger::new(log || STDERR, log_age, log_size)
2486
+ #
2487
+ # hack to fix Logger sync bug
2488
+ #
2489
+ begin
2490
+ class << @logger; attr :logdev unless @logger.respond_to?(:logdev); end
2491
+ @logdev = @logger.logdev.dev
2492
+ @logdev.sync = true
2493
+ rescue
2494
+ nil
2495
+ end
2496
+ level = nil
2497
+ verbosity ||= 'info'
2498
+ verbosity =
2499
+ case verbosity
2500
+ when /^\s*(?:4|d|debug)\s*$/io
2501
+ level = 'Logging::DEBUG'
2502
+ 4
2503
+ when /^\s*(?:3|i|info)\s*$/io
2504
+ level = 'Logging::INFO'
2505
+ 3
2506
+ when /^\s*(?:2|w|warn)\s*$/io
2507
+ level = 'Logging::WARN'
2508
+ 2
2509
+ when /^\s*(?:1|e|error)\s*$/io
2510
+ level = 'Logging::ERROR'
2511
+ 1
2512
+ when /^\s*(?:0|f|fatal)\s*$/io
2513
+ level = 'Logging::FATAL'
2514
+ 0
2515
+ else
2516
+ abort "illegal verbosity setting <#{ verbosity }>"
2517
+ end
2518
+ @logger.level = 2 - ((verbosity % 5) - 2)
2519
+ @logger
2520
+ #--}}}
2521
+ end
2522
+ def parse_argv
2523
+ #--{{{
2524
+ a, b = [], []
2525
+ klass::required_arguments.each do |arg|
2526
+ value = @argv.shift
2527
+ if value
2528
+ send "#{ arg }=", value
2529
+ else
2530
+ die 'msg' => "required_argument <#{ arg }> not given"
2531
+ end
2532
+ a << send("#{ arg }")
2533
+ end
2534
+ klass::optional_arguments.each do |arg|
2535
+ value = @argv.shift
2536
+ if value
2537
+ send "#{ arg }=", value
2538
+ end
2539
+ b << send("#{ arg }")
2540
+ end
2541
+ [a, b, @argv]
2542
+ #--}}}
2543
+ end
2544
+ def die opts = {}
2545
+ #--{{{
2546
+ msg = Util::getopt 'msg', opts, klass.usage
2547
+ errno = Util::getopt 'errno', opts, EXIT_FAILURE
2548
+ STDERR.puts("#{ msg }")
2549
+ exit(Integer(errno))
2550
+ #--}}}
2551
+ end
2552
+ def main
2553
+ #--{{{
2554
+ raise NotImplementedError, 'you need to define main'
2555
+ #--}}}
2556
+ end
2557
+ #--}}}
2558
+ end
2559
+ module Abilities
2560
+ #--{{{
2561
+ def self::append_features other
2562
+ #--{{{
2563
+ other.extend ClassMethods
2564
+ other.module_eval {
2565
+ include Mixins
2566
+ include Constants
2567
+ include InstanceMethods
2568
+ }
2569
+ #--}}}
2570
+ end
2571
+ #--}}}
2572
+ end
2573
+ class SimpleMain
2574
+ #--{{{
2575
+ include Abilities
2576
+ class << self
2577
+ #--{{{
2578
+ def class_initialize
2579
+ #--{{{
2580
+ version '0.0.0'
2581
+
2582
+ prognam ALib::Util::prognam
2583
+
2584
+ usage <<-usage
2585
+ NAME
2586
+ #{ prognam } v#{ version }
2587
+
2588
+ SYNOPSIS
2589
+ #{ prognam } [options]+ [file]+
2590
+ usage
2591
+
2592
+ optspec [
2593
+ [
2594
+ '--help', '-h',
2595
+ 'this message'
2596
+ ],
2597
+ [
2598
+ '--log=path','-l',
2599
+ 'set log file - (default stderr)'
2600
+ ],
2601
+ [
2602
+ '--verbosity=verbostiy', '-v',
2603
+ '0|fatal < 1|error < 2|warn < 3|info < 4|debug - (default info)'
2604
+ ],
2605
+ =begin
2606
+ [
2607
+ '--config=path',
2608
+ 'valid path - specify config file (default nil)'
2609
+ ],
2610
+ [
2611
+ '--template=[path]',
2612
+ 'valid path - generate a template config file in path (default stdout)'
2613
+ ],
2614
+ =end
2615
+ ]
2616
+ #--}}}
2617
+ end
2618
+ #--}}}
2619
+ end
2620
+ def pre_parse_argv
2621
+ #--{{{
2622
+ if(@options.has_key?('help') or (@argv.size == 1 and @argv.first =~ %r/help/i))
2623
+ usage STDOUT
2624
+ exit EXIT_SUCCESS
2625
+ end
2626
+ if(@options.has_key?('version') or @argv.first =~ %r/version/i)
2627
+ STDOUT.puts self.class.version
2628
+ exit EXIT_SUCCESS
2629
+ end
2630
+ #--}}}
2631
+ end
2632
+ #--}}}
2633
+ end
2634
+ class ConfigurableMain
2635
+ #--{{{
2636
+ include Abilities
2637
+
2638
+ class << self
2639
+ #--{{{
2640
+ def class_initialize
2641
+ #--{{{
2642
+ version '0.0.0'
2643
+
2644
+ prognam ALib::Util::prognam
2645
+
2646
+ configfile Class::new(ALib::ConfigFile) #{ default = DATA; DATA.rewind; }
2647
+
2648
+ config_default_path "#{ prognam }.conf"
2649
+
2650
+ config_search_path %w(. ~ /usr/local/etc /usr/etc /etc)
2651
+
2652
+ usage <<-usage
2653
+ NAME
2654
+ #{ prognam } v#{ version }
2655
+
2656
+ SYNOPSIS
2657
+ #{ prognam } [options]+ [file]+
2658
+
2659
+ CONFIG
2660
+ default path -> #{ config_default_path }
2661
+ search path -> #{ config_search_path.join ' ' }
2662
+ usage
2663
+
2664
+ optspec [
2665
+ [
2666
+ '--help', '-h',
2667
+ 'this message'
2668
+ ],
2669
+ [
2670
+ '--log=path','-l',
2671
+ 'set log file - (default stderr)'
2672
+ ],
2673
+ [
2674
+ '--verbosity=verbostiy', '-v',
2675
+ '0|fatal < 1|error < 2|warn < 3|info < 4|debug - (default info)'
2676
+ ],
2677
+ [
2678
+ '--config=path',
2679
+ 'valid path - specify config file (default nil)'
2680
+ ],
2681
+ [
2682
+ '--template=[path]',
2683
+ 'valid path - generate a template config file in path (default stdout)'
2684
+ ],
2685
+ ]
2686
+ #--}}}
2687
+ end
2688
+ #--}}}
2689
+ end
2690
+
2691
+ class_attributes :config_default_path, :config_search_path, :configfile
2692
+ attribute :config
2693
+
2694
+ def pre_parse_argv
2695
+ #--{{{
2696
+ if(@options.has_key?('help') or @argv.first =~ %r/help/i)
2697
+ usage STDOUT
2698
+ exit EXIT_SUCCESS
2699
+ end
2700
+ if(@options.has_key?('version') or @argv.first =~ %r/version/i)
2701
+ STDOUT.puts self.class.version
2702
+ exit EXIT_SUCCESS
2703
+ end
2704
+ if(@options.has_key?('template') or @argv.first =~ %r/template/i)
2705
+ @argv.shift if @argv.first =~ %r/template/i
2706
+ arg = @argv.first
2707
+ gen_template @options['template'] || @options['config'] || arg
2708
+ exit EXIT_SUCCESS
2709
+ end
2710
+ #--}}}
2711
+ end
2712
+ def gen_template template
2713
+ #--{{{
2714
+ klass::configfile::gen_template(template)
2715
+ self
2716
+ #--}}}
2717
+ end
2718
+ def post_parse_argv
2719
+ #--{{{
2720
+ init_config
2721
+ #--}}}
2722
+ end
2723
+ def init_config opts = @options
2724
+ #--{{{
2725
+ conf = Util::getopt 'config', opts
2726
+ @config =
2727
+ if conf
2728
+ klass::configfile::new(conf)
2729
+ else
2730
+ klass::configfile::any klass::config_default_path, klass::config_search_path
2731
+ end
2732
+ @config
2733
+ #--}}}
2734
+ end
2735
+ #--}}}
2736
+ end
2737
+ #--}}}
2738
+ end # module MainModule
2739
+
2740
+ class << self
2741
+ #--{{{
2742
+ def simple_main &b
2743
+ #--{{{
2744
+ if b
2745
+ Class::new(MainModule::SimpleMain, &b)
2746
+ else
2747
+ MainModule::SimpleMain
2748
+ end
2749
+ #--}}}
2750
+ end
2751
+ def configurable_main &b
2752
+ #--{{{
2753
+ if b
2754
+ Class::new(MainModule::ConfigurableMain, &b)
2755
+ else
2756
+ MainModule::ConfigurableMain
2757
+ end
2758
+ #--}}}
2759
+ end
2760
+ #--}}}
2761
+ end
2762
+
2763
+ SimpleMain = simple_main
2764
+ ConfigurableMain = configurable_main
2765
+
2766
+ =begin
2767
+ class SimpleMain
2768
+ #--{{{
2769
+ include ALib
2770
+ include ALib::Logging
2771
+
2772
+ EXIT_SUCCESS = 0
2773
+ EXIT_FAILURE = 1
2774
+
2775
+ class << self
2776
+ #--{{{
2777
+ def stringlist(*list)
2778
+ #--{{{
2779
+ list.flatten.compact.map{|item| "#{ item }".strip}
2780
+ #--}}}
2781
+ end
2782
+ def instance_attribute(*names)
2783
+ #--{{{
2784
+ names = stringlist names
2785
+ names.each do |name|
2786
+ getter = "#{ name }"
2787
+ setter = "#{ name }="
2788
+ code = <<-code
2789
+ def #{ name }(*a)
2790
+ unless a.empty?
2791
+ self.#{ name }= a.shift
2792
+ else
2793
+ @#{ name }
2794
+ end
2795
+ end
2796
+ def #{ name }= value
2797
+ @#{ name } = value
2798
+ end
2799
+ alias #{ name }? #{ name }
2800
+ code
2801
+ module_eval code
2802
+ end
2803
+ #--}}}
2804
+ end
2805
+ alias instance_attributes instance_attribute
2806
+ alias i_attrs instance_attribute
2807
+ alias i_attr instance_attribute
2808
+ def class_attribute(*names)
2809
+ #--{{{
2810
+ names = stringlist names
2811
+ names.each do |name|
2812
+ getter = "#{ name }"
2813
+ setter = "#{ name }="
2814
+ code = <<-code
2815
+ class << self
2816
+ def #{ name }(*a)
2817
+ unless a.empty?
2818
+ self.#{ name }= a.shift
2819
+ else
2820
+ @#{ name }
2821
+ end
2822
+ end
2823
+ def #{ name }= value
2824
+ @#{ name } = value
2825
+ end
2826
+ alias #{ name }? #{ name }
2827
+ end
2828
+ code
2829
+ module_eval code
2830
+ end
2831
+ #--}}}
2832
+ end
2833
+ alias class_attributes class_attribute
2834
+ alias c_attrs class_attribute
2835
+ alias c_attr class_attribute
2836
+ def usage(*a)
2837
+ #--{{{
2838
+ unless a.empty?
2839
+ self.usage = a.shift
2840
+ else
2841
+ @usage
2842
+ end
2843
+ #--}}}
2844
+ end
2845
+ def usage= block
2846
+ #--{{{
2847
+ @usage = unindent_block block
2848
+ #--}}}
2849
+ end
2850
+ alias usage? usage
2851
+ def examples(*a)
2852
+ #--{{{
2853
+ unless a.empty?
2854
+ self.examples = a.shift
2855
+ else
2856
+ @examples
2857
+ end
2858
+ #--}}}
2859
+ end
2860
+ def examples= block
2861
+ #--{{{
2862
+ @examples = unindent_block block
2863
+ #--}}}
2864
+ end
2865
+ alias examples? examples
2866
+ def unindent_block buf
2867
+ #--{{{
2868
+ buf = "#{ buf }"
2869
+ lines = buf.to_a
2870
+ indent_pat = %r/^\s*[\s|]/
2871
+ indent = lines.first[ indent_pat ]
2872
+ if indent
2873
+ lines.map do |line|
2874
+ line[ indent.size..-1 ] || "\n"
2875
+ end.join
2876
+ else
2877
+ buf
2878
+ end
2879
+ #--}}}
2880
+ end
2881
+ def options(*list)
2882
+ #--{{{
2883
+ list.each{|args| (@optspec ||= []) << args}
2884
+ #--}}}
2885
+ end
2886
+ def option(*args)
2887
+ #--{{{
2888
+ options args.flatten
2889
+ #--}}}
2890
+ end
2891
+ def required_arguments(*list)
2892
+ #--{{{
2893
+ @required_arguments ||= []
2894
+ list.flatten.each{|arg| @required_arguments << "#{ arg }"}
2895
+ @required_arguments
2896
+ #--}}}
2897
+ end
2898
+ alias required_argument required_arguments
2899
+ alias arguments required_arguments
2900
+ alias argument required_arguments
2901
+ def optional_arguments(*list)
2902
+ #--{{{
2903
+ @optional_arguments ||= []
2904
+ list.flatten.each{|arg| @optional_arguments << "#{ arg }"}
2905
+ @optional_arguments
2906
+ #--}}}
2907
+ end
2908
+ alias optional_argument optional_arguments
2909
+
2910
+ def option(*args)
2911
+ #--{{{
2912
+ options args.flatten
2913
+ #--}}}
2914
+ end
2915
+ def init
2916
+ #--{{{
2917
+ version '0.0.0'
2918
+ author 'ara.t.howard@noaa.gov'
2919
+ prognam ALib::Util::prognam
2920
+
2921
+ usage <<-usage
2922
+ NAME
2923
+ #{ prognam } v#{ version }
2924
+
2925
+ SYNOPSIS
2926
+ #{ prognam } [options]+ [file]+
2927
+
2928
+ DESCRIPTTION
2929
+
2930
+ ENVIRONMENT
2931
+
2932
+ DIAGNOSTICS
2933
+ success -> $? == 0
2934
+ failure -> $? != 0
2935
+
2936
+ AUTHOR
2937
+ #{ author }
2938
+
2939
+ BUGS
2940
+ > 1
2941
+ usage
2942
+
2943
+ examples <<-examples
2944
+ EXAMPLES
2945
+
2946
+ 0) #{ prognam }
2947
+ examples
2948
+
2949
+ optspec [
2950
+ [
2951
+ '--help', '-h',
2952
+ 'this message'
2953
+ ],
2954
+ [
2955
+ '--log=path','-l',
2956
+ 'set log file - (default stderr)'
2957
+ ],
2958
+ [
2959
+ '--verbosity=verbostiy', '-v',
2960
+ '0|fatal < 1|error < 2|warn < 3|info < 4|debug - (default info)'
2961
+ ],
2962
+ [
2963
+ '--config=path',
2964
+ 'valid path - specify config file (default nil)'
2965
+ ],
2966
+ [
2967
+ '--template=[path]',
2968
+ 'valid path - generate a template config file in path (default stdout)'
2969
+ ],
2970
+ ]
2971
+ #--}}}
2972
+ end
2973
+ def inherited klass
2974
+ #--{{{
2975
+ ret = super
2976
+ klass.init
2977
+ ret
2978
+ #--}}}
2979
+ end
2980
+ #--}}}
2981
+ end
2982
+
2983
+ class_attribute :version
2984
+ class_attribute :author
2985
+ class_attribute :prognam
2986
+ class_attribute :optspec
2987
+
2988
+ self::init
2989
+
2990
+ attr :logger
2991
+ attr :argv
2992
+ attr :env
2993
+ attr :cmdline
2994
+ attr :options
2995
+ attr :op
2996
+ attr :logdev
2997
+ attr :verbosity
2998
+
2999
+ def initialize argv = ARGV, env = ENV
3000
+ #--{{{
3001
+ @argv = Util::mcp(argv.to_a)
3002
+ @env = Util::mcp(env.to_hash)
3003
+ @cmdline = ([$0] + @argv).join ' '
3004
+ #--}}}
3005
+ end
3006
+ def klass; self.class; end
3007
+ def required_arguments
3008
+ #--{{{
3009
+ klass.required_arguments
3010
+ #--}}}
3011
+ end
3012
+ def optional_arguments
3013
+ #--{{{
3014
+ klass.optional_arguments
3015
+ #--}}}
3016
+ end
3017
+ def run
3018
+ #--{{{
3019
+ logcatch do
3020
+ parse_options
3021
+ begin
3022
+ if(@options.has_key?('help') or @argv.first =~ %r/help/i)
3023
+ usage STDOUT
3024
+ exit EXIT_SUCCESS
3025
+ end
3026
+ parse_argv
3027
+ init_logging
3028
+ status = main
3029
+ exit(status ? EXIT_SUCCESS : EXIT_FAILURE)
3030
+ rescue Errno::EPIPE
3031
+ STDOUT.tty? ? raise : exit(EXIT_FAILURE)
3032
+ end
3033
+ end
3034
+ #--}}}
3035
+ end
3036
+ def logcatch
3037
+ #--{{{
3038
+ ret = nil
3039
+ @logger ||= Logger::new STDERR
3040
+ begin
3041
+ ret = yield
3042
+ rescue => e
3043
+ unless SystemExit === e
3044
+ if logger.debug?
3045
+ fatal{ e }
3046
+ exit EXIT_FAILURE
3047
+ else
3048
+ fatal{ emsg(e) }
3049
+ exit EXIT_FAILURE
3050
+ end
3051
+ else
3052
+ exit e.status
3053
+ end
3054
+ end
3055
+ ret
3056
+ #--}}}
3057
+ end
3058
+ def usage port = STDERR
3059
+ #--{{{
3060
+ port << klass::usage << "\n" if klass::usage
3061
+ port << klass::examples << "\n" if klass::examples
3062
+
3063
+ if klass::optspec
3064
+ port << 'OPTIONS' << "\n"
3065
+
3066
+ klass::optspec.each do |os|
3067
+ a, b, c = os
3068
+ long, short, desc = nil
3069
+ [a,b,c].each do |word|
3070
+ next unless word
3071
+ word.strip!
3072
+ case word
3073
+ when %r/^--[^-]/o
3074
+ long = word
3075
+ when %r/^-[^-]/o
3076
+ short = word
3077
+ else
3078
+ desc = word
3079
+ end
3080
+ end
3081
+
3082
+ spec = ((long and short) ? [long, short] : [long])
3083
+
3084
+ if spec
3085
+ port << Util::columnize(spec.join(', '), :width => 80, :indent => 2)
3086
+ port << "\n"
3087
+ end
3088
+
3089
+ if desc
3090
+ port << Util::columnize(desc, :width => 80, :indent => 8)
3091
+ port << "\n"
3092
+ end
3093
+ end
3094
+
3095
+ port << "\n"
3096
+ end
3097
+
3098
+ port
3099
+ #--}}}
3100
+ end
3101
+ def parse_options
3102
+ #--{{{
3103
+ @op = OptionParser::new
3104
+ @options = {}
3105
+ @listoptions = Hash::new{|h,k| h[k] = []}
3106
+ klass::optspec.each do |spec|
3107
+ k = spec.first.gsub(%r/(?:--)|(?:=.*$)|(?:\s+)/o,'')
3108
+ @op.def_option(*spec) do |v|
3109
+ @options[k] = v
3110
+ @listoptions[k] << v
3111
+ end
3112
+ end
3113
+ #begin
3114
+ op.parse! @argv
3115
+ #rescue OptionParser::InvalidOption => e
3116
+ # preverve unknown options
3117
+ #e.recover(argv)
3118
+ #end
3119
+ @options
3120
+ #--}}}
3121
+ end
3122
+ def init_logging
3123
+ #--{{{
3124
+ log, log_age, log_size, verbosity =
3125
+ @options.values_at 'log', 'log_age', 'log_size', 'verbosity'
3126
+ log_age = Integer log_age rescue nil
3127
+ log_size = Integer log_size rescue nil
3128
+ $logger = @logger = Logger::new(log || STDERR, log_age, log_size)
3129
+ #
3130
+ # hack to fix Logger sync bug
3131
+ #
3132
+ begin
3133
+ class << @logger; attr :logdev unless @logger.respond_to?(:logdev); end
3134
+ @logdev = @logger.logdev.dev
3135
+ @logdev.sync = true
3136
+ rescue
3137
+ nil
3138
+ end
3139
+ level = nil
3140
+ verbosity ||= 'info'
3141
+ verbosity =
3142
+ case verbosity
3143
+ when /^\s*(?:4|d|debug)\s*$/io
3144
+ level = 'Logging::DEBUG'
3145
+ 4
3146
+ when /^\s*(?:3|i|info)\s*$/io
3147
+ level = 'Logging::INFO'
3148
+ 3
3149
+ when /^\s*(?:2|w|warn)\s*$/io
3150
+ level = 'Logging::WARN'
3151
+ 2
3152
+ when /^\s*(?:1|e|error)\s*$/io
3153
+ level = 'Logging::ERROR'
3154
+ 1
3155
+ when /^\s*(?:0|f|fatal)\s*$/io
3156
+ level = 'Logging::FATAL'
3157
+ 0
3158
+ else
3159
+ abort "illegal verbosity setting <#{ verbosity }>"
3160
+ end
3161
+ @logger.level = 2 - ((verbosity % 5) - 2)
3162
+ @logger
3163
+ #--}}}
3164
+ end
3165
+ def parse_argv
3166
+ #--{{{
3167
+ klass.required_arguments.each do |arg|
3168
+ value = @argv.shift
3169
+ if value
3170
+ klass.module_eval <<-code
3171
+ def #{ arg }(*__list)
3172
+ if __list.empty?
3173
+ @#{ arg }
3174
+ else
3175
+ send('#{ arg }=', *__list)
3176
+ end
3177
+ end
3178
+ def #{ arg }=(__arg, *__list)
3179
+ if __list.empty?
3180
+ @#{ arg } = __arg
3181
+ else
3182
+ @#{ arg } = ([__arg] + __list)
3183
+ end
3184
+ end
3185
+ code
3186
+ send "#{ arg }=", value
3187
+ else
3188
+ die 'msg' => "required_argument <#{ arg }> not given"
3189
+ end
3190
+ end
3191
+
3192
+ klass.optional_arguments.each do |arg|
3193
+ value = @argv.shift
3194
+ break unless value
3195
+ if value
3196
+ klass.module_eval <<-code
3197
+ def #{ arg }(*__list)
3198
+ if __list.empty?
3199
+ @#{ arg }
3200
+ else
3201
+ send('#{ arg }=', *__list)
3202
+ end
3203
+ end
3204
+ def #{ arg }=(__arg, *__list)
3205
+ if __list.empty?
3206
+ @#{ arg } = __arg
3207
+ else
3208
+ @#{ arg } = ([__arg] + __list)
3209
+ end
3210
+ end
3211
+ code
3212
+ send "#{ arg }=", value
3213
+ end
3214
+ end
3215
+
3216
+ @argv
3217
+ #--}}}
3218
+ end
3219
+ def die opts = {}
3220
+ #--{{{
3221
+ msg = Util::getopt 'msg', opts, klass.usage
3222
+ errno = Util::getopt 'errno', opts, EXIT_FAILURE
3223
+ STDERR.puts("#{ msg }")
3224
+ exit(Integer(errno))
3225
+ #--}}}
3226
+ end
3227
+ def main
3228
+ #--{{{
3229
+ #--}}}
3230
+ end
3231
+ #--}}}
3232
+ end
3233
+ class ConfigurableMain
3234
+ #--{{{
3235
+ include ALib
3236
+ include ALib::Logging
3237
+
3238
+ EXIT_SUCCESS = 0
3239
+ EXIT_FAILURE = 1
3240
+
3241
+ class << self
3242
+ #--{{{
3243
+ def stringlist(*list)
3244
+ #--{{{
3245
+ list.flatten.compact.map{|item| "#{ item }".strip}
3246
+ #--}}}
3247
+ end
3248
+ def instance_attribute(*names)
3249
+ #--{{{
3250
+ names = stringlist names
3251
+ names.each do |name|
3252
+ getter = "#{ name }"
3253
+ setter = "#{ name }="
3254
+ code = <<-code
3255
+ def #{ name }(*a)
3256
+ unless a.empty?
3257
+ self.#{ name }= a.shift
3258
+ else
3259
+ @#{ name }
3260
+ end
3261
+ end
3262
+ def #{ name }= value
3263
+ @#{ name } = value
3264
+ end
3265
+ alias #{ name }? #{ name }
3266
+ code
3267
+ module_eval code
3268
+ end
3269
+ #--}}}
3270
+ end
3271
+ alias instance_attributes instance_attribute
3272
+ alias i_attrs instance_attribute
3273
+ alias i_attr instance_attribute
3274
+ def class_attribute(*names)
3275
+ #--{{{
3276
+ names = stringlist names
3277
+ names.each do |name|
3278
+ getter = "#{ name }"
3279
+ setter = "#{ name }="
3280
+ code = <<-code
3281
+ class << self
3282
+ def #{ name }(*a)
3283
+ unless a.empty?
3284
+ self.#{ name }= a.shift
3285
+ else
3286
+ @#{ name }
3287
+ end
3288
+ end
3289
+ def #{ name }= value
3290
+ @#{ name } = value
3291
+ end
3292
+ alias #{ name }? #{ name }
3293
+ end
3294
+ code
3295
+ module_eval code
3296
+ end
3297
+ #--}}}
3298
+ end
3299
+ alias class_attributes class_attribute
3300
+ alias c_attrs class_attribute
3301
+ alias c_attr class_attribute
3302
+ def usage(*a)
3303
+ #--{{{
3304
+ unless a.empty?
3305
+ self.usage = a.shift
3306
+ else
3307
+ @usage
3308
+ end
3309
+ #--}}}
3310
+ end
3311
+ def usage= block
3312
+ #--{{{
3313
+ @usage = unindent_block block
3314
+ #--}}}
3315
+ end
3316
+ alias usage? usage
3317
+ def examples(*a)
3318
+ #--{{{
3319
+ unless a.empty?
3320
+ self.examples = a.shift
3321
+ else
3322
+ @examples
3323
+ end
3324
+ #--}}}
3325
+ end
3326
+ def examples= block
3327
+ #--{{{
3328
+ @examples = unindent_block block
3329
+ #--}}}
3330
+ end
3331
+ alias examples? examples
3332
+ def unindent_block buf
3333
+ #--{{{
3334
+ buf = "#{ buf }"
3335
+ lines = buf.to_a
3336
+ indent_pat = %r/^\s*[\s|]/
3337
+ indent = lines.first[ indent_pat ]
3338
+ if indent
3339
+ lines.map do |line|
3340
+ line[ indent.size..-1 ] || "\n"
3341
+ end.join
3342
+ else
3343
+ buf
3344
+ end
3345
+ #--}}}
3346
+ end
3347
+ def options(*list)
3348
+ #--{{{
3349
+ list.each{|args| (@optspec ||= []) << args}
3350
+ #--}}}
3351
+ end
3352
+ def option(*args)
3353
+ #--{{{
3354
+ options args.flatten
3355
+ #--}}}
3356
+ end
3357
+ def required_arguments(*list)
3358
+ #--{{{
3359
+ @required_arguments ||= []
3360
+ list.flatten.each{|arg| @required_arguments << "#{ arg }"}
3361
+ @required_arguments
3362
+ #--}}}
3363
+ end
3364
+ alias required_argument required_arguments
3365
+ alias arguments required_arguments
3366
+ alias argument required_arguments
3367
+ def optional_arguments(*list)
3368
+ #--{{{
3369
+ @optional_arguments ||= []
3370
+ list.flatten.each{|arg| @optional_arguments << "#{ arg }"}
3371
+ @optional_arguments
3372
+ #--}}}
3373
+ end
3374
+ alias optional_argument optional_arguments
3375
+ def init
3376
+ #--{{{
3377
+ version '0.0.0'
3378
+ author 'ara.t.howard@noaa.gov'
3379
+ prognam ALib::Util::prognam
3380
+ configfile Class::new(ALib::ConfigFile) #{ default = DATA; DATA.rewind; }
3381
+ config_default_path "#{ prognam }.conf"
3382
+ config_search_path %w(. ~ /usr/local/etc /usr/etc /etc)
3383
+
3384
+ usage <<-usage
3385
+ NAME
3386
+ #{ prognam } v#{ version }
3387
+
3388
+ SYNOPSIS
3389
+ #{ prognam } [options]+ [file]+
3390
+
3391
+ DESCRIPTTION
3392
+
3393
+ ENVIRONMENT
3394
+
3395
+ CONFIG
3396
+ default path -> #{ config_default_path }
3397
+ search path -> #{ config_search_path.join ' ' }
3398
+
3399
+ DIAGNOSTICS
3400
+ success -> $? == 0
3401
+ failure -> $? != 0
3402
+
3403
+ AUTHOR
3404
+ #{ author }
3405
+
3406
+ BUGS
3407
+ > 1
3408
+ usage
3409
+
3410
+ examples <<-examples
3411
+ EXAMPLES
3412
+
3413
+ 0) #{ prognam }
3414
+ examples
3415
+
3416
+ optspec [
3417
+ [
3418
+ '--help', '-h',
3419
+ 'this message'
3420
+ ],
3421
+ [
3422
+ '--log=path','-l',
3423
+ 'set log file - (default stderr)'
3424
+ ],
3425
+ [
3426
+ '--verbosity=verbostiy', '-v',
3427
+ '0|fatal < 1|error < 2|warn < 3|info < 4|debug - (default info)'
3428
+ ],
3429
+ [
3430
+ '--config=path',
3431
+ 'valid path - specify config file (default nil)'
3432
+ ],
3433
+ [
3434
+ '--template=[path]',
3435
+ 'valid path - generate a template config file in path (default stdout)'
3436
+ ],
3437
+ ]
3438
+ #--}}}
3439
+ end
3440
+ def inherited klass
3441
+ #--{{{
3442
+ ret = super
3443
+ klass.init
3444
+ ret
3445
+ #--}}}
3446
+ end
3447
+ #--}}}
3448
+ end
3449
+
3450
+ class_attribute :version
3451
+ class_attribute :author
3452
+ class_attribute :prognam
3453
+ class_attribute :config_default_path
3454
+ class_attribute :config_search_path
3455
+ class_attribute :optspec
3456
+ class_attribute :configfile
3457
+
3458
+ self::init
3459
+
3460
+ attr :logger
3461
+ attr :argv
3462
+ attr :env
3463
+ attr :cmdline
3464
+ attr :options
3465
+ attr :op
3466
+ attr :logdev
3467
+ attr :verbosity
3468
+ attr :config
3469
+
3470
+ def initialize argv = ARGV, env = ENV
3471
+ #--{{{
3472
+ @argv = Util::mcp(argv.to_a)
3473
+ @env = Util::mcp(env.to_hash)
3474
+ @cmdline = ([$0] + @argv).join ' '
3475
+ #--}}}
3476
+ end
3477
+ def klass; self.class; end
3478
+ def required_arguments
3479
+ #--{{{
3480
+ klass.required_arguments
3481
+ #--}}}
3482
+ end
3483
+ def optional_arguments
3484
+ #--{{{
3485
+ klass.optional_arguments
3486
+ #--}}}
3487
+ end
3488
+ def run
3489
+ #--{{{
3490
+ logcatch do
3491
+ parse_options
3492
+ begin
3493
+ if(@options.has_key?('help') or @argv.first =~ %r/help/i)
3494
+ usage STDOUT
3495
+ exit EXIT_SUCCESS
3496
+ end
3497
+ if(@options.has_key?('template') or @argv.first =~ %r/template/i)
3498
+ @argv.shift if @argv.first =~ %r/template/i
3499
+ arg = @argv.first
3500
+ gen_template @options['template'] || @options['config'] || arg
3501
+ exit EXIT_SUCCESS
3502
+ end
3503
+ parse_argv
3504
+ init_logging
3505
+ init_config
3506
+ status = main
3507
+ exit(status ? EXIT_SUCCESS : EXIT_FAILURE)
3508
+ rescue Errno::EPIPE
3509
+ STDOUT.tty? ? raise : exit(EXIT_FAILURE)
3510
+ end
3511
+ end
3512
+ #--}}}
3513
+ end
3514
+ def logcatch
3515
+ #--{{{
3516
+ ret = nil
3517
+ @logger ||= Logger::new STDERR
3518
+ begin
3519
+ ret = yield
3520
+ rescue => e
3521
+ unless SystemExit === e
3522
+ if logger.debug?
3523
+ fatal{ e }
3524
+ exit EXIT_FAILURE
3525
+ else
3526
+ fatal{ emsg(e) }
3527
+ exit EXIT_FAILURE
3528
+ end
3529
+ else
3530
+ exit e.status
3531
+ end
3532
+ end
3533
+ ret
3534
+ #--}}}
3535
+ end
3536
+ def usage port = STDERR
3537
+ #--{{{
3538
+ port << klass::usage << "\n" if klass::usage
3539
+ port << klass::examples << "\n" if klass::examples
3540
+
3541
+ if klass::optspec
3542
+ port << 'OPTIONS' << "\n"
3543
+
3544
+ klass::optspec.each do |os|
3545
+ a, b, c = os
3546
+ long, short, desc = nil
3547
+ [a,b,c].each do |word|
3548
+ next unless word
3549
+ word.strip!
3550
+ case word
3551
+ when %r/^--[^-]/o
3552
+ long = word
3553
+ when %r/^-[^-]/o
3554
+ short = word
3555
+ else
3556
+ desc = word
3557
+ end
3558
+ end
3559
+
3560
+ spec = ((long and short) ? [long, short] : [long])
3561
+
3562
+ if spec
3563
+ port << Util::columnize(spec.join(', '), :width => 80, :indent => 2)
3564
+ port << "\n"
3565
+ end
3566
+
3567
+ if desc
3568
+ port << Util::columnize(desc, :width => 80, :indent => 8)
3569
+ port << "\n"
3570
+ end
3571
+ end
3572
+
3573
+ port << "\n"
3574
+ end
3575
+
3576
+ port
3577
+ #--}}}
3578
+ end
3579
+ def parse_options
3580
+ #--{{{
3581
+ @op = OptionParser::new
3582
+ @options = {}
3583
+ @listoptions = Hash::new{|h,k| h[k] = []}
3584
+ klass::optspec.each do |spec|
3585
+ k = spec.first.gsub(%r/(?:--)|(?:=.*$)|(?:\s+)/o,'')
3586
+ @op.def_option(*spec) do |v|
3587
+ @options[k] = v
3588
+ @listoptions[k] << v
3589
+ end
3590
+ end
3591
+ #begin
3592
+ op.parse! @argv
3593
+ #rescue OptionParser::InvalidOption => e
3594
+ # preverve unknown options
3595
+ #e.recover(argv)
3596
+ #end
3597
+ @options
3598
+ #--}}}
3599
+ end
3600
+ def init_logging
3601
+ #--{{{
3602
+ log, log_age, log_size, verbosity =
3603
+ @options.values_at 'log', 'log_age', 'log_size', 'verbosity'
3604
+ log_age = Integer log_age rescue nil
3605
+ log_size = Integer log_size rescue nil
3606
+ $logger = @logger = Logger::new(log || STDERR, log_age, log_size)
3607
+ #
3608
+ # hack to fix Logger sync bug
3609
+ #
3610
+ begin
3611
+ class << @logger; attr :logdev unless @logger.respond_to?(:logdev); end
3612
+ @logdev = @logger.logdev.dev
3613
+ @logdev.sync = true
3614
+ rescue
3615
+ nil
3616
+ end
3617
+ level = nil
3618
+ verbosity ||= 'info'
3619
+ verbosity =
3620
+ case verbosity
3621
+ when /^\s*(?:4|d|debug)\s*$/io
3622
+ level = 'Logging::DEBUG'
3623
+ 4
3624
+ when /^\s*(?:3|i|info)\s*$/io
3625
+ level = 'Logging::INFO'
3626
+ 3
3627
+ when /^\s*(?:2|w|warn)\s*$/io
3628
+ level = 'Logging::WARN'
3629
+ 2
3630
+ when /^\s*(?:1|e|error)\s*$/io
3631
+ level = 'Logging::ERROR'
3632
+ 1
3633
+ when /^\s*(?:0|f|fatal)\s*$/io
3634
+ level = 'Logging::FATAL'
3635
+ 0
3636
+ else
3637
+ abort "illegal verbosity setting <#{ verbosity }>"
3638
+ end
3639
+ @logger.level = 2 - ((verbosity % 5) - 2)
3640
+ @logger
3641
+ #--}}}
3642
+ end
3643
+ def init_config
3644
+ #--{{{
3645
+ @config =
3646
+ if @options['config']
3647
+ klass::configfile::new(@options['config'])
3648
+ else
3649
+ klass::configfile::any klass::config_default_path, klass::config_search_path
3650
+ end
3651
+ @config
3652
+ #--}}}
3653
+ end
3654
+ def gen_template template
3655
+ #--{{{
3656
+ klass::configfile::gen_template(template)
3657
+ self
3658
+ #--}}}
3659
+ end
3660
+ def parse_argv
3661
+ #--{{{
3662
+ klass.required_arguments.each do |arg|
3663
+ value = @argv.shift
3664
+ if value
3665
+ klass.module_eval <<-code
3666
+ def #{ arg }(*__list)
3667
+ if __list.empty?
3668
+ @#{ arg }
3669
+ else
3670
+ send('#{ arg }=', *__list)
3671
+ end
3672
+ end
3673
+ def #{ arg }=(__arg, *__list)
3674
+ if __list.empty?
3675
+ @#{ arg } = __arg
3676
+ else
3677
+ @#{ arg } = ([__arg] + __list)
3678
+ end
3679
+ end
3680
+ code
3681
+ send "#{ arg }=", value
3682
+ else
3683
+ die 'msg' => "required_argument <#{ arg }> not given"
3684
+ end
3685
+ end
3686
+
3687
+ klass.optional_arguments.each do |arg|
3688
+ value = @argv.shift
3689
+ break unless value
3690
+ if value
3691
+ klass.module_eval <<-code
3692
+ def #{ arg }(*__list)
3693
+ if __list.empty?
3694
+ @#{ arg }
3695
+ else
3696
+ send('#{ arg }=', *__list)
3697
+ end
3698
+ end
3699
+ def #{ arg }=(__arg, *__list)
3700
+ if __list.empty?
3701
+ @#{ arg } = __arg
3702
+ else
3703
+ @#{ arg } = ([__arg] + __list)
3704
+ end
3705
+ end
3706
+ code
3707
+ send "#{ arg }=", value
3708
+ end
3709
+ end
3710
+
3711
+ @argv
3712
+ #--}}}
3713
+ end
3714
+ def main
3715
+ #--{{{
3716
+ warn{ "foobar" }
3717
+ p @listoptions
3718
+ return EXIT_SUCCESS
3719
+ #--}}}
3720
+ end
3721
+ def die errno = EXIT_FAILURE
3722
+ #--{{{
3723
+ STDERR.puts klass.usage
3724
+ exit EXIT_FAILURE
3725
+ #--}}}
3726
+ end
3727
+ #--}}}
3728
+ end
3729
+ =end
3730
+
3731
+
3732
+ #
3733
+ # on-demand extensions to standard classes - (incomplete now)
3734
+ #
3735
+ module ModuleExtensions
3736
+ #--{{{
3737
+ def reader_attributes(*names)
3738
+ #--{{{
3739
+ @reader_attributes ||= []
3740
+ unless names.empty?
3741
+ names.flatten.each do |name|
3742
+ @reader_attributes << "#{ name }"
3743
+ getter = "#{ name }"
3744
+ unless instance_methods.include? getter
3745
+ code = <<-code
3746
+ def #{ name }(*a)
3747
+ unless a.empty?
3748
+ self.#{ name }= a.shift
3749
+ else
3750
+ @#{ name }
3751
+ end
3752
+ end
3753
+ code
3754
+ module_eval code
3755
+ end
3756
+
3757
+ setter = "#{ name }="
3758
+ unless instance_methods.include? setter
3759
+ code = <<-code
3760
+ def #{ name }= value
3761
+ @#{ name } = value
3762
+ end
3763
+ private '#{ name }='.intern
3764
+ code
3765
+ module_eval code
3766
+ end
3767
+
3768
+ query = "#{ name }?"
3769
+ unless instance_methods.include? query
3770
+ code = <<-code
3771
+ alias #{ name }? #{ name }
3772
+ code
3773
+ module_eval code
3774
+ end
3775
+ end
3776
+ end
3777
+ @reader_attributes
3778
+ #--}}}
3779
+ end
3780
+ alias reader_attribute reader_attributes
3781
+ def writer_attributes(*names)
3782
+ #--{{{
3783
+ @writer_attributes ||= []
3784
+ unless names.empty?
3785
+ names.flatten.each do |name|
3786
+ @writer_attributes << "#{ name }="
3787
+ getter = "#{ name }"
3788
+ unless instance_methods.include? getter
3789
+ code = <<-code
3790
+ def #{ name }(*a)
3791
+ unless a.empty?
3792
+ self.#{ name }= a.shift
3793
+ else
3794
+ @#{ name }
3795
+ end
3796
+ end
3797
+ private '#{ name }'.intern
3798
+ code
3799
+ module_eval code
3800
+ end
3801
+
3802
+ setter = "#{ name }="
3803
+ unless instance_methods.include? setter
3804
+ code = <<-code
3805
+ def #{ name }= value
3806
+ @#{ name } = value
3807
+ end
3808
+ code
3809
+ module_eval code
3810
+ end
3811
+
3812
+ query = "#{ name }?"
3813
+ unless instance_methods.include? query
3814
+ code = <<-code
3815
+ alias #{ name }? #{ name }
3816
+ private '#{ name }?'.intern
3817
+ code
3818
+ module_eval code
3819
+ end
3820
+ end
3821
+ end
3822
+ @writer_attributes
3823
+ #--}}}
3824
+ end
3825
+ alias writer_attribute writer_attributes
3826
+ def attributes(*names)
3827
+ #--{{{
3828
+ reader_attributes(*names) + writer_attributes(*names)
3829
+ #--}}}
3830
+ end
3831
+ alias attribute attributes
3832
+ def class_reader_attributes(*names)
3833
+ #--{{{
3834
+ class << self; self; end.instance_eval{ reader_attributes(*names) }
3835
+ #--}}}
3836
+ end
3837
+ alias class_reader_attribute class_reader_attributes
3838
+ def class_writer_attributes(*names)
3839
+ #--{{{
3840
+ class << self; self; end.instance_eval{ writer_attributes(*names) }
3841
+ #--}}}
3842
+ end
3843
+ alias class_writer_attribute class_writer_attributes
3844
+ def class_attributes(*names)
3845
+ #--{{{
3846
+ class << self; self; end.instance_eval{ attributes(*names) }
3847
+ #--}}}
3848
+ end
3849
+ alias class_attribute class_attributes
3850
+ class << self
3851
+ def self.append_features c
3852
+ #--{{{
3853
+ Module.extend self
3854
+ #--}}}
3855
+ end
3856
+ end
3857
+ #--}}}
3858
+ end # module ModuleExtensions
3859
+ module ClassExtensions;
3860
+ end # module ClassExtensions
3861
+ module ObjectExtensions
3862
+ end # module ObjectExtensions
3863
+ module StandardExtensions
3864
+ include ModuleExtensions
3865
+ include ClassExtensions
3866
+ include ObjectExtensions
3867
+ end # StandardExtensions
3868
+
3869
+ #--}}}
3870
+ end
3871
+
3872
+ Alib = ALib