minitar 0.5.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of minitar might be problematic. Click here for more details.

@@ -0,0 +1,814 @@
1
+ #!/usr/bin/env ruby
2
+ #--
3
+ # Archive::Tar::Baby 0.5.2
4
+ # Copyright 2004 Mauricio Julio Ferna'ndez Pradier and Austin Ziegler
5
+ # This is free software with ABSOLUTELY NO WARRANTY.
6
+ #
7
+ # This program is based on and incorporates parts of RPA::Package from
8
+ # rpa-base (lib/rpa/package.rb and lib/rpa/util.rb) by Mauricio and has been
9
+ # adapted to be more generic by Austin.
10
+ #
11
+ # This file contains an adaptation of Ruby/ProgressBar by Satoru
12
+ # Takabayashi <satoru@namazu.org>, copyright 2001 - 2004.
13
+ #
14
+ # It is licensed under the GNU General Public Licence or Ruby's licence.
15
+ #
16
+ # $Id$
17
+ #++
18
+
19
+ require 'zlib'
20
+
21
+ # TODO: add
22
+ # TODO: delete ???
23
+
24
+ require 'optparse'
25
+ require 'ostruct'
26
+ require 'fileutils'
27
+
28
+ module Archive::Tar::Minitar::Command
29
+ class ProgressBar
30
+ VERSION = "0.8"
31
+
32
+ attr_accessor :total
33
+ attr_accessor :title
34
+
35
+ def initialize (title, total, out = STDERR)
36
+ @title = title
37
+ @total = total
38
+ @out = out
39
+ @bar_width = 80
40
+ @bar_mark = "o"
41
+ @current = 0
42
+ @previous = 0
43
+ @is_finished = false
44
+ @start_time = Time.now
45
+ @previous_time = @start_time
46
+ @title_width = 14
47
+ @format = "%-#{@title_width}s %3d%% %s %s"
48
+ @format_arguments = [:title, :percentage, :bar, :stat]
49
+ show
50
+ end
51
+
52
+ private
53
+ def convert_bytes (bytes)
54
+ if bytes < 1024
55
+ sprintf("%6dB", bytes)
56
+ elsif bytes < 1024 * 1000 # 1000kb
57
+ sprintf("%5.1fKB", bytes.to_f / 1024)
58
+ elsif bytes < 1024 * 1024 * 1000 # 1000mb
59
+ sprintf("%5.1fMB", bytes.to_f / 1024 / 1024)
60
+ else
61
+ sprintf("%5.1fGB", bytes.to_f / 1024 / 1024 / 1024)
62
+ end
63
+ end
64
+
65
+ def transfer_rate
66
+ bytes_per_second = @current.to_f / (Time.now - @start_time)
67
+ sprintf("%s/s", convert_bytes(bytes_per_second))
68
+ end
69
+
70
+ def bytes
71
+ convert_bytes(@current)
72
+ end
73
+
74
+ def format_time (t)
75
+ t = t.to_i
76
+ sec = t % 60
77
+ min = (t / 60) % 60
78
+ hour = t / 3600
79
+ sprintf("%02d:%02d:%02d", hour, min, sec);
80
+ end
81
+
82
+ # ETA stands for Estimated Time of Arrival.
83
+ def eta
84
+ if @current == 0
85
+ "ETA: --:--:--"
86
+ else
87
+ elapsed = Time.now - @start_time
88
+ eta = elapsed * @total / @current - elapsed;
89
+ sprintf("ETA: %s", format_time(eta))
90
+ end
91
+ end
92
+
93
+ def elapsed
94
+ elapsed = Time.now - @start_time
95
+ sprintf("Time: %s", format_time(elapsed))
96
+ end
97
+
98
+ def stat
99
+ if @is_finished then elapsed else eta end
100
+ end
101
+
102
+ def stat_for_file_transfer
103
+ if @is_finished then
104
+ sprintf("%s %s %s", bytes, transfer_rate, elapsed)
105
+ else
106
+ sprintf("%s %s %s", bytes, transfer_rate, eta)
107
+ end
108
+ end
109
+
110
+ def eol
111
+ if @is_finished then "\n" else "\r" end
112
+ end
113
+
114
+ def bar
115
+ len = percentage * @bar_width / 100
116
+ sprintf("|%s%s|", @bar_mark * len, " " * (@bar_width - len))
117
+ end
118
+
119
+ def percentage(value = nil)
120
+ if @total.zero?
121
+ 100
122
+ else
123
+ (value || @current) * 100 / @total
124
+ end
125
+ end
126
+
127
+ def title
128
+ @title[0,(@title_width - 1)] + ":"
129
+ end
130
+
131
+ def get_width
132
+ # FIXME: I don't know how portable it is.
133
+ default_width = 80
134
+ # begin
135
+ # tiocgwinsz = 0x5413
136
+ # data = [0, 0, 0, 0].pack("SSSS")
137
+ # if @out.ioctl(tiocgwinsz, data) >= 0 then
138
+ # rows, cols, xpixels, ypixels = data.unpack("SSSS")
139
+ # if cols >= 0 then cols else default_width end
140
+ # else
141
+ # default_width
142
+ # end
143
+ # rescue Exception
144
+ # default_width
145
+ # end
146
+ end
147
+
148
+ def show
149
+ arguments = @format_arguments.map {|method| send(method) }
150
+ line = sprintf(@format, *arguments)
151
+
152
+ width = get_width
153
+ if line.length == width - 1
154
+ @out.print(line + eol)
155
+ elsif line.length >= width
156
+ @bar_width = [@bar_width - (line.length - width + 1), 0].max
157
+ if @bar_width == 0 then @out.print(line + eol) else show end
158
+ else # line.length < width - 1
159
+ @bar_width += width - line.length + 1
160
+ show
161
+ end
162
+ @previous_time = Time.now
163
+ end
164
+
165
+ def show_progress
166
+ if @total.zero?
167
+ cur_percentage = 100
168
+ prev_percentage = 0
169
+ else
170
+ cur_percentage = (@current * 100 / @total).to_i
171
+ prev_percentage = (@previous * 100 / @total).to_i
172
+ end
173
+
174
+ if cur_percentage > prev_percentage ||
175
+ Time.now - @previous_time >= 1 ||
176
+ @is_finished
177
+ show
178
+ end
179
+ end
180
+
181
+ public
182
+ def file_transfer_mode
183
+ @format_arguments = [:title, :percentage, :bar, :stat_for_file_transfer]
184
+ end
185
+
186
+ def format= (format)
187
+ @format = format
188
+ end
189
+
190
+ def format_arguments= (arguments)
191
+ @format_arguments = arguments
192
+ end
193
+
194
+ def finish
195
+ @current = @total
196
+ @is_finished = true
197
+ show_progress
198
+ end
199
+
200
+ def halt
201
+ @is_finished = true
202
+ show_progress
203
+ end
204
+
205
+ def set (count)
206
+ if count < 0 || count > @total
207
+ raise "invalid count: #{count} (total: #{@total})"
208
+ end
209
+ @current = count
210
+ show_progress
211
+ @previous = @current
212
+ end
213
+
214
+ def inc (step = 1)
215
+ @current += step
216
+ @current = @total if @current > @total
217
+ show_progress
218
+ @previous = @current
219
+ end
220
+
221
+ def inspect
222
+ "(ProgressBar: #{@current}/#{@total})"
223
+ end
224
+ end
225
+
226
+ class CommandPattern
227
+ class AbstractCommandError < Exception; end
228
+ class UnknownCommandError < RuntimeError; end
229
+ class CommandAlreadyExists < RuntimeError; end
230
+
231
+ class << self
232
+ def add(command)
233
+ command = command.new if command.kind_of?(Class)
234
+
235
+ @commands ||= {}
236
+ if @commands.has_key?(command.name)
237
+ raise CommandAlreadyExists
238
+ else
239
+ @commands[command.name] = command
240
+ end
241
+
242
+ if command.respond_to?(:altname)
243
+ unless @commands.has_key?(command.altname)
244
+ @commands[command.altname] = command
245
+ end
246
+ end
247
+ end
248
+
249
+ def <<(command)
250
+ add(command)
251
+ end
252
+
253
+ attr_accessor :default
254
+ def default=(command) #:nodoc:
255
+ if command.kind_of?(CommandPattern)
256
+ @default = command
257
+ elsif command.kind_of?(Class)
258
+ @default = command.new
259
+ elsif @commands.has_key?(command)
260
+ @default = @commands[command]
261
+ else
262
+ raise UnknownCommandError
263
+ end
264
+ end
265
+
266
+ def command?(command)
267
+ @commands.has_key?(command)
268
+ end
269
+
270
+ def command(command)
271
+ if command?(command)
272
+ @commands[command]
273
+ else
274
+ @default
275
+ end
276
+ end
277
+
278
+ def [](cmd)
279
+ self.command(cmd)
280
+ end
281
+
282
+ def default_ioe(ioe = {})
283
+ ioe[:input] ||= $stdin
284
+ ioe[:output] ||= $stdout
285
+ ioe[:error] ||= $stderr
286
+ ioe
287
+ end
288
+ end
289
+
290
+ def [](args, opts = {}, ioe = {})
291
+ call(args, opts, ioe)
292
+ end
293
+
294
+ def name
295
+ raise AbstractCommandError
296
+ end
297
+
298
+ def call(args, opts = {}, ioe = {})
299
+ raise AbstractCommandError
300
+ end
301
+
302
+ def help
303
+ raise AbstractCommandError
304
+ end
305
+ end
306
+
307
+ class CommandHelp < CommandPattern
308
+ def name
309
+ "help"
310
+ end
311
+
312
+ def call(args, opts = {}, ioe = {})
313
+ ioe = CommandPattern.default_ioe(ioe)
314
+
315
+ help_on = args.shift
316
+
317
+ if CommandPattern.command?(help_on)
318
+ ioe[:output] << CommandPattern[help_on].help
319
+ elsif help_on == "commands"
320
+ ioe[:output] << <<-EOH
321
+ The commands known to minitar are:
322
+
323
+ minitar create Creates a new tarfile.
324
+ minitar extract Extracts files from a tarfile.
325
+ minitar list Lists files in the tarfile.
326
+
327
+ All commands accept the options --verbose and --progress, which are
328
+ mutually exclusive. In "minitar list", --progress means the same as
329
+ --verbose.
330
+
331
+ --verbose, -V Performs the requested command verbosely.
332
+ --progress, -P Shows a progress bar, if appropriate, for the action
333
+ being performed.
334
+
335
+ EOH
336
+ else
337
+ ioe[:output] << "Unknown command: #{help_on}\n" unless help_on.nil? or help_on.empty?
338
+ ioe[:output] << self.help
339
+ end
340
+
341
+ 0
342
+ end
343
+
344
+ def help
345
+ help = <<-EOH
346
+ This is a basic help message containing pointers to more information on
347
+ how to use this command-line tool. Try:
348
+
349
+ minitar help commands list all 'minitar' commands
350
+ minitar help <COMMAND> show help on <COMMAND>
351
+ (e.g., 'minitar help create')
352
+ EOH
353
+ end
354
+ # minitar add Adds a file to an existing tarfile.
355
+ # minitar delete Deletes a file from an existing tarfile.
356
+ end
357
+
358
+ class CommandCreate < CommandPattern
359
+ def name
360
+ "create"
361
+ end
362
+
363
+ def altname
364
+ "cr"
365
+ end
366
+
367
+ def call(args, opts = {}, ioe = {})
368
+ argv = []
369
+
370
+ while (arg = args.shift)
371
+ case arg
372
+ when '--compress', '-z'
373
+ opts[:compress] = true
374
+ else
375
+ argv << arg
376
+ end
377
+ end
378
+
379
+ if argv.size < 2
380
+ ioe[:output] << "Not enough arguments.\n\n"
381
+ CommandPattern["help"][["create"]]
382
+ return 255
383
+ end
384
+
385
+ output = argv.shift
386
+ if '-' == output
387
+ opts[:name] = "STDOUT"
388
+ output = ioe[:output]
389
+ opts[:output] = ioe[:error]
390
+ else
391
+ opts[:name] = output
392
+ output = File.open(output, "wb")
393
+ opts[:output] = ioe[:output]
394
+ end
395
+
396
+ if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:compress]
397
+ output = Zlib::GzipWriter.new(output)
398
+ end
399
+
400
+ files = []
401
+ if argv.include?("--")
402
+ # Read stdin for the list of files.
403
+ files = ""
404
+ files << ioe[:input].read while not ioe[:input].eof?
405
+ files = files.split(/\r\n|\n|\r/)
406
+ args.delete("--")
407
+ end
408
+
409
+ files << argv.to_a
410
+ files.flatten!
411
+
412
+ if opts[:verbose]
413
+ watcher = lambda do |action, name, stats|
414
+ opts[:output] << "#{name}\n" if action == :dir or action == :file_done
415
+ end
416
+ finisher = lambda { opts[:output] << "\n" }
417
+ elsif opts[:progress]
418
+ progress = ProgressBar.new(opts[:name], 1)
419
+ watcher = lambda do |action, name, stats|
420
+ case action
421
+ when :file_start, :dir
422
+ progress.title = File.basename(name)
423
+ if action == :dir
424
+ progress.total += 1
425
+ progress.inc
426
+ else
427
+ progress.total += stats[:size]
428
+ end
429
+ when :file_progress
430
+ progress.inc(stats[:currinc])
431
+ end
432
+ end
433
+ finisher = lambda do
434
+ progress.title = opts[:name]
435
+ progress.finish
436
+ end
437
+ else
438
+ watcher = nil
439
+ finisher = lambda { }
440
+ end
441
+
442
+ Archive::Tar::Minitar.pack(files, output, &watcher)
443
+ finisher.call
444
+ 0
445
+ ensure
446
+ output.close if output and not output.closed?
447
+ end
448
+
449
+ def help
450
+ help = <<-EOH
451
+ minitar create [OPTIONS] <tarfile|-> <file|directory|-->+
452
+
453
+ Creates a new tarfile. If the tarfile is named .tar.gz or .tgz, then it
454
+ will be compressed automatically. If the tarfile is "-", then it will be
455
+ output to standard output (stdout) so that minitar may be piped.
456
+
457
+ The files or directories that will be packed into the tarfile are
458
+ specified after the name of the tarfile itself. Directories will be
459
+ processed recursively. If the token "--" is found in the list of files
460
+ to be packed, additional filenames will be read from standard input
461
+ (stdin). If any file is not found, the packaging will be halted.
462
+
463
+ create Options:
464
+ --compress, -z Compresses the tarfile with gzip.
465
+
466
+ EOH
467
+ end
468
+ end
469
+
470
+ class CommandExtract < CommandPattern
471
+ def name
472
+ "extract"
473
+ end
474
+
475
+ def altname
476
+ "ex"
477
+ end
478
+
479
+ def call(args, opts = {}, ioe = {})
480
+ argv = []
481
+ output = nil
482
+ dest = "."
483
+ files = []
484
+
485
+ while (arg = args.shift)
486
+ case arg
487
+ when '--uncompress', '-z'
488
+ opts[:uncompress] = true
489
+ when '--pipe'
490
+ opts[:output] = ioe[:error]
491
+ output = ioe[:output]
492
+ when '--output', '-o'
493
+ dest = args.shift
494
+ else
495
+ argv << arg
496
+ end
497
+ end
498
+
499
+ if argv.size < 1
500
+ ioe[:output] << "Not enough arguments.\n\n"
501
+ CommandPattern["help"][["extract"]]
502
+ return 255
503
+ end
504
+
505
+ input = argv.shift
506
+ if '-' == input
507
+ opts[:name] = "STDIN"
508
+ input = ioe[:input]
509
+ else
510
+ opts[:name] = input
511
+ input = File.open(input, "rb")
512
+ end
513
+
514
+ if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:uncompress]
515
+ input = Zlib::GzipReader.new(input)
516
+ end
517
+
518
+ files << argv.to_a
519
+ files.flatten!
520
+
521
+ if opts[:verbose]
522
+ watcher = lambda do |action, name, stats|
523
+ opts[:output] << "#{name}\n" if action == :dir or action == :file_done
524
+ end
525
+ finisher = lambda { opts[:output] << "\n" }
526
+ elsif opts[:progress]
527
+ progress = ProgressBar.new(opts[:name], 1)
528
+ watcher = lambda do |action, name, stats|
529
+ case action
530
+ when :file_start, :dir
531
+ progress.title = File.basename(name)
532
+ if action == :dir
533
+ progress.total += 1
534
+ progress.inc
535
+ else
536
+ progress.total += stats[:entry].size
537
+ end
538
+ when :file_progress
539
+ progress.inc(stats[:currinc])
540
+ end
541
+ end
542
+ finisher = lambda do
543
+ progress.title = opts[:name]
544
+ progress.finish
545
+ end
546
+ else
547
+ watcher = nil
548
+ finisher = lambda { }
549
+ end
550
+
551
+ if output.nil?
552
+ Archive::Tar::Minitar.unpack(input, dest, files, &watcher)
553
+ finisher.call
554
+ else
555
+ Archive::Tar::Minitar::Input.open(input) do |inp|
556
+ inp.each do |entry|
557
+ stats = {
558
+ :mode => entry.mode,
559
+ :mtime => entry.mtime,
560
+ :size => entry.size,
561
+ :gid => entry.gid,
562
+ :uid => entry.uid,
563
+ :current => 0,
564
+ :currinc => 0,
565
+ :entry => entry
566
+ }
567
+
568
+ if files.empty? or files.include?(entry.full_name)
569
+ if entry.directory?
570
+ puts "Directory: #{entry.full_name}"
571
+ watcher[:dir, dest, stats] unless watcher.nil?
572
+ else
573
+ puts "File: #{entry.full_name}"
574
+ watcher[:file_start, destfile, stats] unless watcher.nil?
575
+ loop do
576
+ data = entry.read(4096)
577
+ break unless data
578
+ stats[:currinc] = output.write(data)
579
+ stats[:current] += stats[:currinc]
580
+
581
+ watcher[:file_progress, name, stats] unless watcher.nil?
582
+ end
583
+ watcher[:file_done, name, stats] unless watcher.nil?
584
+ end
585
+ end
586
+ end
587
+ end
588
+ end
589
+
590
+ 0
591
+ end
592
+
593
+ def help
594
+ help = <<-EOH
595
+ minitar extract [OPTIONS] <tarfile|-> [<file>+]
596
+
597
+ Extracts files from an existing tarfile. If the tarfile is named .tar.gz
598
+ or .tgz, then it will be uncompressed automatically. If the tarfile is
599
+ "-", then it will be read from standard input (stdin) so that minitar
600
+ may be piped.
601
+
602
+ The files or directories that will be extracted from the tarfile are
603
+ specified after the name of the tarfile itself. Directories will be
604
+ processed recursively. Files must be specified in full. A file
605
+ "foo/bar/baz.txt" cannot simply be specified by specifying "baz.txt".
606
+ Any file not found will simply be skipped and an error will be reported.
607
+
608
+ extract Options:
609
+ --uncompress, -z Uncompresses the tarfile with gzip.
610
+ --pipe Emits the extracted files to STDOUT for piping.
611
+ --output, -o Extracts the files to the specified directory.
612
+
613
+ EOH
614
+ end
615
+ end
616
+
617
+ class CommandList < CommandPattern
618
+ def name
619
+ "list"
620
+ end
621
+
622
+ def altname
623
+ "ls"
624
+ end
625
+
626
+ def modestr(mode)
627
+ s = "---"
628
+ s[0] = ?r if (mode & 4) == 4
629
+ s[1] = ?w if (mode & 2) == 2
630
+ s[2] = ?x if (mode & 1) == 1
631
+ s
632
+ end
633
+
634
+ def call(args, opts = {}, ioe = {})
635
+ argv = []
636
+ output = nil
637
+ dest = "."
638
+ files = []
639
+ opts[:field] = "name"
640
+
641
+ while (arg = args.shift)
642
+ case arg
643
+ when '--sort', '-S'
644
+ opts[:sort] = true
645
+ opts[:field] = args.shift
646
+ when '--reverse', '-R'
647
+ opts[:reverse] = true
648
+ opts[:sort] = true
649
+ when '--uncompress', '-z'
650
+ opts[:uncompress] = true
651
+ when '-l'
652
+ opts[:verbose] = true
653
+ else
654
+ argv << arg
655
+ end
656
+ end
657
+
658
+ if argv.size < 1
659
+ ioe[:output] << "Not enough arguments.\n\n"
660
+ CommandPattern["help"][["list"]]
661
+ return 255
662
+ end
663
+
664
+ input = argv.shift
665
+ if '-' == input
666
+ opts[:name] = "STDIN"
667
+ input = ioe[:input]
668
+ else
669
+ opts[:name] = input
670
+ input = File.open(input, "rb")
671
+ end
672
+
673
+ if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:uncompress]
674
+ input = Zlib::GzipReader.new(input)
675
+ end
676
+
677
+ files << argv.to_a
678
+ files.flatten!
679
+
680
+ if opts[:verbose] or opts[:progress]
681
+ format = "%10s %4d %8s %8s %8d %12s %s"
682
+ datefmt = "%b %d %Y"
683
+ timefmt = "%b %d %H:%M"
684
+ fields = %w(permissions inodes user group size date fullname)
685
+ else
686
+ format = "%s"
687
+ fields = %w(fullname)
688
+ end
689
+
690
+ opts[:field] = opts[:field].intern
691
+ opts[:field] = :full_name if opts[:field] == :name
692
+
693
+ output = []
694
+
695
+ Archive::Tar::Minitar::Input.open(input) do |inp|
696
+ today = Time.now
697
+ oneyear = Time.mktime(today.year - 1, today.month, today.day)
698
+ inp.each do |entry|
699
+ value = format % fields.map do |ff|
700
+ case ff
701
+ when "permissions"
702
+ s = entry.directory? ? "d" : "-"
703
+ s << modestr(entry.mode / 0100)
704
+ s << modestr(entry.mode / 0010)
705
+ s << modestr(entry.mode)
706
+ when "inodes"
707
+ entry.size / 512
708
+ when "user"
709
+ entry.uname || entry.uid || 0
710
+ when "group"
711
+ entry.gname || entry.gid || 0
712
+ when "size"
713
+ entry.size
714
+ when "date"
715
+ if Time.at(entry.mtime) > (oneyear)
716
+ Time.at(entry.mtime).strftime(timefmt)
717
+ else
718
+ Time.at(entry.mtime).strftime(datefmt)
719
+ end
720
+ when "fullname"
721
+ entry.full_name
722
+ end
723
+ end
724
+
725
+ if opts[:sort]
726
+ output << [entry.send(opts[:field]), value]
727
+ else
728
+ ioe[:output] << value << "\n"
729
+ end
730
+
731
+ end
732
+ end
733
+
734
+ if opts[:sort]
735
+ output = output.sort { |a, b| a[0] <=> b[0] }
736
+ if opts[:reverse]
737
+ output.reverse_each { |oo| ioe[:output] << oo[1] << "\n" }
738
+ else
739
+ output.each { |oo| ioe[:output] << oo[1] << "\n" }
740
+ end
741
+ end
742
+
743
+ 0
744
+ end
745
+
746
+ def help
747
+ help = <<-EOH
748
+ minitar list [OPTIONS] <tarfile|-> [<file>+]
749
+
750
+ Lists files in an existing tarfile. If the tarfile is named .tar.gz or
751
+ .tgz, then it will be uncompressed automatically. If the tarfile is "-",
752
+ then it will be read from standard input (stdin) so that minitar may be
753
+ piped.
754
+
755
+ If --verbose or --progress is specified, then the file list will be
756
+ similar to that produced by the Unix command "ls -l".
757
+
758
+ list Options:
759
+ --uncompress, -z Uncompresses the tarfile with gzip.
760
+ --sort [<FIELD>], -S Sorts the list of files by the specified
761
+ field. The sort defaults to the filename.
762
+ --reverse, -R Reverses the sort.
763
+ -l Lists the files in detail.
764
+
765
+ Sort Fields:
766
+ name, mtime, size
767
+
768
+ EOH
769
+ end
770
+ end
771
+
772
+ CommandPattern << CommandHelp
773
+ CommandPattern << CommandCreate
774
+ CommandPattern << CommandExtract
775
+ CommandPattern << CommandList
776
+ # CommandPattern << CommandAdd
777
+ # CommandPattern << CommandDelete
778
+
779
+ def self.run(argv, input = $stdin, output = $stdout, error = $stderr)
780
+ ioe = {
781
+ :input => input,
782
+ :output => output,
783
+ :error => error,
784
+ }
785
+ opts = { }
786
+
787
+ if argv.include?("--version")
788
+ output << <<-EOB
789
+ minitar #{Archive::Tar::Minitar::VERSION}
790
+ Copyright 2004 Mauricio Julio Ferna'ndez Pradier and Austin Ziegler
791
+ This is free software with ABSOLUTELY NO WARRANTY.
792
+
793
+ see http://rubyforge.org/projects/ruwiki for more information
794
+ EOB
795
+ end
796
+
797
+ if argv.include?("--verbose") or argv.include?("-V")
798
+ opts[:verbose] = true
799
+ argv.delete("--verbose")
800
+ argv.delete("-V")
801
+ end
802
+
803
+ if argv.include?("--progress") or argv.include?("-P")
804
+ opts[:progress] = true
805
+ opts[:verbose] = false
806
+ argv.delete("--progress")
807
+ argv.delete("-P")
808
+ end
809
+
810
+ command = CommandPattern[(argv.shift or "").downcase]
811
+ command ||= CommandPattern["help"]
812
+ return command[argv, opts, ioe]
813
+ end
814
+ end