minitar-cli 0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The base for commands in Minitar::CLI. This will be replaced in a future
4
+ # version by one of the better-executed CLI application frameworks like GLI,
5
+ # after Ruby 1.8 and 1.9 support have been dropped.
6
+ class Minitar::CLI::Command
7
+ @children = []
8
+
9
+ attr_reader :commander
10
+ attr_reader :ioe
11
+
12
+ class << self
13
+ attr_reader :children
14
+
15
+ def inherited(subclass)
16
+ children << subclass
17
+ end
18
+ end
19
+
20
+ module CatchMinitarErrors # :nodoc:
21
+ def call(args, opts)
22
+ run(args, opts)
23
+ rescue Archive::Tar::Minitar::Error => error
24
+ ioe[:error] << "#{error}\n"
25
+ 5
26
+ end
27
+ end
28
+
29
+ def initialize(commander)
30
+ @commander = commander
31
+ @ioe = commander.ioe
32
+ end
33
+
34
+ def name
35
+ raise Minitar::CLI::AbstractCommandError
36
+ end
37
+
38
+ def call(_args, _opts = {})
39
+ raise Minitar::CLI::AbstractCommandError
40
+ end
41
+ alias [] call
42
+
43
+ def help
44
+ raise Minitar::CLI::AbstractCommandError
45
+ end
46
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tarball creation command. This will be replaced in a future version by one of
4
+ # the better-executed CLI application frameworks like GLI, after Ruby 1.8 and
5
+ # 1.9 support have been dropped.
6
+ class Minitar::CLI::Command::Create < Minitar::CLI::Command
7
+ def name
8
+ 'create'
9
+ end
10
+
11
+ def altname
12
+ 'cr'
13
+ end
14
+
15
+ HELP = <<-EOH.freeze
16
+ minitar create [OPTIONS] <tarfile|-> <file|directory|-->+
17
+
18
+ Creates a new tarfile. If the tarfile is named .tar.gz or .tgz, then it
19
+ will be compressed automatically. If the tarfile is "-", then it will be
20
+ output to standard output (stdout) so that minitar may be piped.
21
+
22
+ The files or directories that will be packed into the tarfile are
23
+ specified after the name of the tarfile itself. Directories will be
24
+ processed recursively. If the token "--" is found in the list of files
25
+ to be packed, additional filenames will be read from standard input
26
+ (stdin). If any file is not found, the packaging will be halted.
27
+
28
+ create Options:
29
+ --compress, -z Compresses the tarfile with gzip.
30
+
31
+ EOH
32
+
33
+ include CatchMinitarErrors
34
+
35
+ def run(args, opts = {})
36
+ argv = []
37
+
38
+ while (arg = args.shift)
39
+ case arg
40
+ when '--compress', '-z'
41
+ opts[:compress] = true
42
+ else
43
+ argv << arg
44
+ end
45
+ end
46
+
47
+ if argv.size < 2
48
+ ioe[:output] << "Not enough arguments.\n\n"
49
+ commander.command('help').call(%w(create))
50
+ return 255
51
+ end
52
+
53
+ output = argv.shift
54
+ if '-' == output
55
+ opts[:name] = 'STDOUT'
56
+ output = ioe[:output]
57
+ opts[:output] = ioe[:error]
58
+ else
59
+ opts[:name] = output
60
+ output = File.open(output, 'wb')
61
+ opts[:output] = ioe[:output]
62
+ end
63
+
64
+ if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:compress]
65
+ output = Zlib::GzipWriter.new(output)
66
+ end
67
+
68
+ files = []
69
+ if argv.include?('--')
70
+ # Read stdin for the list of files.
71
+ files = ''
72
+ files << ioe[:input].read until ioe[:input].eof?
73
+ files = files.split(/\r\n|\n|\r/)
74
+ args.delete('--')
75
+ end
76
+
77
+ files << argv.to_a
78
+ files.flatten!
79
+
80
+ watcher, finisher =
81
+ if opts[:verbose]
82
+ verbose
83
+ elsif opts[:progress]
84
+ progress
85
+ else
86
+ silent
87
+ end
88
+
89
+ Archive::Tar::Minitar.pack(files, output, &watcher)
90
+ finisher.call
91
+ 0
92
+ ensure
93
+ output.close if output && !output.closed?
94
+ end
95
+
96
+ def help
97
+ HELP
98
+ end
99
+
100
+ private
101
+
102
+ def verbose
103
+ [
104
+ lambda { |action, name, _stats|
105
+ opts[:output] << "#{name}\n" if action == :dir || action == :file_done
106
+ },
107
+ lambda { opts[:output] << "\n" }
108
+ ]
109
+ end
110
+
111
+ def progress
112
+ require 'powerbar'
113
+ progress = PowerBar.new(:msg => opts[:name], :total => 1)
114
+ [
115
+ lambda { |action, name, stats|
116
+ progress_info = {}
117
+
118
+ case action
119
+ when :file_start, :dir
120
+ progress_info[:msg] = File.basename(name)
121
+
122
+ if action == :dir
123
+ progress_info[:total] = progress.total + 1
124
+ progress_info[:done] = progress.done + 1
125
+ else
126
+ progress_info[:total] = progress.total + stats[:size]
127
+ end
128
+ when :file_progress
129
+ progress_info[:done] = progress.done + stats[:currinc]
130
+ end
131
+
132
+ progress.show(progress_info)
133
+ },
134
+ lambda {
135
+ progress.show(:msg => opts[:name])
136
+ progress.close(true)
137
+ }
138
+ ]
139
+ end
140
+
141
+ def silent
142
+ [ lambda { |_, _, _| }, lambda {} ]
143
+ end
144
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tarball extraction command. This will be replaced in a future version by one
4
+ # of the better-executed CLI application frameworks like GLI, after Ruby 1.8
5
+ # and 1.9 support have been dropped.
6
+ class Minitar::CLI::Command::Extract < Minitar::CLI::Command
7
+ def name
8
+ 'extract'
9
+ end
10
+
11
+ def altname
12
+ 'ex'
13
+ end
14
+
15
+ HELP = <<-EOH.freeze
16
+ minitar extract [OPTIONS] <tarfile|-> [<file>+]
17
+
18
+ Extracts files from an existing tarfile. If the tarfile is named .tar.gz
19
+ or .tgz, then it will be uncompressed automatically. If the tarfile is
20
+ "-", then it will be read from standard input (stdin) so that minitar
21
+ may be piped.
22
+
23
+ The files or directories that will be extracted from the tarfile are
24
+ specified after the name of the tarfile itself. Directories will be
25
+ processed recursively. Files must be specified in full. A file
26
+ "foo/bar/baz.txt" cannot simply be specified by specifying "baz.txt".
27
+ Any file not found will simply be skipped and an error will be reported.
28
+
29
+ extract Options:
30
+ --uncompress, -z Uncompresses the tarfile with gzip.
31
+ --pipe Emits the extracted files to STDOUT for piping.
32
+ --output, -o Extracts the files to the specified directory.
33
+
34
+ EOH
35
+
36
+ include CatchMinitarErrors
37
+
38
+ def run(args, opts = {})
39
+ argv = []
40
+ output = nil
41
+ dest = '.'
42
+ files = []
43
+
44
+ while (arg = args.shift)
45
+ case arg
46
+ when '--uncompress', '-z'
47
+ opts[:uncompress] = true
48
+ when '--pipe'
49
+ output = ioe[:output]
50
+ ioe[:output] = ioe[:error]
51
+ when '--output', '-o'
52
+ dest = args.shift
53
+ else
54
+ argv << arg
55
+ end
56
+ end
57
+
58
+ if argv.empty?
59
+ ioe[:output] << "Not enough arguments.\n\n"
60
+ commander.command('help').call(%w(extract))
61
+ return 255
62
+ end
63
+
64
+ input = argv.shift
65
+ if '-' == input
66
+ opts[:name] = 'STDIN'
67
+ input = ioe[:input]
68
+ else
69
+ opts[:name] = input
70
+ input = File.open(input, 'rb')
71
+ end
72
+
73
+ if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:uncompress]
74
+ input = Zlib::GzipReader.new(input)
75
+ end
76
+
77
+ files << argv.to_a
78
+ files.flatten!
79
+
80
+ watcher, finisher =
81
+ if opts[:verbose]
82
+ verbose
83
+ elsif opts[:progress]
84
+ progress
85
+ else
86
+ silent
87
+ end
88
+
89
+ if output.nil?
90
+ Archive::Tar::Minitar.unpack(input, dest, files, &watcher)
91
+ finisher.call
92
+ else
93
+ Archive::Tar::Minitar::Input.each_entry(input) do |entry|
94
+ next unless files.empty? || files.include?(entry.full_name)
95
+
96
+ stats = {
97
+ :mode => entry.mode,
98
+ :mtime => entry.mtime,
99
+ :size => entry.size,
100
+ :gid => entry.gid,
101
+ :uid => entry.uid,
102
+ :current => 0,
103
+ :currinc => 0,
104
+ :entry => entry
105
+ }
106
+
107
+ if entry.directory?
108
+ watcher.call(:dir, dest, stats)
109
+ else
110
+ watcher.call(:file_start, destfile, stats)
111
+ loop do
112
+ data = entry.read(4096)
113
+ break unless data
114
+ stats[:currinc] = output.write(data)
115
+ stats[:current] += stats[:currinc]
116
+
117
+ watcher.call(:file_progress, name, stats)
118
+ end
119
+ watcher.call(:file_done, name, stats)
120
+ end
121
+ end
122
+ end
123
+
124
+ 0
125
+ end
126
+
127
+ def help
128
+ HELP
129
+ end
130
+
131
+ private
132
+
133
+ def verbose
134
+ [
135
+ lambda { |action, name, _stats|
136
+ ioe[:output] << "#{name}\n" if action == :dir or action == :file_done
137
+ },
138
+ lambda { ioe[:output] << "\n" }
139
+ ]
140
+ end
141
+
142
+ def progress
143
+ require 'powerbar'
144
+ progress = PowerBar.new(:msg => opts[:name], :total => 1)
145
+ [
146
+ lambda { |action, name, stats|
147
+ progress_info = {}
148
+
149
+ case action
150
+ when :file_start, :dir
151
+ progress_info[:msg] = File.basename(name)
152
+ if action == :dir
153
+ progress_info[:total] = progress.total + 1
154
+ progress_info[:done] = progress.done + 1
155
+ else
156
+ progress_info[:total] = progress.total + stats[:entry].size
157
+ end
158
+ when :file_progress
159
+ progress_info[:done] = progress.done + stats[:currinc]
160
+ end
161
+
162
+ progress.show(progress_info)
163
+ },
164
+ lambda {
165
+ progress.show(:msg => opts[:name])
166
+ progress.close(true)
167
+ }
168
+ ]
169
+ end
170
+
171
+ def silent
172
+ [ lambda { |_, _, _| }, lambda {} ]
173
+ end
174
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Help command. This will be replaced in a future version by one of the
4
+ # better-executed CLI application frameworks like GLI, after Ruby 1.8 and 1.9
5
+ # support have been dropped.
6
+ class Minitar::CLI::Command::Help < Minitar::CLI::Command
7
+ def name
8
+ 'help'
9
+ end
10
+
11
+ COMMANDS = <<-EOS.freeze
12
+ The commands known to minitar are:
13
+
14
+ minitar create Creates a new tarfile.
15
+ minitar extract Extracts files from a tarfile.
16
+ minitar list Lists files in the tarfile.
17
+
18
+ All commands accept the options --verbose and --progress, which are
19
+ mutually exclusive. In "minitar list", --progress means the same as
20
+ --verbose.
21
+
22
+ --verbose, -V Performs the requested command verbosely.
23
+ --progress, -P Shows a progress bar, if appropriate, for the action
24
+ being performed.
25
+
26
+ EOS
27
+
28
+ BASIC = <<-EOS.freeze
29
+ This is a basic help message containing pointers to more information on
30
+ how to use this command-line tool. Try:
31
+
32
+ minitar help commands list all 'minitar' commands
33
+ minitar help <COMMAND> show help on <COMMAND>
34
+ (e.g., 'minitar help create')
35
+ EOS
36
+
37
+ def call(args, _opts = {})
38
+ help_on = args.shift
39
+
40
+ if commander.command?(help_on)
41
+ ioe[:output] << commander[help_on].help
42
+ elsif help_on == 'commands'
43
+ ioe[:output] << COMMANDS
44
+ else
45
+ unless help_on.nil? or help_on.empty?
46
+ ioe[:output] << "Unknown command: #{help_on}\n"
47
+ end
48
+ ioe[:output] << help
49
+ end
50
+
51
+ 0
52
+ end
53
+
54
+ def help
55
+ BASIC
56
+ end
57
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Tarball list command. This will be replaced in a future version by one of the
4
+ # better-executed CLI application frameworks like GLI, after Ruby 1.8 and 1.9
5
+ # support have been dropped.
6
+ class Minitar::CLI::Command::List < Minitar::CLI::Command
7
+ def name
8
+ 'list'
9
+ end
10
+
11
+ def altname
12
+ 'ls'
13
+ end
14
+
15
+ HELP = <<-EOH.freeze
16
+ minitar list [OPTIONS] <tarfile|-> [<file>+]
17
+
18
+ Lists files in an existing tarfile. If the tarfile is named .tar.gz or
19
+ .tgz, then it will be uncompressed automatically. If the tarfile is "-",
20
+ then it will be read from standard input (stdin) so that minitar may be
21
+ piped.
22
+
23
+ If --verbose or --progress is specified, then the file list will be
24
+ similar to that produced by the Unix command "ls -l".
25
+
26
+ list Options:
27
+ --uncompress, -z Uncompresses the tarfile with gzip.
28
+ --sort [<FIELD>], -S Sorts the list of files by the specified
29
+ field. The sort defaults to the filename.
30
+ --reverse, -R Reverses the sort.
31
+ -l Lists the files in detail.
32
+
33
+ Sort Fields:
34
+ name, mtime, size
35
+
36
+ EOH
37
+
38
+ def modestr(mode)
39
+ s = String.new('---')
40
+ s[0] = 'r' if (mode & 4) == 4
41
+ s[1] = 'w' if (mode & 2) == 2
42
+ s[2] = 'x' if (mode & 1) == 1
43
+ s
44
+ end
45
+
46
+ include CatchMinitarErrors
47
+
48
+ def run(args, opts = {})
49
+ argv = []
50
+ output = nil
51
+ files = []
52
+ opts[:field] = 'name'
53
+
54
+ while (arg = args.shift)
55
+ case arg
56
+ when '--sort', '-S'
57
+ opts[:sort] = true
58
+ opts[:field] = args.shift
59
+ when '--reverse', '-R'
60
+ opts[:reverse] = true
61
+ opts[:sort] = true
62
+ when '--uncompress', '-z'
63
+ opts[:uncompress] = true
64
+ when '-l'
65
+ opts[:verbose] = true
66
+ else
67
+ argv << arg
68
+ end
69
+ end
70
+
71
+ if argv.empty?
72
+ ioe[:output] << "Not enough arguments.\n\n"
73
+ commander.command('help').call(%w(list))
74
+ return 255
75
+ end
76
+
77
+ input = argv.shift
78
+ if '-' == input
79
+ opts[:name] = 'STDIN'
80
+ input = ioe[:input]
81
+ else
82
+ opts[:name] = input
83
+ input = File.open(input, 'rb')
84
+ end
85
+
86
+ if opts[:name] =~ /\.tar\.gz$|\.tgz$/ or opts[:uncompress]
87
+ input = Zlib::GzipReader.new(input)
88
+ end
89
+
90
+ files << argv.to_a
91
+ files.flatten!
92
+
93
+ if opts[:verbose] or opts[:progress]
94
+ format = '%10s %4d %8s %8s %8d %12s %s'
95
+ datefmt = '%b %d %Y'
96
+ timefmt = '%b %d %H:%M'
97
+ fields = %w(permissions inodes user group size date fullname)
98
+ else
99
+ format = '%s'
100
+ fields = %w(fullname)
101
+ end
102
+
103
+ opts[:field] = opts[:field].to_sym
104
+ opts[:field] = :full_name if opts[:field] == :name
105
+
106
+ output = []
107
+
108
+ today = Time.now
109
+ oneyear = Time.mktime(today.year - 1, today.month, today.day)
110
+
111
+ Archive::Tar::Minitar::Input.each_entry(input) do |entry|
112
+ next unless files.empty? || files.include?(entry.full_name)
113
+
114
+ value = format % fields.map { |ff|
115
+ case ff
116
+ when 'permissions'
117
+ s = String.new(entry.directory? ? 'd' : '-')
118
+ s << modestr(entry.mode / 0o100)
119
+ s << modestr(entry.mode / 0o010)
120
+ s << modestr(entry.mode)
121
+ when 'inodes'
122
+ entry.size / 512
123
+ when 'user'
124
+ entry.uname || entry.uid || 0
125
+ when 'group'
126
+ entry.gname || entry.gid || 0
127
+ when 'size'
128
+ entry.size
129
+ when 'date'
130
+ if Time.at(entry.mtime) > oneyear
131
+ Time.at(entry.mtime).strftime(timefmt)
132
+ else
133
+ Time.at(entry.mtime).strftime(datefmt)
134
+ end
135
+ when 'fullname'
136
+ entry.full_name
137
+ end
138
+ }
139
+
140
+ if opts[:sort]
141
+ output << [ entry.send(opts[:field]), value ]
142
+ else
143
+ ioe[:output] << value << "\n"
144
+ end
145
+ end
146
+
147
+ if opts[:sort]
148
+ output = output.sort { |a, b| a[0] <=> b[0] }
149
+ if opts[:reverse]
150
+ output.reverse_each { |oo| ioe[:output] << oo[1] << "\n" }
151
+ else
152
+ output.each { |oo| ioe[:output] << oo[1] << "\n" }
153
+ end
154
+ end
155
+
156
+ 0
157
+ end
158
+
159
+ def help
160
+ HELP
161
+ end
162
+ end