archive-tar-minitar 0.5.2 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,814 +0,0 @@
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: command.rb 213 2008-02-26 22:32:11Z austin $
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