rakpak 1.1.0 → 1.2.0
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.
- checksums.yaml +4 -4
- data/README.md +11 -5
- data/lib/rakpak/app.rb +53 -50
- data/lib/rakpak/browser.rb +86 -36
- data/lib/rakpak/dir_cache.rb +1 -3
- data/lib/rakpak/entry.rb +1 -4
- data/lib/rakpak/formats.rb +2 -16
- data/lib/rakpak/job.rb +12 -41
- data/lib/rakpak/modal.rb +70 -23
- data/lib/rakpak/plan.rb +70 -55
- data/lib/rakpak/screen.rb +2 -10
- data/lib/rakpak/sizer.rb +2 -6
- data/lib/rakpak/term.rb +3 -11
- data/lib/rakpak/text.rb +3 -12
- data/lib/rakpak/theme.rb +1 -2
- data/lib/rakpak/unpack.rb +10 -47
- data/lib/rakpak/version.rb +1 -1
- data/lib/rakpak.rb +5 -23
- metadata +1 -1
data/lib/rakpak.rb
CHANGED
|
@@ -28,19 +28,15 @@ module Rakpak
|
|
|
28
28
|
|
|
29
29
|
Options = Struct.new(:dir, :pack, :unpack, :action, keyword_init: true)
|
|
30
30
|
|
|
31
|
-
# A folder as a person would type it: ~, ~/x, $HOME/x, ${HOME}/x, or an
|
|
32
|
-
# absolute or relative path.
|
|
33
31
|
def self.expand_dir(text)
|
|
34
32
|
text = text.to_s.strip
|
|
35
33
|
text = text.gsub(/\$\{(\w+)\}|\$(\w+)/) { ENV.fetch(::Regexp.last_match(1) || ::Regexp.last_match(2), "") }
|
|
36
34
|
text = "~" if text.empty?
|
|
37
35
|
File.expand_path(text)
|
|
38
36
|
rescue ArgumentError
|
|
39
|
-
text
|
|
37
|
+
text
|
|
40
38
|
end
|
|
41
39
|
|
|
42
|
-
# Returns Options. `action` is :browse, :unpack, :help or :version. Raises
|
|
43
|
-
# ArgumentError on anything it does not understand.
|
|
44
40
|
def self.parse(argv)
|
|
45
41
|
pack_mode = false
|
|
46
42
|
depack_mode = false
|
|
@@ -81,8 +77,6 @@ module Rakpak
|
|
|
81
77
|
paths.each do |p|
|
|
82
78
|
raise ArgumentError, "no such file or folder: #{p}" unless File.exist?(p) || File.symlink?(p)
|
|
83
79
|
end
|
|
84
|
-
# Open the browser on the folder holding the first target, with that
|
|
85
|
-
# target under the cursor, so the archive lands next to it.
|
|
86
80
|
Options.new(action: :browse, dir: File.dirname(paths.first), pack: paths, unpack: [])
|
|
87
81
|
else
|
|
88
82
|
raise ArgumentError, "expected one folder, got #{positional.size}" if positional.size > 1
|
|
@@ -94,10 +88,6 @@ module Rakpak
|
|
|
94
88
|
end
|
|
95
89
|
end
|
|
96
90
|
|
|
97
|
-
# Unpacks each archive into the folder you are standing in, the way tar
|
|
98
|
-
# would, each into a folder of its own name. A lone compressed file has
|
|
99
|
-
# nothing to wrap, so it lands beside you. Returns an exit status;
|
|
100
|
-
# one bad archive does not stop the rest.
|
|
101
91
|
def self.unpack_all(archives, dir: Dir.pwd, out: $stdout)
|
|
102
92
|
status = 0
|
|
103
93
|
archives.each do |archive|
|
|
@@ -111,7 +101,7 @@ module Rakpak
|
|
|
111
101
|
end
|
|
112
102
|
out.puts "#{Text.plain(File.basename(archive))} → #{Text.plain(Text.tilde(plan.dest))}"
|
|
113
103
|
plan.warnings.each { |w| out.puts " #{w}" }
|
|
114
|
-
#
|
|
104
|
+
# The job writes to stderr; flush so the streams stay in order.
|
|
115
105
|
out.flush
|
|
116
106
|
status = 1 unless run_headless(plan, out)
|
|
117
107
|
end
|
|
@@ -120,12 +110,8 @@ module Rakpak
|
|
|
120
110
|
130
|
|
121
111
|
end
|
|
122
112
|
|
|
123
|
-
#
|
|
124
|
-
#
|
|
125
|
-
#
|
|
126
|
-
# The extractor runs in its own process group so a cancel can take its whole
|
|
127
|
-
# pipeline, which also means the terminal's ctrl-c never reaches it. Catching
|
|
128
|
-
# the interrupt here is what stops tar carrying on without us.
|
|
113
|
+
# The extractor runs in its own process group, so ctrl-c never reaches it;
|
|
114
|
+
# catching Interrupt here is what stops tar carrying on without us.
|
|
129
115
|
def self.run_headless(plan, out)
|
|
130
116
|
job = Job.new(plan).start
|
|
131
117
|
live = out.respond_to?(:tty?) && out.tty?
|
|
@@ -147,9 +133,7 @@ module Rakpak
|
|
|
147
133
|
out.print "\r\e[K" if live
|
|
148
134
|
unless job.ok?
|
|
149
135
|
warn "rakpak: #{job.error}"
|
|
150
|
-
#
|
|
151
|
-
# Member names come from inside the archive, so they reach the shell
|
|
152
|
-
# defanged, the way the finished-job report does.
|
|
136
|
+
# Member names come from inside the archive; defang before printing.
|
|
153
137
|
job.tail(4).each { |line| warn " #{Text.plain(line)}" unless line.start_with?("▸") }
|
|
154
138
|
return false
|
|
155
139
|
end
|
|
@@ -157,7 +141,6 @@ module Rakpak
|
|
|
157
141
|
true
|
|
158
142
|
end
|
|
159
143
|
|
|
160
|
-
# Exit status.
|
|
161
144
|
def self.start(argv = ARGV)
|
|
162
145
|
begin
|
|
163
146
|
opts = parse(argv)
|
|
@@ -174,7 +157,6 @@ module Rakpak
|
|
|
174
157
|
puts "rakpak #{VERSION}"
|
|
175
158
|
return 0
|
|
176
159
|
when :unpack
|
|
177
|
-
# Nothing to browse and nothing to ask, so this works down a pipe.
|
|
178
160
|
return unpack_all(opts.unpack)
|
|
179
161
|
end
|
|
180
162
|
unless $stdout.tty? && $stdin.tty?
|