2023-12-26 14:48:33 +03:00
|
|
|
local M = {}
|
|
|
|
|
|
|
|
function M:peek()
|
2024-06-26 19:47:56 +03:00
|
|
|
local limit = self.area.h
|
2024-07-28 03:14:53 +03:00
|
|
|
local paths, sizes = {}, {}
|
2024-06-26 19:47:56 +03:00
|
|
|
|
2024-07-28 03:14:53 +03:00
|
|
|
local files, bound, code = self:list_files({ "-p", tostring(self.file.url) }, self.skip, limit)
|
|
|
|
if code ~= 0 then
|
|
|
|
return ya.preview_widgets(self, {
|
|
|
|
ui.Paragraph(self.area, {
|
|
|
|
ui.Line(code == 2 and "File list in this archive is encrypted" or "Spawn `7z` and `7zz` both commands failed"),
|
|
|
|
}),
|
|
|
|
})
|
|
|
|
end
|
2024-06-26 19:47:56 +03:00
|
|
|
|
2024-07-28 03:14:53 +03:00
|
|
|
for _, f in ipairs(files) do
|
|
|
|
local icon = File({
|
|
|
|
url = Url(f.path),
|
|
|
|
cha = Cha { kind = f.attr:sub(1, 1) == "D" and 1 or 0 },
|
2024-06-26 19:47:56 +03:00
|
|
|
}):icon()
|
|
|
|
|
|
|
|
if icon then
|
2024-07-28 03:14:53 +03:00
|
|
|
paths[#paths + 1] = ui.Line { ui.Span(" " .. icon.text .. " "):style(icon.style), ui.Span(f.path) }
|
2024-06-26 19:47:56 +03:00
|
|
|
else
|
2024-07-28 03:14:53 +03:00
|
|
|
paths[#paths + 1] = ui.Line(f.path)
|
2024-06-26 19:47:56 +03:00
|
|
|
end
|
|
|
|
|
2024-07-28 03:14:53 +03:00
|
|
|
if f.size > 0 then
|
|
|
|
sizes[#sizes + 1] = ui.Line(string.format(" %s ", ya.readable_size(f.size)))
|
2024-06-26 19:47:56 +03:00
|
|
|
else
|
2024-06-28 03:59:23 +03:00
|
|
|
sizes[#sizes + 1] = ui.Line("")
|
2024-06-26 19:47:56 +03:00
|
|
|
end
|
2024-07-28 03:14:53 +03:00
|
|
|
end
|
2024-06-26 19:47:56 +03:00
|
|
|
|
2024-07-28 03:14:53 +03:00
|
|
|
if self.skip > 0 and bound < self.skip + limit then
|
|
|
|
ya.manager_emit("peek", { math.max(0, bound - limit), only_if = self.file.url, upper_bound = true })
|
2024-06-26 19:47:56 +03:00
|
|
|
else
|
|
|
|
ya.preview_widgets(self, {
|
2024-07-28 03:14:53 +03:00
|
|
|
ui.Paragraph(self.area, paths),
|
2024-06-26 19:47:56 +03:00
|
|
|
ui.Paragraph(self.area, sizes):align(ui.Paragraph.RIGHT),
|
|
|
|
})
|
2023-12-26 14:48:33 +03:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function M:seek(units)
|
|
|
|
local h = cx.active.current.hovered
|
|
|
|
if h and h.url == self.file.url then
|
|
|
|
local step = math.floor(units * self.area.h / 10)
|
|
|
|
ya.manager_emit("peek", {
|
2024-03-01 05:22:12 +03:00
|
|
|
math.max(0, cx.active.preview.skip + step),
|
2024-04-22 09:18:00 +03:00
|
|
|
only_if = self.file.url,
|
2023-12-26 14:48:33 +03:00
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-07-28 03:14:53 +03:00
|
|
|
function M:spawn_7z(args)
|
|
|
|
local last_error = nil
|
|
|
|
local try = function(name)
|
|
|
|
local stdout = args[1] == "l" and Command.PIPED or Command.NULL
|
|
|
|
local child, code = Command(name):args(args):stdout(stdout):stderr(Command.PIPED):spawn()
|
|
|
|
if not child then
|
|
|
|
last_error = code
|
|
|
|
end
|
|
|
|
return child
|
|
|
|
end
|
|
|
|
|
|
|
|
local child
|
|
|
|
if ya.target_os() == "macos" then
|
|
|
|
child = try("7zz") or try("7z")
|
|
|
|
else
|
|
|
|
child = try("7z") or try("7zz")
|
|
|
|
end
|
|
|
|
|
|
|
|
if not child then
|
|
|
|
return ya.err("spawn `7z` and `7zz` both commands failed, error code: " .. tostring(last_error))
|
|
|
|
end
|
|
|
|
return child, last_error
|
|
|
|
end
|
|
|
|
|
|
|
|
---List files in an archive
|
|
|
|
---@param args table
|
|
|
|
---@param skip integer
|
|
|
|
---@param limit integer
|
|
|
|
---@return table
|
|
|
|
---@return integer
|
|
|
|
---@return integer
|
|
|
|
--- 0: success
|
|
|
|
--- 1: failed to spawn
|
|
|
|
--- 2: wrong password
|
|
|
|
--- 3: partial success
|
|
|
|
function M:list_files(args, skip, limit)
|
|
|
|
local child = self:spawn_7z { "l", "-ba", "-slt", table.unpack(args) }
|
2024-06-26 19:47:56 +03:00
|
|
|
if not child then
|
2024-07-28 03:14:53 +03:00
|
|
|
return {}, 0, 1
|
2024-06-26 19:47:56 +03:00
|
|
|
end
|
2024-07-28 03:14:53 +03:00
|
|
|
|
|
|
|
local i, files, code = 0, { { path = "", size = 0, attr = "" } }, 0
|
|
|
|
local key, value = "", ""
|
|
|
|
repeat
|
|
|
|
local next, event = child:read_line()
|
|
|
|
if event == 1 and self:is_encrypted(next) then
|
|
|
|
code = 2
|
|
|
|
break
|
|
|
|
elseif event == 1 then
|
|
|
|
code = 3
|
|
|
|
goto continue
|
|
|
|
elseif event ~= 0 then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
|
|
|
if next == "\n" or next == "\r\n" then
|
|
|
|
i = i + 1
|
|
|
|
if files[#files].path ~= "" then
|
|
|
|
files[#files + 1] = { path = "", size = 0, attr = "" }
|
|
|
|
end
|
|
|
|
goto continue
|
|
|
|
elseif i < skip then
|
|
|
|
goto continue
|
|
|
|
end
|
|
|
|
|
|
|
|
key, value = next:match("^(%u%l+) = (.+)[\r\n]+")
|
|
|
|
if key == "Path" then
|
|
|
|
files[#files].path = value
|
|
|
|
elseif key == "Size" then
|
|
|
|
files[#files].size = tonumber(value) or 0
|
|
|
|
elseif key == "Attributes" then
|
|
|
|
files[#files].attr = value
|
|
|
|
end
|
|
|
|
|
|
|
|
::continue::
|
|
|
|
until i >= skip + limit
|
|
|
|
child:start_kill()
|
|
|
|
|
|
|
|
if files[#files].path == "" then
|
|
|
|
files[#files] = nil
|
|
|
|
end
|
|
|
|
return files, i, code
|
|
|
|
end
|
|
|
|
|
2024-08-11 12:30:52 +03:00
|
|
|
function M:is_encrypted(s) return s:find(" Wrong password", 1, true) end
|
2024-06-26 19:47:56 +03:00
|
|
|
|
2023-12-26 14:48:33 +03:00
|
|
|
return M
|