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.
- data/ChangeLog +14 -0
- data/Install +6 -0
- data/README +66 -0
- data/Rakefile +113 -0
- data/bin/minitar +27 -0
- data/lib/archive/tar/minitar.rb +985 -0
- data/lib/archive/tar/minitar/command.rb +814 -0
- data/tests/tc_tar.rb +614 -0
- data/tests/testall.rb +10 -0
- metadata +69 -0
@@ -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
|