minitar-cli 0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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