-- Send error notification local function notify_error(message, urgency) ya.notify({ title = "Archive", content = message, level = urgency, timeout = 5, }) end -- Check for windows local is_windows = ya.target_family() == "windows" -- Make table of selected or hovered: path = filenames local selected_or_hovered = ya.sync(function() local tab, paths, names, path_fnames = cx.active, {}, {}, {} for _, u in pairs(tab.selected) do paths[#paths + 1] = tostring(u:parent()) names[#names + 1] = tostring(u:name()) end if #paths == 0 and tab.current.hovered then paths[1] = tostring(tab.current.hovered.url:parent()) names[1] = tostring(tab.current.hovered.name) end for idx, name in ipairs(names) do if not path_fnames[paths[idx]] then path_fnames[paths[idx]] = {} end table.insert(path_fnames[paths[idx]], name) end return path_fnames, tostring(tab.current.cwd) end) -- Check if archive command is available local function is_command_available(cmd) local stat_cmd if is_windows then stat_cmd = string.format("where %s > nul 2>&1", cmd) else stat_cmd = string.format("command -v %s >/dev/null 2>&1", cmd) end local cmd_exists = os.execute(stat_cmd) if cmd_exists then return true else return false end end -- Archive command list --> string local function find_binary(cmd_list) for _, cmd in ipairs(cmd_list) do if is_command_available(cmd) then return cmd end end return cmd_list[1] -- Return first command as fallback end -- Check if file exists local function file_exists(name) local f = io.open(name, "r") if f ~= nil then io.close(f) return true else return false end end -- Append filename to it's parent directory local function combine_url(path, file) path, file = Url(path), Url(file) return tostring(path:join(file)) end return { entry = function() -- Exit visual mode ya.manager_emit("escape", { visual = true }) -- Define file table and output_dir (pwd) local path_fnames, output_dir = selected_or_hovered() -- Get input local output_name, event = ya.input({ title = "Create archive:", position = { "top-center", y = 3, w = 40 }, }) if event ~= 1 then return end -- Use appropriate archive command local archive_commands = { ["%.zip$"] = { command = "zip", args = { "-r" } }, ["%.7z$"] = { command = { "7z", "7zz" }, args = { "a" } }, ["%.tar.gz$"] = { command = "tar", args = { "rpf" }, compress = "gzip" }, ["%.tar.xz$"] = { command = "tar", args = { "rpf" }, compress = "xz" }, ["%.tar.bz2$"] = { command = "tar", args = { "rpf" }, compress = "bzip2" }, ["%.tar.zst$"] = { command = "tar", args = { "rpf" }, compress = "zstd", compress_args = { "--rm" } }, ["%.tar$"] = { command = "tar", args = { "rpf" } }, } if is_windows then archive_commands = { ["%.zip$"] = { command = "7z", args = { "a", "-tzip" } }, ["%.7z$"] = { command = "7z", args = { "a" } }, ["%.tar.gz$"] = { command = "tar", args = { "rpf" }, compress = "7z", compress_args = { "a", "-tgzip", "-sdel", output_name }, }, ["%.tar.xz$"] = { command = "tar", args = { "rpf" }, compress = "7z", compress_args = { "a", "-txz", "-sdel", output_name }, }, ["%.tar.bz2$"] = { command = "tar", args = { "rpf" }, compress = "7z", compress_args = { "a", "-tbzip2", "-sdel", output_name }, }, ["%.tar.zst$"] = { command = "tar", args = { "rpf" }, compress = "zstd", compress_args = { "--rm" } }, ["%.tar$"] = { command = "tar", args = { "rpf" } }, } end -- Match user input to archive command local archive_cmd, archive_args, archive_compress, archive_compress_args for pattern, cmd_pair in pairs(archive_commands) do if output_name:match(pattern) then archive_cmd = cmd_pair.command archive_args = cmd_pair.args archive_compress = cmd_pair.compress archive_compress_args = cmd_pair.compress_args or {} end end -- Check if archive command has multiple names if type(archive_cmd) == "table" then archive_cmd = find_binary(archive_cmd) end -- Check if no archive command is available for the extention if not archive_cmd then notify_error("Unsupported file extention", "error") return end -- Exit if archive command is not available if not is_command_available(archive_cmd) then notify_error(string.format("%s not available", archive_cmd), "error") return end -- Exit if compress command is not available if archive_compress and not is_command_available(archive_compress) then notify_error(string.format("%s compression not available", archive_compress), "error") return end -- If file exists show overwrite prompt local output_url = combine_url(output_dir, output_name) while true do if file_exists(output_url) then local overwrite_answer = ya.input({ title = "Overwrite " .. output_name .. "? y/N:", position = { "top-center", y = 3, w = 40 }, }) if overwrite_answer:lower() ~= "y" then notify_error("Operation canceled", "warn") return -- If no overwrite selected, exit else local rm_status, rm_err = os.remove(output_url) if not rm_status then notify_error(string.format("Failed to remove %s, exit code %s", output_name, rm_err), "error") return end -- If overwrite fails, exit end end if archive_compress and not output_name:match("%.tar$") then output_name = output_name:match("(.*%.tar)") -- Test for .tar and .tar.* output_url = combine_url(output_dir, output_name) -- Update output_url else break end end -- Add to output archive in each path, their respective files for path, names in pairs(path_fnames) do local archive_status, archive_err = Command(archive_cmd):args(archive_args):arg(output_url):args(names):cwd(path):spawn():wait() if not archive_status or not archive_status.success then notify_error( string.format( "%s with selected files failed, exit code %s", archive_args, archive_status and archive_status.code or archive_err ), "error" ) end end -- Use compress command if needed if archive_compress then local compress_status, compress_err = Command(archive_compress):args(archive_compress_args):arg(output_name):cwd(output_dir):spawn():wait() if not compress_status or not compress_status.success then notify_error( string.format( "%s with %s failed, exit code %s", archive_compress, output_name, compress_status and compress_status.code or compress_err ), "error" ) end end end, }